diff --git a/404.html b/404.html index 1e104cdd5898..090dd6ccdd41 100644 --- a/404.html +++ b/404.html @@ -5,15 +5,16 @@ 404 | KernelSU - - + + + - + + -
Skip to content

404

PAGE NOT FOUND

But if you don't change your direction, and if you keep looking, you may end up where you are heading.

Released under the GPL3 License.

- +
Skip to content

404

PAGE NOT FOUND

But if you don't change your direction, and if you keep looking, you may end up where you are heading.

Released under the GPL3 License.

+ \ No newline at end of file diff --git a/assets/app.9b09a9be.js b/assets/app.9b09a9be.js deleted file mode 100644 index 7d078559dc52..000000000000 --- a/assets/app.9b09a9be.js +++ /dev/null @@ -1 +0,0 @@ -import{d as p,K as s,a2 as i,u,p as c,k as l,a3 as d,a4 as f,a5 as m,a6 as h,a7 as A,a8 as g,a9 as P,aa as v,ab as y,ac as C,ad as w,ae as _,af as b,ag as E}from"./chunks/framework.43781440.js";import{t as R}from"./chunks/theme.85c31bd3.js";function r(e){if(e.extends){const a=r(e.extends);return{...a,...e,async enhanceApp(t){a.enhanceApp&&await a.enhanceApp(t),e.enhanceApp&&await e.enhanceApp(t)}}}return e}const n=r(R),D=p({name:"VitePressApp",setup(){const{site:e}=u();return c(()=>{l(()=>{document.documentElement.lang=e.value.lang,document.documentElement.dir=e.value.dir})}),d(),f(),m(),n.setup&&n.setup(),()=>h(n.Layout)}});async function O(){const e=T(),a=S();a.provide(A,e);const t=g(e.route);return a.provide(P,t),a.component("Content",v),a.component("ClientOnly",y),Object.defineProperties(a.config.globalProperties,{$frontmatter:{get(){return t.frontmatter.value}},$params:{get(){return t.page.value.params}}}),n.enhanceApp&&await n.enhanceApp({app:a,router:e,siteData:C}),{app:a,router:e,data:t}}function S(){return w(D)}function T(){let e=s,a;return _(t=>{let o=b(t);return e&&(a=o),(e||a===o)&&(o=o.replace(/\.js$/,".lean.js")),s&&(e=!1),E(()=>import(o),[])},n.NotFound)}s&&O().then(({app:e,router:a,data:t})=>{a.go().then(()=>{i(a.route,t.site),e.mount("#app")})});export{O as createApp}; diff --git a/assets/app.a2c23f3d.js b/assets/app.a2c23f3d.js new file mode 100644 index 000000000000..e4294fa789d9 --- /dev/null +++ b/assets/app.a2c23f3d.js @@ -0,0 +1 @@ +import{s,a0 as i,a1 as u,a2 as c,a3 as l,a4 as d,a5 as f,a6 as m,a7 as h,a8 as A,a9 as g,aa as P,d as v,u as y,j as C,y as w,ab as _,ac as b,ad as E,ae as R}from"./chunks/framework.ec8f7e8e.js";import{t as D}from"./chunks/theme.4b73f3e8.js";function p(e){if(e.extends){const a=p(e.extends);return{...a,...e,async enhanceApp(t){a.enhanceApp&&await a.enhanceApp(t),e.enhanceApp&&await e.enhanceApp(t)}}}return e}const o=p(D),j=v({name:"VitePressApp",setup(){const{site:e}=y();return C(()=>{w(()=>{document.documentElement.lang=e.value.lang,document.documentElement.dir=e.value.dir})}),_(),b(),E(),o.setup&&o.setup(),()=>R(o.Layout)}});async function O(){const e=T(),a=S();a.provide(u,e);const t=c(e.route);return a.provide(l,t),a.component("Content",d),a.component("ClientOnly",f),Object.defineProperties(a.config.globalProperties,{$frontmatter:{get(){return t.frontmatter.value}},$params:{get(){return t.page.value.params}}}),o.enhanceApp&&await o.enhanceApp({app:a,router:e,siteData:m}),{app:a,router:e,data:t}}function S(){return h(j)}function T(){let e=s,a;return A(t=>{let n=g(t),r=null;return n&&(e&&(a=n),(e||a===n)&&(n=n.replace(/\.js$/,".lean.js")),r=P(()=>import(n),[])),s&&(e=!1),r},o.NotFound)}s&&O().then(({app:e,router:a,data:t})=>{a.go().then(()=>{i(a.route,t.site),e.mount("#app")})});export{O as createApp}; diff --git a/assets/chunks/framework.43781440.js b/assets/chunks/framework.43781440.js deleted file mode 100644 index 716951ef8b6b..000000000000 --- a/assets/chunks/framework.43781440.js +++ /dev/null @@ -1,2 +0,0 @@ -function Kn(e,t){const n=Object.create(null),s=e.split(",");for(let r=0;r!!n[r.toLowerCase()]:r=>!!n[r]}function kn(e){if(N(e)){const t={};for(let n=0;n{if(n){const s=n.split(bi);s.length>1&&(t[s[0].trim()]=s[1].trim())}}),t}function Wn(e){let t="";if(re(e))t=e;else if(N(e))for(let n=0;nre(e)?e:e==null?"":N(e)||ee(e)&&(e.toString===cr||!j(e.toString))?JSON.stringify(e,ir,2):String(e),ir=(e,t)=>t&&t.__v_isRef?ir(e,t.value):ut(t)?{[`Map(${t.size})`]:[...t.entries()].reduce((n,[s,r])=>(n[`${s} =>`]=r,n),{})}:or(t)?{[`Set(${t.size})`]:[...t.values()]}:ee(t)&&!N(t)&&!fr(t)?String(t):t,te={},ft=[],Ie=()=>{},Ei=()=>!1,vi=/^on[^a-z]/,Lt=e=>vi.test(e),qn=e=>e.startsWith("onUpdate:"),ce=Object.assign,Vn=(e,t)=>{const n=e.indexOf(t);n>-1&&e.splice(n,1)},Ti=Object.prototype.hasOwnProperty,V=(e,t)=>Ti.call(e,t),N=Array.isArray,ut=e=>sn(e)==="[object Map]",or=e=>sn(e)==="[object Set]",j=e=>typeof e=="function",re=e=>typeof e=="string",zn=e=>typeof e=="symbol",ee=e=>e!==null&&typeof e=="object",lr=e=>ee(e)&&j(e.then)&&j(e.catch),cr=Object.prototype.toString,sn=e=>cr.call(e),Ai=e=>sn(e).slice(8,-1),fr=e=>sn(e)==="[object Object]",Yn=e=>re(e)&&e!=="NaN"&&e[0]!=="-"&&""+parseInt(e,10)===e,Et=Kn(",key,ref,ref_for,ref_key,onVnodeBeforeMount,onVnodeMounted,onVnodeBeforeUpdate,onVnodeUpdated,onVnodeBeforeUnmount,onVnodeUnmounted"),rn=e=>{const t=Object.create(null);return n=>t[n]||(t[n]=e(n))},Ri=/-(\w)/g,Se=rn(e=>e.replace(Ri,(t,n)=>n?n.toUpperCase():"")),Ii=/\B([A-Z])/g,it=rn(e=>e.replace(Ii,"-$1").toLowerCase()),on=rn(e=>e.charAt(0).toUpperCase()+e.slice(1)),Vt=rn(e=>e?`on${on(e)}`:""),It=(e,t)=>!Object.is(e,t),wn=(e,t)=>{for(let n=0;n{Object.defineProperty(e,t,{configurable:!0,enumerable:!1,value:n})},Jn=e=>{const t=parseFloat(e);return isNaN(t)?e:t};let _s;const Oi=()=>_s||(_s=typeof globalThis<"u"?globalThis:typeof self<"u"?self:typeof window<"u"?window:typeof global<"u"?global:{});let me;class Mi{constructor(t=!1){this.detached=t,this.active=!0,this.effects=[],this.cleanups=[],this.parent=me,!t&&me&&(this.index=(me.scopes||(me.scopes=[])).push(this)-1)}run(t){if(this.active){const n=me;try{return me=this,t()}finally{me=n}}}on(){me=this}off(){me=this.parent}stop(t){if(this.active){let n,s;for(n=0,s=this.effects.length;n{const t=new Set(e);return t.w=0,t.n=0,t},ur=e=>(e.w&qe)>0,ar=e=>(e.n&qe)>0,Pi=({deps:e})=>{if(e.length)for(let t=0;t{const{deps:t}=e;if(t.length){let n=0;for(let s=0;s{(d==="length"||d>=f)&&l.push(a)})}else switch(n!==void 0&&l.push(o.get(n)),t){case"add":N(e)?Yn(n)&&l.push(o.get("length")):(l.push(o.get(st)),ut(e)&&l.push(o.get(Fn)));break;case"delete":N(e)||(l.push(o.get(st)),ut(e)&&l.push(o.get(Fn)));break;case"set":ut(e)&&l.push(o.get(st));break}if(l.length===1)l[0]&&Pn(l[0]);else{const f=[];for(const a of l)a&&f.push(...a);Pn(Xn(f))}}function Pn(e,t){const n=N(e)?e:[...e];for(const s of n)s.computed&&ys(s);for(const s of n)s.computed||ys(s)}function ys(e,t){(e!==Ae||e.allowRecurse)&&(e.scheduler?e.scheduler():e.run())}const Li=Kn("__proto__,__v_isRef,__isVue"),pr=new Set(Object.getOwnPropertyNames(Symbol).filter(e=>e!=="arguments"&&e!=="caller").map(e=>Symbol[e]).filter(zn)),Ni=Qn(),Hi=Qn(!1,!0),$i=Qn(!0),ws=Ui();function Ui(){const e={};return["includes","indexOf","lastIndexOf"].forEach(t=>{e[t]=function(...n){const s=Y(this);for(let i=0,o=this.length;i{e[t]=function(...n){bt();const s=Y(this)[t].apply(this,n);return yt(),s}}),e}function Qn(e=!1,t=!1){return function(s,r,i){if(r==="__v_isReactive")return!e;if(r==="__v_isReadonly")return e;if(r==="__v_isShallow")return t;if(r==="__v_raw"&&i===(e?t?eo:yr:t?br:_r).get(s))return s;const o=N(s);if(!e&&o&&V(ws,r))return Reflect.get(ws,r,i);const l=Reflect.get(s,r,i);return(zn(r)?pr.has(r):Li(r))||(e||be(s,"get",r),t)?l:oe(l)?o&&Yn(r)?l:l.value:ee(l)?e?wr(l):cn(l):l}}const ji=gr(),Bi=gr(!0);function gr(e=!1){return function(n,s,r,i){let o=n[s];if(gt(o)&&oe(o)&&!oe(r))return!1;if(!e&&(!Zt(r)&&!gt(r)&&(o=Y(o),r=Y(r)),!N(n)&&oe(o)&&!oe(r)))return o.value=r,!0;const l=N(n)&&Yn(s)?Number(s)e,ln=e=>Reflect.getPrototypeOf(e);function $t(e,t,n=!1,s=!1){e=e.__v_raw;const r=Y(e),i=Y(t);n||(t!==i&&be(r,"get",t),be(r,"get",i));const{has:o}=ln(r),l=s?Gn:n?ns:Ot;if(o.call(r,t))return l(e.get(t));if(o.call(r,i))return l(e.get(i));e!==r&&e.get(t)}function Ut(e,t=!1){const n=this.__v_raw,s=Y(n),r=Y(e);return t||(e!==r&&be(s,"has",e),be(s,"has",r)),e===r?n.has(e):n.has(e)||n.has(r)}function jt(e,t=!1){return e=e.__v_raw,!t&&be(Y(e),"iterate",st),Reflect.get(e,"size",e)}function Cs(e){e=Y(e);const t=Y(this);return ln(t).has.call(t,e)||(t.add(e),He(t,"add",e,e)),this}function xs(e,t){t=Y(t);const n=Y(this),{has:s,get:r}=ln(n);let i=s.call(n,e);i||(e=Y(e),i=s.call(n,e));const o=r.call(n,e);return n.set(e,t),i?It(t,o)&&He(n,"set",e,t):He(n,"add",e,t),this}function Es(e){const t=Y(this),{has:n,get:s}=ln(t);let r=n.call(t,e);r||(e=Y(e),r=n.call(t,e)),s&&s.call(t,e);const i=t.delete(e);return r&&He(t,"delete",e,void 0),i}function vs(){const e=Y(this),t=e.size!==0,n=e.clear();return t&&He(e,"clear",void 0,void 0),n}function Bt(e,t){return function(s,r){const i=this,o=i.__v_raw,l=Y(o),f=t?Gn:e?ns:Ot;return!e&&be(l,"iterate",st),o.forEach((a,d)=>s.call(r,f(a),f(d),i))}}function Dt(e,t,n){return function(...s){const r=this.__v_raw,i=Y(r),o=ut(i),l=e==="entries"||e===Symbol.iterator&&o,f=e==="keys"&&o,a=r[e](...s),d=n?Gn:t?ns:Ot;return!t&&be(i,"iterate",f?Fn:st),{next(){const{value:p,done:y}=a.next();return y?{value:p,done:y}:{value:l?[d(p[0]),d(p[1])]:d(p),done:y}},[Symbol.iterator](){return this}}}}function Ue(e){return function(...t){return e==="delete"?!1:this}}function Vi(){const e={get(i){return $t(this,i)},get size(){return jt(this)},has:Ut,add:Cs,set:xs,delete:Es,clear:vs,forEach:Bt(!1,!1)},t={get(i){return $t(this,i,!1,!0)},get size(){return jt(this)},has:Ut,add:Cs,set:xs,delete:Es,clear:vs,forEach:Bt(!1,!0)},n={get(i){return $t(this,i,!0)},get size(){return jt(this,!0)},has(i){return Ut.call(this,i,!0)},add:Ue("add"),set:Ue("set"),delete:Ue("delete"),clear:Ue("clear"),forEach:Bt(!0,!1)},s={get(i){return $t(this,i,!0,!0)},get size(){return jt(this,!0)},has(i){return Ut.call(this,i,!0)},add:Ue("add"),set:Ue("set"),delete:Ue("delete"),clear:Ue("clear"),forEach:Bt(!0,!0)};return["keys","values","entries",Symbol.iterator].forEach(i=>{e[i]=Dt(i,!1,!1),n[i]=Dt(i,!0,!1),t[i]=Dt(i,!1,!0),s[i]=Dt(i,!0,!0)}),[e,n,t,s]}const[zi,Yi,Ji,Xi]=Vi();function es(e,t){const n=t?e?Xi:Ji:e?Yi:zi;return(s,r,i)=>r==="__v_isReactive"?!e:r==="__v_isReadonly"?e:r==="__v_raw"?s:Reflect.get(V(n,r)&&r in s?n:s,r,i)}const Zi={get:es(!1,!1)},Qi={get:es(!1,!0)},Gi={get:es(!0,!1)},_r=new WeakMap,br=new WeakMap,yr=new WeakMap,eo=new WeakMap;function to(e){switch(e){case"Object":case"Array":return 1;case"Map":case"Set":case"WeakMap":case"WeakSet":return 2;default:return 0}}function no(e){return e.__v_skip||!Object.isExtensible(e)?0:to(Ai(e))}function cn(e){return gt(e)?e:ts(e,!1,mr,Zi,_r)}function so(e){return ts(e,!1,qi,Qi,br)}function wr(e){return ts(e,!0,Wi,Gi,yr)}function ts(e,t,n,s,r){if(!ee(e)||e.__v_raw&&!(t&&e.__v_isReactive))return e;const i=r.get(e);if(i)return i;const o=no(e);if(o===0)return e;const l=new Proxy(e,o===2?s:n);return r.set(e,l),l}function at(e){return gt(e)?at(e.__v_raw):!!(e&&e.__v_isReactive)}function gt(e){return!!(e&&e.__v_isReadonly)}function Zt(e){return!!(e&&e.__v_isShallow)}function Cr(e){return at(e)||gt(e)}function Y(e){const t=e&&e.__v_raw;return t?Y(t):e}function vt(e){return Xt(e,"__v_skip",!0),e}const Ot=e=>ee(e)?cn(e):e,ns=e=>ee(e)?wr(e):e;function ss(e){ke&&Ae&&(e=Y(e),hr(e.dep||(e.dep=Xn())))}function rs(e,t){e=Y(e),e.dep&&Pn(e.dep)}function oe(e){return!!(e&&e.__v_isRef===!0)}function xr(e){return Er(e,!1)}function ro(e){return Er(e,!0)}function Er(e,t){return oe(e)?e:new io(e,t)}class io{constructor(t,n){this.__v_isShallow=n,this.dep=void 0,this.__v_isRef=!0,this._rawValue=n?t:Y(t),this._value=n?t:Ot(t)}get value(){return ss(this),this._value}set value(t){const n=this.__v_isShallow||Zt(t)||gt(t);t=n?t:Y(t),It(t,this._rawValue)&&(this._rawValue=t,this._value=n?t:Ot(t),rs(this))}}function oo(e){return oe(e)?e.value:e}const lo={get:(e,t,n)=>oo(Reflect.get(e,t,n)),set:(e,t,n,s)=>{const r=e[t];return oe(r)&&!oe(n)?(r.value=n,!0):Reflect.set(e,t,n,s)}};function vr(e){return at(e)?e:new Proxy(e,lo)}class co{constructor(t){this.dep=void 0,this.__v_isRef=!0;const{get:n,set:s}=t(()=>ss(this),()=>rs(this));this._get=n,this._set=s}get value(){return this._get()}set value(t){this._set(t)}}function Cc(e){return new co(e)}class fo{constructor(t,n,s){this._object=t,this._key=n,this._defaultValue=s,this.__v_isRef=!0}get value(){const t=this._object[this._key];return t===void 0?this._defaultValue:t}set value(t){this._object[this._key]=t}}function xc(e,t,n){const s=e[t];return oe(s)?s:new fo(e,t,n)}var Tr;class uo{constructor(t,n,s,r){this._setter=n,this.dep=void 0,this.__v_isRef=!0,this[Tr]=!1,this._dirty=!0,this.effect=new Zn(t,()=>{this._dirty||(this._dirty=!0,rs(this))}),this.effect.computed=this,this.effect.active=this._cacheable=!r,this.__v_isReadonly=s}get value(){const t=Y(this);return ss(t),(t._dirty||!t._cacheable)&&(t._dirty=!1,t._value=t.effect.run()),t._value}set value(t){this._setter(t)}}Tr="__v_isReadonly";function ao(e,t,n=!1){let s,r;const i=j(e);return i?(s=e,r=Ie):(s=e.get,r=e.set),new uo(s,r,i||!r,n)}function We(e,t,n,s){let r;try{r=s?e(...s):e()}catch(i){fn(i,t,n)}return r}function xe(e,t,n,s){if(j(e)){const i=We(e,t,n,s);return i&&lr(i)&&i.catch(o=>{fn(o,t,n)}),i}const r=[];for(let i=0;i>>1;Ft(fe[s])Pe&&fe.splice(t,1)}function mo(e){N(e)?dt.push(...e):(!Ne||!Ne.includes(e,e.allowRecurse?Ge+1:Ge))&&dt.push(e),Ir()}function Ts(e,t=Mt?Pe+1:0){for(;tFt(n)-Ft(s)),Ge=0;Gee.id==null?1/0:e.id,_o=(e,t)=>{const n=Ft(e)-Ft(t);if(n===0){if(e.pre&&!t.pre)return-1;if(t.pre&&!e.pre)return 1}return n};function Or(e){Sn=!1,Mt=!0,fe.sort(_o);const t=Ie;try{for(Pe=0;Pere(R)?R.trim():R)),p&&(r=n.map(Jn))}let l,f=s[l=Vt(t)]||s[l=Vt(Se(t))];!f&&i&&(f=s[l=Vt(it(t))]),f&&xe(f,e,6,r);const a=s[l+"Once"];if(a){if(!e.emitted)e.emitted={};else if(e.emitted[l])return;e.emitted[l]=!0,xe(a,e,6,r)}}function Mr(e,t,n=!1){const s=t.emitsCache,r=s.get(e);if(r!==void 0)return r;const i=e.emits;let o={},l=!1;if(!j(e)){const f=a=>{const d=Mr(a,t,!0);d&&(l=!0,ce(o,d))};!n&&t.mixins.length&&t.mixins.forEach(f),e.extends&&f(e.extends),e.mixins&&e.mixins.forEach(f)}return!i&&!l?(ee(e)&&s.set(e,null),null):(N(i)?i.forEach(f=>o[f]=null):ce(o,i),ee(e)&&s.set(e,o),o)}function un(e,t){return!e||!Lt(t)?!1:(t=t.slice(2).replace(/Once$/,""),V(e,t[0].toLowerCase()+t.slice(1))||V(e,it(t))||V(e,t))}let ue=null,an=null;function Gt(e){const t=ue;return ue=e,an=e&&e.type.__scopeId||null,t}function Ec(e){an=e}function vc(){an=null}function yo(e,t=ue,n){if(!t||e._n)return e;const s=(...r)=>{s._d&&Ns(-1);const i=Gt(t);let o;try{o=e(...r)}finally{Gt(i),s._d&&Ns(1)}return o};return s._n=!0,s._c=!0,s._d=!0,s}function Cn(e){const{type:t,vnode:n,proxy:s,withProxy:r,props:i,propsOptions:[o],slots:l,attrs:f,emit:a,render:d,renderCache:p,data:y,setupState:R,ctx:L,inheritAttrs:M}=e;let W,_;const E=Gt(e);try{if(n.shapeFlag&4){const $=r||s;W=Te(d.call($,$,p,i,R,y,L)),_=f}else{const $=t;W=Te($.length>1?$(i,{attrs:f,slots:l,emit:a}):$(i,null)),_=t.props?f:wo(f)}}catch($){Rt.length=0,fn($,e,1),W=le(_e)}let A=W;if(_&&M!==!1){const $=Object.keys(_),{shapeFlag:D}=A;$.length&&D&7&&(o&&$.some(qn)&&(_=Co(_,o)),A=Ve(A,_))}return n.dirs&&(A=Ve(A),A.dirs=A.dirs?A.dirs.concat(n.dirs):n.dirs),n.transition&&(A.transition=n.transition),W=A,Gt(E),W}const wo=e=>{let t;for(const n in e)(n==="class"||n==="style"||Lt(n))&&((t||(t={}))[n]=e[n]);return t},Co=(e,t)=>{const n={};for(const s in e)(!qn(s)||!(s.slice(9)in t))&&(n[s]=e[s]);return n};function xo(e,t,n){const{props:s,children:r,component:i}=e,{props:o,children:l,patchFlag:f}=t,a=i.emitsOptions;if(t.dirs||t.transition)return!0;if(n&&f>=0){if(f&1024)return!0;if(f&16)return s?As(s,o,a):!!o;if(f&8){const d=t.dynamicProps;for(let p=0;pe.__isSuspense;function Fr(e,t){t&&t.pendingBranch?N(e)?t.effects.push(...e):t.effects.push(e):mo(e)}function To(e,t){if(ie){let n=ie.provides;const s=ie.parent&&ie.parent.provides;s===n&&(n=ie.provides=Object.create(s)),n[e]=t}}function ht(e,t,n=!1){const s=ie||ue;if(s){const r=s.parent==null?s.vnode.appContext&&s.vnode.appContext.provides:s.parent.provides;if(r&&e in r)return r[e];if(arguments.length>1)return n&&j(t)?t.call(s.proxy):t}}function Ao(e,t){return dn(e,null,t)}function Tc(e,t){return dn(e,null,{flush:"post"})}const Kt={};function zt(e,t,n){return dn(e,t,n)}function dn(e,t,{immediate:n,deep:s,flush:r,onTrack:i,onTrigger:o}=te){const l=ie;let f,a=!1,d=!1;if(oe(e)?(f=()=>e.value,a=Zt(e)):at(e)?(f=()=>e,s=!0):N(e)?(d=!0,a=e.some(A=>at(A)||Zt(A)),f=()=>e.map(A=>{if(oe(A))return A.value;if(at(A))return ct(A);if(j(A))return We(A,l,2)})):j(e)?t?f=()=>We(e,l,2):f=()=>{if(!(l&&l.isUnmounted))return p&&p(),xe(e,l,3,[y])}:f=Ie,t&&s){const A=f;f=()=>ct(A())}let p,y=A=>{p=_.onStop=()=>{We(A,l,4)}},R;if(St)if(y=Ie,t?n&&xe(t,l,3,[f(),d?[]:void 0,y]):f(),r==="sync"){const A=bl();R=A.__watcherHandles||(A.__watcherHandles=[])}else return Ie;let L=d?new Array(e.length).fill(Kt):Kt;const M=()=>{if(_.active)if(t){const A=_.run();(s||a||(d?A.some(($,D)=>It($,L[D])):It(A,L)))&&(p&&p(),xe(t,l,3,[A,L===Kt?void 0:d&&L[0]===Kt?[]:L,y]),L=A)}else _.run()};M.allowRecurse=!!t;let W;r==="sync"?W=M:r==="post"?W=()=>de(M,l&&l.suspense):(M.pre=!0,l&&(M.id=l.uid),W=()=>os(M));const _=new Zn(f,W);t?n?M():L=_.run():r==="post"?de(_.run.bind(_),l&&l.suspense):_.run();const E=()=>{_.stop(),l&&l.scope&&Vn(l.scope.effects,_)};return R&&R.push(E),E}function Ro(e,t,n){const s=this.proxy,r=re(e)?e.includes(".")?Pr(s,e):()=>s[e]:e.bind(s,s);let i;j(t)?i=t:(i=t.handler,n=t);const o=ie;_t(this);const l=dn(r,i.bind(s),n);return o?_t(o):rt(),l}function Pr(e,t){const n=t.split(".");return()=>{let s=e;for(let r=0;r{ct(n,t)});else if(fr(e))for(const n in e)ct(e[n],t);return e}function Io(){const e={isMounted:!1,isLeaving:!1,isUnmounting:!1,leavingVNodes:new Map};return gn(()=>{e.isMounted=!0}),Ur(()=>{e.isUnmounting=!0}),e}const ye=[Function,Array],Oo={name:"BaseTransition",props:{mode:String,appear:Boolean,persisted:Boolean,onBeforeEnter:ye,onEnter:ye,onAfterEnter:ye,onEnterCancelled:ye,onBeforeLeave:ye,onLeave:ye,onAfterLeave:ye,onLeaveCancelled:ye,onBeforeAppear:ye,onAppear:ye,onAfterAppear:ye,onAppearCancelled:ye},setup(e,{slots:t}){const n=ni(),s=Io();let r;return()=>{const i=t.default&&Nr(t.default(),!0);if(!i||!i.length)return;let o=i[0];if(i.length>1){for(const M of i)if(M.type!==_e){o=M;break}}const l=Y(e),{mode:f}=l;if(s.isLeaving)return xn(o);const a=Rs(o);if(!a)return xn(o);const d=Ln(a,l,s,n);Nn(a,d);const p=n.subTree,y=p&&Rs(p);let R=!1;const{getTransitionKey:L}=a.type;if(L){const M=L();r===void 0?r=M:M!==r&&(r=M,R=!0)}if(y&&y.type!==_e&&(!et(a,y)||R)){const M=Ln(y,l,s,n);if(Nn(y,M),f==="out-in")return s.isLeaving=!0,M.afterLeave=()=>{s.isLeaving=!1,n.update.active!==!1&&n.update()},xn(o);f==="in-out"&&a.type!==_e&&(M.delayLeave=(W,_,E)=>{const A=Lr(s,y);A[String(y.key)]=y,W._leaveCb=()=>{_(),W._leaveCb=void 0,delete d.delayedLeave},d.delayedLeave=E})}return o}}},Sr=Oo;function Lr(e,t){const{leavingVNodes:n}=e;let s=n.get(t.type);return s||(s=Object.create(null),n.set(t.type,s)),s}function Ln(e,t,n,s){const{appear:r,mode:i,persisted:o=!1,onBeforeEnter:l,onEnter:f,onAfterEnter:a,onEnterCancelled:d,onBeforeLeave:p,onLeave:y,onAfterLeave:R,onLeaveCancelled:L,onBeforeAppear:M,onAppear:W,onAfterAppear:_,onAppearCancelled:E}=t,A=String(e.key),$=Lr(n,e),D=(T,B)=>{T&&xe(T,s,9,B)},Z=(T,B)=>{const U=B[1];D(T,B),N(T)?T.every(z=>z.length<=1)&&U():T.length<=1&&U()},q={mode:i,persisted:o,beforeEnter(T){let B=l;if(!n.isMounted)if(r)B=M||l;else return;T._leaveCb&&T._leaveCb(!0);const U=$[A];U&&et(e,U)&&U.el._leaveCb&&U.el._leaveCb(),D(B,[T])},enter(T){let B=f,U=a,z=d;if(!n.isMounted)if(r)B=W||f,U=_||a,z=E||d;else return;let I=!1;const K=T._enterCb=F=>{I||(I=!0,F?D(z,[T]):D(U,[T]),q.delayedLeave&&q.delayedLeave(),T._enterCb=void 0)};B?Z(B,[T,K]):K()},leave(T,B){const U=String(e.key);if(T._enterCb&&T._enterCb(!0),n.isUnmounting)return B();D(p,[T]);let z=!1;const I=T._leaveCb=K=>{z||(z=!0,B(),K?D(L,[T]):D(R,[T]),T._leaveCb=void 0,$[U]===e&&delete $[U])};$[U]=e,y?Z(y,[T,I]):I()},clone(T){return Ln(T,t,n,s)}};return q}function xn(e){if(hn(e))return e=Ve(e),e.children=null,e}function Rs(e){return hn(e)?e.children?e.children[0]:void 0:e}function Nn(e,t){e.shapeFlag&6&&e.component?Nn(e.component.subTree,t):e.shapeFlag&128?(e.ssContent.transition=t.clone(e.ssContent),e.ssFallback.transition=t.clone(e.ssFallback)):e.transition=t}function Nr(e,t=!1,n){let s=[],r=0;for(let i=0;i1)for(let i=0;i!!e.type.__asyncLoader,hn=e=>e.type.__isKeepAlive;function Mo(e,t){$r(e,"a",t)}function Fo(e,t){$r(e,"da",t)}function $r(e,t,n=ie){const s=e.__wdc||(e.__wdc=()=>{let r=n;for(;r;){if(r.isDeactivated)return;r=r.parent}return e()});if(pn(t,s,n),n){let r=n.parent;for(;r&&r.parent;)hn(r.parent.vnode)&&Po(s,t,n,r),r=r.parent}}function Po(e,t,n,s){const r=pn(t,e,s,!0);mn(()=>{Vn(s[t],r)},n)}function pn(e,t,n=ie,s=!1){if(n){const r=n[e]||(n[e]=[]),i=t.__weh||(t.__weh=(...o)=>{if(n.isUnmounted)return;bt(),_t(n);const l=xe(t,n,e,o);return rt(),yt(),l});return s?r.unshift(i):r.push(i),i}}const $e=e=>(t,n=ie)=>(!St||e==="sp")&&pn(e,(...s)=>t(...s),n),So=$e("bm"),gn=$e("m"),Lo=$e("bu"),No=$e("u"),Ur=$e("bum"),mn=$e("um"),Ho=$e("sp"),$o=$e("rtg"),Uo=$e("rtc");function jo(e,t=ie){pn("ec",e,t)}function Fe(e,t,n,s){const r=e.dirs,i=t&&t.dirs;for(let o=0;ot(o,l,void 0,i&&i[l]));else{const o=Object.keys(e);r=new Array(o.length);for(let l=0,f=o.length;lnn(t)?!(t.type===_e||t.type===he&&!Dr(t.children)):!0)?e:null}function Mc(e,t){const n={};for(const s in e)n[t&&/[A-Z]/.test(s)?`on:${s}`:Vt(s)]=e[s];return n}const Hn=e=>e?si(e)?as(e)||e.proxy:Hn(e.parent):null,Tt=ce(Object.create(null),{$:e=>e,$el:e=>e.vnode.el,$data:e=>e.data,$props:e=>e.props,$attrs:e=>e.attrs,$slots:e=>e.slots,$refs:e=>e.refs,$parent:e=>Hn(e.parent),$root:e=>Hn(e.root),$emit:e=>e.emit,$options:e=>cs(e),$forceUpdate:e=>e.f||(e.f=()=>os(e.update)),$nextTick:e=>e.n||(e.n=Rr.bind(e.proxy)),$watch:e=>Ro.bind(e)}),En=(e,t)=>e!==te&&!e.__isScriptSetup&&V(e,t),Bo={get({_:e},t){const{ctx:n,setupState:s,data:r,props:i,accessCache:o,type:l,appContext:f}=e;let a;if(t[0]!=="$"){const R=o[t];if(R!==void 0)switch(R){case 1:return s[t];case 2:return r[t];case 4:return n[t];case 3:return i[t]}else{if(En(s,t))return o[t]=1,s[t];if(r!==te&&V(r,t))return o[t]=2,r[t];if((a=e.propsOptions[0])&&V(a,t))return o[t]=3,i[t];if(n!==te&&V(n,t))return o[t]=4,n[t];$n&&(o[t]=0)}}const d=Tt[t];let p,y;if(d)return t==="$attrs"&&be(e,"get",t),d(e);if((p=l.__cssModules)&&(p=p[t]))return p;if(n!==te&&V(n,t))return o[t]=4,n[t];if(y=f.config.globalProperties,V(y,t))return y[t]},set({_:e},t,n){const{data:s,setupState:r,ctx:i}=e;return En(r,t)?(r[t]=n,!0):s!==te&&V(s,t)?(s[t]=n,!0):V(e.props,t)||t[0]==="$"&&t.slice(1)in e?!1:(i[t]=n,!0)},has({_:{data:e,setupState:t,accessCache:n,ctx:s,appContext:r,propsOptions:i}},o){let l;return!!n[o]||e!==te&&V(e,o)||En(t,o)||(l=i[0])&&V(l,o)||V(s,o)||V(Tt,o)||V(r.config.globalProperties,o)},defineProperty(e,t,n){return n.get!=null?e._.accessCache[t]=0:V(n,"value")&&this.set(e,t,n.value,null),Reflect.defineProperty(e,t,n)}};let $n=!0;function Do(e){const t=cs(e),n=e.proxy,s=e.ctx;$n=!1,t.beforeCreate&&Os(t.beforeCreate,e,"bc");const{data:r,computed:i,methods:o,watch:l,provide:f,inject:a,created:d,beforeMount:p,mounted:y,beforeUpdate:R,updated:L,activated:M,deactivated:W,beforeDestroy:_,beforeUnmount:E,destroyed:A,unmounted:$,render:D,renderTracked:Z,renderTriggered:q,errorCaptured:T,serverPrefetch:B,expose:U,inheritAttrs:z,components:I,directives:K,filters:F}=t;if(a&&Ko(a,s,null,e.appContext.config.unwrapInjectedRef),o)for(const ne in o){const Q=o[ne];j(Q)&&(s[ne]=Q.bind(n))}if(r){const ne=r.call(n,n);ee(ne)&&(e.data=cn(ne))}if($n=!0,i)for(const ne in i){const Q=i[ne],ze=j(Q)?Q.bind(n,n):j(Q.get)?Q.get.bind(n,n):Ie,Nt=!j(Q)&&j(Q.set)?Q.set.bind(n):Ie,Ye=ve({get:ze,set:Nt});Object.defineProperty(s,ne,{enumerable:!0,configurable:!0,get:()=>Ye.value,set:Oe=>Ye.value=Oe})}if(l)for(const ne in l)Kr(l[ne],s,n,ne);if(f){const ne=j(f)?f.call(n):f;Reflect.ownKeys(ne).forEach(Q=>{To(Q,ne[Q])})}d&&Os(d,e,"c");function J(ne,Q){N(Q)?Q.forEach(ze=>ne(ze.bind(n))):Q&&ne(Q.bind(n))}if(J(So,p),J(gn,y),J(Lo,R),J(No,L),J(Mo,M),J(Fo,W),J(jo,T),J(Uo,Z),J($o,q),J(Ur,E),J(mn,$),J(Ho,B),N(U))if(U.length){const ne=e.exposed||(e.exposed={});U.forEach(Q=>{Object.defineProperty(ne,Q,{get:()=>n[Q],set:ze=>n[Q]=ze})})}else e.exposed||(e.exposed={});D&&e.render===Ie&&(e.render=D),z!=null&&(e.inheritAttrs=z),I&&(e.components=I),K&&(e.directives=K)}function Ko(e,t,n=Ie,s=!1){N(e)&&(e=Un(e));for(const r in e){const i=e[r];let o;ee(i)?"default"in i?o=ht(i.from||r,i.default,!0):o=ht(i.from||r):o=ht(i),oe(o)&&s?Object.defineProperty(t,r,{enumerable:!0,configurable:!0,get:()=>o.value,set:l=>o.value=l}):t[r]=o}}function Os(e,t,n){xe(N(e)?e.map(s=>s.bind(t.proxy)):e.bind(t.proxy),t,n)}function Kr(e,t,n,s){const r=s.includes(".")?Pr(n,s):()=>n[s];if(re(e)){const i=t[e];j(i)&&zt(r,i)}else if(j(e))zt(r,e.bind(n));else if(ee(e))if(N(e))e.forEach(i=>Kr(i,t,n,s));else{const i=j(e.handler)?e.handler.bind(n):t[e.handler];j(i)&&zt(r,i,e)}}function cs(e){const t=e.type,{mixins:n,extends:s}=t,{mixins:r,optionsCache:i,config:{optionMergeStrategies:o}}=e.appContext,l=i.get(t);let f;return l?f=l:!r.length&&!n&&!s?f=t:(f={},r.length&&r.forEach(a=>en(f,a,o,!0)),en(f,t,o)),ee(t)&&i.set(t,f),f}function en(e,t,n,s=!1){const{mixins:r,extends:i}=t;i&&en(e,i,n,!0),r&&r.forEach(o=>en(e,o,n,!0));for(const o in t)if(!(s&&o==="expose")){const l=ko[o]||n&&n[o];e[o]=l?l(e[o],t[o]):t[o]}return e}const ko={data:Ms,props:Qe,emits:Qe,methods:Qe,computed:Qe,beforeCreate:ae,created:ae,beforeMount:ae,mounted:ae,beforeUpdate:ae,updated:ae,beforeDestroy:ae,beforeUnmount:ae,destroyed:ae,unmounted:ae,activated:ae,deactivated:ae,errorCaptured:ae,serverPrefetch:ae,components:Qe,directives:Qe,watch:qo,provide:Ms,inject:Wo};function Ms(e,t){return t?e?function(){return ce(j(e)?e.call(this,this):e,j(t)?t.call(this,this):t)}:t:e}function Wo(e,t){return Qe(Un(e),Un(t))}function Un(e){if(N(e)){const t={};for(let n=0;n0)&&!(o&16)){if(o&8){const d=e.vnode.dynamicProps;for(let p=0;p{f=!0;const[y,R]=Wr(p,t,!0);ce(o,y),R&&l.push(...R)};!n&&t.mixins.length&&t.mixins.forEach(d),e.extends&&d(e.extends),e.mixins&&e.mixins.forEach(d)}if(!i&&!f)return ee(e)&&s.set(e,ft),ft;if(N(i))for(let d=0;d-1,R[1]=M<0||L-1||V(R,"default"))&&l.push(p)}}}const a=[o,l];return ee(e)&&s.set(e,a),a}function Fs(e){return e[0]!=="$"}function Ps(e){const t=e&&e.toString().match(/^\s*function (\w+)/);return t?t[1]:e===null?"null":""}function Ss(e,t){return Ps(e)===Ps(t)}function Ls(e,t){return N(t)?t.findIndex(n=>Ss(n,e)):j(t)&&Ss(t,e)?0:-1}const qr=e=>e[0]==="_"||e==="$stable",fs=e=>N(e)?e.map(Te):[Te(e)],Yo=(e,t,n)=>{if(t._n)return t;const s=yo((...r)=>fs(t(...r)),n);return s._c=!1,s},Vr=(e,t,n)=>{const s=e._ctx;for(const r in e){if(qr(r))continue;const i=e[r];if(j(i))t[r]=Yo(r,i,s);else if(i!=null){const o=fs(i);t[r]=()=>o}}},zr=(e,t)=>{const n=fs(t);e.slots.default=()=>n},Jo=(e,t)=>{if(e.vnode.shapeFlag&32){const n=t._;n?(e.slots=Y(t),Xt(t,"_",n)):Vr(t,e.slots={})}else e.slots={},t&&zr(e,t);Xt(e.slots,_n,1)},Xo=(e,t,n)=>{const{vnode:s,slots:r}=e;let i=!0,o=te;if(s.shapeFlag&32){const l=t._;l?n&&l===1?i=!1:(ce(r,t),!n&&l===1&&delete r._):(i=!t.$stable,Vr(t,r)),o=t}else t&&(zr(e,t),o={default:1});if(i)for(const l in r)!qr(l)&&!(l in o)&&delete r[l]};function Yr(){return{app:null,config:{isNativeTag:Ei,performance:!1,globalProperties:{},optionMergeStrategies:{},errorHandler:void 0,warnHandler:void 0,compilerOptions:{}},mixins:[],components:{},directives:{},provides:Object.create(null),optionsCache:new WeakMap,propsCache:new WeakMap,emitsCache:new WeakMap}}let Zo=0;function Qo(e,t){return function(s,r=null){j(s)||(s=Object.assign({},s)),r!=null&&!ee(r)&&(r=null);const i=Yr(),o=new Set;let l=!1;const f=i.app={_uid:Zo++,_component:s,_props:r,_container:null,_context:i,_instance:null,version:yl,get config(){return i.config},set config(a){},use(a,...d){return o.has(a)||(a&&j(a.install)?(o.add(a),a.install(f,...d)):j(a)&&(o.add(a),a(f,...d))),f},mixin(a){return i.mixins.includes(a)||i.mixins.push(a),f},component(a,d){return d?(i.components[a]=d,f):i.components[a]},directive(a,d){return d?(i.directives[a]=d,f):i.directives[a]},mount(a,d,p){if(!l){const y=le(s,r);return y.appContext=i,d&&t?t(y,a):e(y,a,p),l=!0,f._container=a,a.__vue_app__=f,as(y.component)||y.component.proxy}},unmount(){l&&(e(null,f._container),delete f._container.__vue_app__)},provide(a,d){return i.provides[a]=d,f}};return f}}function tn(e,t,n,s,r=!1){if(N(e)){e.forEach((y,R)=>tn(y,t&&(N(t)?t[R]:t),n,s,r));return}if(pt(s)&&!r)return;const i=s.shapeFlag&4?as(s.component)||s.component.proxy:s.el,o=r?null:i,{i:l,r:f}=e,a=t&&t.r,d=l.refs===te?l.refs={}:l.refs,p=l.setupState;if(a!=null&&a!==f&&(re(a)?(d[a]=null,V(p,a)&&(p[a]=null)):oe(a)&&(a.value=null)),j(f))We(f,l,12,[o,d]);else{const y=re(f),R=oe(f);if(y||R){const L=()=>{if(e.f){const M=y?V(p,f)?p[f]:d[f]:f.value;r?N(M)&&Vn(M,i):N(M)?M.includes(i)||M.push(i):y?(d[f]=[i],V(p,f)&&(p[f]=d[f])):(f.value=[i],e.k&&(d[e.k]=f.value))}else y?(d[f]=o,V(p,f)&&(p[f]=o)):R&&(f.value=o,e.k&&(d[e.k]=o))};o?(L.id=-1,de(L,n)):L()}}}let je=!1;const kt=e=>/svg/.test(e.namespaceURI)&&e.tagName!=="foreignObject",Wt=e=>e.nodeType===8;function Go(e){const{mt:t,p:n,o:{patchProp:s,createText:r,nextSibling:i,parentNode:o,remove:l,insert:f,createComment:a}}=e,d=(_,E)=>{if(!E.hasChildNodes()){n(null,_,E),Qt(),E._vnode=_;return}je=!1,p(E.firstChild,_,null,null,null),Qt(),E._vnode=_,je&&console.error("Hydration completed but contains mismatches.")},p=(_,E,A,$,D,Z=!1)=>{const q=Wt(_)&&_.data==="[",T=()=>M(_,E,A,$,D,q),{type:B,ref:U,shapeFlag:z,patchFlag:I}=E;let K=_.nodeType;E.el=_,I===-2&&(Z=!1,E.dynamicChildren=null);let F=null;switch(B){case mt:K!==3?E.children===""?(f(E.el=r(""),o(_),_),F=_):F=T():(_.data!==E.children&&(je=!0,_.data=E.children),F=i(_));break;case _e:K!==8||q?F=T():F=i(_);break;case At:if(q&&(_=i(_),K=_.nodeType),K===1||K===3){F=_;const pe=!E.children.length;for(let J=0;J{Z=Z||!!E.dynamicChildren;const{type:q,props:T,patchFlag:B,shapeFlag:U,dirs:z}=E,I=q==="input"&&z||q==="option";if(I||B!==-1){if(z&&Fe(E,null,A,"created"),T)if(I||!Z||B&48)for(const F in T)(I&&F.endsWith("value")||Lt(F)&&!Et(F))&&s(_,F,null,T[F],!1,void 0,A);else T.onClick&&s(_,"onClick",null,T.onClick,!1,void 0,A);let K;if((K=T&&T.onVnodeBeforeMount)&&we(K,A,E),z&&Fe(E,null,A,"beforeMount"),((K=T&&T.onVnodeMounted)||z)&&Fr(()=>{K&&we(K,A,E),z&&Fe(E,null,A,"mounted")},$),U&16&&!(T&&(T.innerHTML||T.textContent))){let F=R(_.firstChild,E,_,A,$,D,Z);for(;F;){je=!0;const pe=F;F=F.nextSibling,l(pe)}}else U&8&&_.textContent!==E.children&&(je=!0,_.textContent=E.children)}return _.nextSibling},R=(_,E,A,$,D,Z,q)=>{q=q||!!E.dynamicChildren;const T=E.children,B=T.length;for(let U=0;U{const{slotScopeIds:q}=E;q&&(D=D?D.concat(q):q);const T=o(_),B=R(i(_),E,T,A,$,D,Z);return B&&Wt(B)&&B.data==="]"?i(E.anchor=B):(je=!0,f(E.anchor=a("]"),T,B),B)},M=(_,E,A,$,D,Z)=>{if(je=!0,E.el=null,Z){const B=W(_);for(;;){const U=i(_);if(U&&U!==B)l(U);else break}}const q=i(_),T=o(_);return l(_),n(null,E,T,q,A,$,kt(T),D),q},W=_=>{let E=0;for(;_;)if(_=i(_),_&&Wt(_)&&(_.data==="["&&E++,_.data==="]")){if(E===0)return i(_);E--}return _};return[d,p]}const de=Fr;function el(e){return tl(e,Go)}function tl(e,t){const n=Oi();n.__VUE__=!0;const{insert:s,remove:r,patchProp:i,createElement:o,createText:l,createComment:f,setText:a,setElementText:d,parentNode:p,nextSibling:y,setScopeId:R=Ie,insertStaticContent:L}=e,M=(c,u,h,m=null,g=null,C=null,v=!1,w=null,x=!!u.dynamicChildren)=>{if(c===u)return;c&&!et(c,u)&&(m=Ht(c),Oe(c,g,C,!0),c=null),u.patchFlag===-2&&(x=!1,u.dynamicChildren=null);const{type:b,ref:P,shapeFlag:O}=u;switch(b){case mt:W(c,u,h,m);break;case _e:_(c,u,h,m);break;case At:c==null&&E(u,h,m,v);break;case he:I(c,u,h,m,g,C,v,w,x);break;default:O&1?D(c,u,h,m,g,C,v,w,x):O&6?K(c,u,h,m,g,C,v,w,x):(O&64||O&128)&&b.process(c,u,h,m,g,C,v,w,x,ot)}P!=null&&g&&tn(P,c&&c.ref,C,u||c,!u)},W=(c,u,h,m)=>{if(c==null)s(u.el=l(u.children),h,m);else{const g=u.el=c.el;u.children!==c.children&&a(g,u.children)}},_=(c,u,h,m)=>{c==null?s(u.el=f(u.children||""),h,m):u.el=c.el},E=(c,u,h,m)=>{[c.el,c.anchor]=L(c.children,u,h,m,c.el,c.anchor)},A=({el:c,anchor:u},h,m)=>{let g;for(;c&&c!==u;)g=y(c),s(c,h,m),c=g;s(u,h,m)},$=({el:c,anchor:u})=>{let h;for(;c&&c!==u;)h=y(c),r(c),c=h;r(u)},D=(c,u,h,m,g,C,v,w,x)=>{v=v||u.type==="svg",c==null?Z(u,h,m,g,C,v,w,x):B(c,u,g,C,v,w,x)},Z=(c,u,h,m,g,C,v,w)=>{let x,b;const{type:P,props:O,shapeFlag:S,transition:H,dirs:k}=c;if(x=c.el=o(c.type,C,O&&O.is,O),S&8?d(x,c.children):S&16&&T(c.children,x,null,m,g,C&&P!=="foreignObject",v,w),k&&Fe(c,null,m,"created"),O){for(const X in O)X!=="value"&&!Et(X)&&i(x,X,null,O[X],C,c.children,m,g,Le);"value"in O&&i(x,"value",null,O.value),(b=O.onVnodeBeforeMount)&&we(b,m,c)}q(x,c,c.scopeId,v,m),k&&Fe(c,null,m,"beforeMount");const G=(!g||g&&!g.pendingBranch)&&H&&!H.persisted;G&&H.beforeEnter(x),s(x,u,h),((b=O&&O.onVnodeMounted)||G||k)&&de(()=>{b&&we(b,m,c),G&&H.enter(x),k&&Fe(c,null,m,"mounted")},g)},q=(c,u,h,m,g)=>{if(h&&R(c,h),m)for(let C=0;C{for(let b=x;b{const w=u.el=c.el;let{patchFlag:x,dynamicChildren:b,dirs:P}=u;x|=c.patchFlag&16;const O=c.props||te,S=u.props||te;let H;h&&Je(h,!1),(H=S.onVnodeBeforeUpdate)&&we(H,h,u,c),P&&Fe(u,c,h,"beforeUpdate"),h&&Je(h,!0);const k=g&&u.type!=="foreignObject";if(b?U(c.dynamicChildren,b,w,h,m,k,C):v||Q(c,u,w,null,h,m,k,C,!1),x>0){if(x&16)z(w,u,O,S,h,m,g);else if(x&2&&O.class!==S.class&&i(w,"class",null,S.class,g),x&4&&i(w,"style",O.style,S.style,g),x&8){const G=u.dynamicProps;for(let X=0;X{H&&we(H,h,u,c),P&&Fe(u,c,h,"updated")},m)},U=(c,u,h,m,g,C,v)=>{for(let w=0;w{if(h!==m){if(h!==te)for(const w in h)!Et(w)&&!(w in m)&&i(c,w,h[w],null,v,u.children,g,C,Le);for(const w in m){if(Et(w))continue;const x=m[w],b=h[w];x!==b&&w!=="value"&&i(c,w,b,x,v,u.children,g,C,Le)}"value"in m&&i(c,"value",h.value,m.value)}},I=(c,u,h,m,g,C,v,w,x)=>{const b=u.el=c?c.el:l(""),P=u.anchor=c?c.anchor:l("");let{patchFlag:O,dynamicChildren:S,slotScopeIds:H}=u;H&&(w=w?w.concat(H):H),c==null?(s(b,h,m),s(P,h,m),T(u.children,h,P,g,C,v,w,x)):O>0&&O&64&&S&&c.dynamicChildren?(U(c.dynamicChildren,S,h,g,C,v,w),(u.key!=null||g&&u===g.subTree)&&Jr(c,u,!0)):Q(c,u,h,P,g,C,v,w,x)},K=(c,u,h,m,g,C,v,w,x)=>{u.slotScopeIds=w,c==null?u.shapeFlag&512?g.ctx.activate(u,h,m,v,x):F(u,h,m,g,C,v,x):pe(c,u,x)},F=(c,u,h,m,g,C,v)=>{const w=c.component=ul(c,m,g);if(hn(c)&&(w.ctx.renderer=ot),al(w),w.asyncDep){if(g&&g.registerDep(w,J),!c.el){const x=w.subTree=le(_e);_(null,x,u,h)}return}J(w,c,u,h,g,C,v)},pe=(c,u,h)=>{const m=u.component=c.component;if(xo(c,u,h))if(m.asyncDep&&!m.asyncResolved){ne(m,u,h);return}else m.next=u,go(m.update),m.update();else u.el=c.el,m.vnode=u},J=(c,u,h,m,g,C,v)=>{const w=()=>{if(c.isMounted){let{next:P,bu:O,u:S,parent:H,vnode:k}=c,G=P,X;Je(c,!1),P?(P.el=k.el,ne(c,P,v)):P=k,O&&wn(O),(X=P.props&&P.props.onVnodeBeforeUpdate)&&we(X,H,P,k),Je(c,!0);const se=Cn(c),Ee=c.subTree;c.subTree=se,M(Ee,se,p(Ee.el),Ht(Ee),c,g,C),P.el=se.el,G===null&&Eo(c,se.el),S&&de(S,g),(X=P.props&&P.props.onVnodeUpdated)&&de(()=>we(X,H,P,k),g)}else{let P;const{el:O,props:S}=u,{bm:H,m:k,parent:G}=c,X=pt(u);if(Je(c,!1),H&&wn(H),!X&&(P=S&&S.onVnodeBeforeMount)&&we(P,G,u),Je(c,!0),O&&yn){const se=()=>{c.subTree=Cn(c),yn(O,c.subTree,c,g,null)};X?u.type.__asyncLoader().then(()=>!c.isUnmounted&&se()):se()}else{const se=c.subTree=Cn(c);M(null,se,h,m,c,g,C),u.el=se.el}if(k&&de(k,g),!X&&(P=S&&S.onVnodeMounted)){const se=u;de(()=>we(P,G,se),g)}(u.shapeFlag&256||G&&pt(G.vnode)&&G.vnode.shapeFlag&256)&&c.a&&de(c.a,g),c.isMounted=!0,u=h=m=null}},x=c.effect=new Zn(w,()=>os(b),c.scope),b=c.update=()=>x.run();b.id=c.uid,Je(c,!0),b()},ne=(c,u,h)=>{u.component=c;const m=c.vnode.props;c.vnode=u,c.next=null,zo(c,u.props,m,h),Xo(c,u.children,h),bt(),Ts(),yt()},Q=(c,u,h,m,g,C,v,w,x=!1)=>{const b=c&&c.children,P=c?c.shapeFlag:0,O=u.children,{patchFlag:S,shapeFlag:H}=u;if(S>0){if(S&128){Nt(b,O,h,m,g,C,v,w,x);return}else if(S&256){ze(b,O,h,m,g,C,v,w,x);return}}H&8?(P&16&&Le(b,g,C),O!==b&&d(h,O)):P&16?H&16?Nt(b,O,h,m,g,C,v,w,x):Le(b,g,C,!0):(P&8&&d(h,""),H&16&&T(O,h,m,g,C,v,w,x))},ze=(c,u,h,m,g,C,v,w,x)=>{c=c||ft,u=u||ft;const b=c.length,P=u.length,O=Math.min(b,P);let S;for(S=0;SP?Le(c,g,C,!0,!1,O):T(u,h,m,g,C,v,w,x,O)},Nt=(c,u,h,m,g,C,v,w,x)=>{let b=0;const P=u.length;let O=c.length-1,S=P-1;for(;b<=O&&b<=S;){const H=c[b],k=u[b]=x?Ke(u[b]):Te(u[b]);if(et(H,k))M(H,k,h,null,g,C,v,w,x);else break;b++}for(;b<=O&&b<=S;){const H=c[O],k=u[S]=x?Ke(u[S]):Te(u[S]);if(et(H,k))M(H,k,h,null,g,C,v,w,x);else break;O--,S--}if(b>O){if(b<=S){const H=S+1,k=HS)for(;b<=O;)Oe(c[b],g,C,!0),b++;else{const H=b,k=b,G=new Map;for(b=k;b<=S;b++){const ge=u[b]=x?Ke(u[b]):Te(u[b]);ge.key!=null&&G.set(ge.key,b)}let X,se=0;const Ee=S-k+1;let lt=!1,ps=0;const wt=new Array(Ee);for(b=0;b=Ee){Oe(ge,g,C,!0);continue}let Me;if(ge.key!=null)Me=G.get(ge.key);else for(X=k;X<=S;X++)if(wt[X-k]===0&&et(ge,u[X])){Me=X;break}Me===void 0?Oe(ge,g,C,!0):(wt[Me-k]=b+1,Me>=ps?ps=Me:lt=!0,M(ge,u[Me],h,null,g,C,v,w,x),se++)}const gs=lt?nl(wt):ft;for(X=gs.length-1,b=Ee-1;b>=0;b--){const ge=k+b,Me=u[ge],ms=ge+1{const{el:C,type:v,transition:w,children:x,shapeFlag:b}=c;if(b&6){Ye(c.component.subTree,u,h,m);return}if(b&128){c.suspense.move(u,h,m);return}if(b&64){v.move(c,u,h,ot);return}if(v===he){s(C,u,h);for(let O=0;Ow.enter(C),g);else{const{leave:O,delayLeave:S,afterLeave:H}=w,k=()=>s(C,u,h),G=()=>{O(C,()=>{k(),H&&H()})};S?S(C,k,G):G()}else s(C,u,h)},Oe=(c,u,h,m=!1,g=!1)=>{const{type:C,props:v,ref:w,children:x,dynamicChildren:b,shapeFlag:P,patchFlag:O,dirs:S}=c;if(w!=null&&tn(w,null,h,c,!0),P&256){u.ctx.deactivate(c);return}const H=P&1&&S,k=!pt(c);let G;if(k&&(G=v&&v.onVnodeBeforeUnmount)&&we(G,u,c),P&6)mi(c.component,h,m);else{if(P&128){c.suspense.unmount(h,m);return}H&&Fe(c,null,u,"beforeUnmount"),P&64?c.type.remove(c,u,h,g,ot,m):b&&(C!==he||O>0&&O&64)?Le(b,u,h,!1,!0):(C===he&&O&384||!g&&P&16)&&Le(x,u,h),m&&ds(c)}(k&&(G=v&&v.onVnodeUnmounted)||H)&&de(()=>{G&&we(G,u,c),H&&Fe(c,null,u,"unmounted")},h)},ds=c=>{const{type:u,el:h,anchor:m,transition:g}=c;if(u===he){gi(h,m);return}if(u===At){$(c);return}const C=()=>{r(h),g&&!g.persisted&&g.afterLeave&&g.afterLeave()};if(c.shapeFlag&1&&g&&!g.persisted){const{leave:v,delayLeave:w}=g,x=()=>v(h,C);w?w(c.el,C,x):x()}else C()},gi=(c,u)=>{let h;for(;c!==u;)h=y(c),r(c),c=h;r(u)},mi=(c,u,h)=>{const{bum:m,scope:g,update:C,subTree:v,um:w}=c;m&&wn(m),g.stop(),C&&(C.active=!1,Oe(v,c,u,h)),w&&de(w,u),de(()=>{c.isUnmounted=!0},u),u&&u.pendingBranch&&!u.isUnmounted&&c.asyncDep&&!c.asyncResolved&&c.suspenseId===u.pendingId&&(u.deps--,u.deps===0&&u.resolve())},Le=(c,u,h,m=!1,g=!1,C=0)=>{for(let v=C;vc.shapeFlag&6?Ht(c.component.subTree):c.shapeFlag&128?c.suspense.next():y(c.anchor||c.el),hs=(c,u,h)=>{c==null?u._vnode&&Oe(u._vnode,null,null,!0):M(u._vnode||null,c,u,null,null,null,h),Ts(),Qt(),u._vnode=c},ot={p:M,um:Oe,m:Ye,r:ds,mt:F,mc:T,pc:Q,pbc:U,n:Ht,o:e};let bn,yn;return t&&([bn,yn]=t(ot)),{render:hs,hydrate:bn,createApp:Qo(hs,bn)}}function Je({effect:e,update:t},n){e.allowRecurse=t.allowRecurse=n}function Jr(e,t,n=!1){const s=e.children,r=t.children;if(N(s)&&N(r))for(let i=0;i>1,e[n[l]]0&&(t[s]=n[i-1]),n[i]=s)}}for(i=n.length,o=n[i-1];i-- >0;)n[i]=o,o=t[o];return n}const sl=e=>e.__isTeleport,he=Symbol(void 0),mt=Symbol(void 0),_e=Symbol(void 0),At=Symbol(void 0),Rt=[];let Re=null;function Xr(e=!1){Rt.push(Re=e?null:[])}function rl(){Rt.pop(),Re=Rt[Rt.length-1]||null}let Pt=1;function Ns(e){Pt+=e}function Zr(e){return e.dynamicChildren=Pt>0?Re||ft:null,rl(),Pt>0&&Re&&Re.push(e),e}function Fc(e,t,n,s,r,i){return Zr(ei(e,t,n,s,r,i,!0))}function Qr(e,t,n,s,r){return Zr(le(e,t,n,s,r,!0))}function nn(e){return e?e.__v_isVNode===!0:!1}function et(e,t){return e.type===t.type&&e.key===t.key}const _n="__vInternal",Gr=({key:e})=>e??null,Yt=({ref:e,ref_key:t,ref_for:n})=>e!=null?re(e)||oe(e)||j(e)?{i:ue,r:e,k:t,f:!!n}:e:null;function ei(e,t=null,n=null,s=0,r=null,i=e===he?0:1,o=!1,l=!1){const f={__v_isVNode:!0,__v_skip:!0,type:e,props:t,key:t&&Gr(t),ref:t&&Yt(t),scopeId:an,slotScopeIds:null,children:n,component:null,suspense:null,ssContent:null,ssFallback:null,dirs:null,transition:null,el:null,anchor:null,target:null,targetAnchor:null,staticCount:0,shapeFlag:i,patchFlag:s,dynamicProps:r,dynamicChildren:null,appContext:null,ctx:ue};return l?(us(f,n),i&128&&e.normalize(f)):n&&(f.shapeFlag|=re(n)?8:16),Pt>0&&!o&&Re&&(f.patchFlag>0||i&6)&&f.patchFlag!==32&&Re.push(f),f}const le=il;function il(e,t=null,n=null,s=0,r=null,i=!1){if((!e||e===jr)&&(e=_e),nn(e)){const l=Ve(e,t,!0);return n&&us(l,n),Pt>0&&!i&&Re&&(l.shapeFlag&6?Re[Re.indexOf(e)]=l:Re.push(l)),l.patchFlag|=-2,l}if(gl(e)&&(e=e.__vccOpts),t){t=ol(t);let{class:l,style:f}=t;l&&!re(l)&&(t.class=Wn(l)),ee(f)&&(Cr(f)&&!N(f)&&(f=ce({},f)),t.style=kn(f))}const o=re(e)?1:vo(e)?128:sl(e)?64:ee(e)?4:j(e)?2:0;return ei(e,t,n,s,r,o,i,!0)}function ol(e){return e?Cr(e)||_n in e?ce({},e):e:null}function Ve(e,t,n=!1){const{props:s,ref:r,patchFlag:i,children:o}=e,l=t?ll(s||{},t):s;return{__v_isVNode:!0,__v_skip:!0,type:e.type,props:l,key:l&&Gr(l),ref:t&&t.ref?n&&r?N(r)?r.concat(Yt(t)):[r,Yt(t)]:Yt(t):r,scopeId:e.scopeId,slotScopeIds:e.slotScopeIds,children:o,target:e.target,targetAnchor:e.targetAnchor,staticCount:e.staticCount,shapeFlag:e.shapeFlag,patchFlag:t&&e.type!==he?i===-1?16:i|16:i,dynamicProps:e.dynamicProps,dynamicChildren:e.dynamicChildren,appContext:e.appContext,dirs:e.dirs,transition:e.transition,component:e.component,suspense:e.suspense,ssContent:e.ssContent&&Ve(e.ssContent),ssFallback:e.ssFallback&&Ve(e.ssFallback),el:e.el,anchor:e.anchor,ctx:e.ctx}}function ti(e=" ",t=0){return le(mt,null,e,t)}function Pc(e,t){const n=le(At,null,e);return n.staticCount=t,n}function Sc(e="",t=!1){return t?(Xr(),Qr(_e,null,e)):le(_e,null,e)}function Te(e){return e==null||typeof e=="boolean"?le(_e):N(e)?le(he,null,e.slice()):typeof e=="object"?Ke(e):le(mt,null,String(e))}function Ke(e){return e.el===null&&e.patchFlag!==-1||e.memo?e:Ve(e)}function us(e,t){let n=0;const{shapeFlag:s}=e;if(t==null)t=null;else if(N(t))n=16;else if(typeof t=="object")if(s&65){const r=t.default;r&&(r._c&&(r._d=!1),us(e,r()),r._c&&(r._d=!0));return}else{n=32;const r=t._;!r&&!(_n in t)?t._ctx=ue:r===3&&ue&&(ue.slots._===1?t._=1:(t._=2,e.patchFlag|=1024))}else j(t)?(t={default:t,_ctx:ue},n=32):(t=String(t),s&64?(n=16,t=[ti(t)]):n=8);e.children=t,e.shapeFlag|=n}function ll(...e){const t={};for(let n=0;nie||ue,_t=e=>{ie=e,e.scope.on()},rt=()=>{ie&&ie.scope.off(),ie=null};function si(e){return e.vnode.shapeFlag&4}let St=!1;function al(e,t=!1){St=t;const{props:n,children:s}=e.vnode,r=si(e);Vo(e,n,r,t),Jo(e,s);const i=r?dl(e,t):void 0;return St=!1,i}function dl(e,t){const n=e.type;e.accessCache=Object.create(null),e.proxy=vt(new Proxy(e.ctx,Bo));const{setup:s}=n;if(s){const r=e.setupContext=s.length>1?ii(e):null;_t(e),bt();const i=We(s,e,0,[e.props,r]);if(yt(),rt(),lr(i)){if(i.then(rt,rt),t)return i.then(o=>{Hs(e,o,t)}).catch(o=>{fn(o,e,0)});e.asyncDep=i}else Hs(e,i,t)}else ri(e,t)}function Hs(e,t,n){j(t)?e.type.__ssrInlineRender?e.ssrRender=t:e.render=t:ee(t)&&(e.setupState=vr(t)),ri(e,n)}let $s;function ri(e,t,n){const s=e.type;if(!e.render){if(!t&&$s&&!s.render){const r=s.template||cs(e).template;if(r){const{isCustomElement:i,compilerOptions:o}=e.appContext.config,{delimiters:l,compilerOptions:f}=s,a=ce(ce({isCustomElement:i,delimiters:l},o),f);s.render=$s(r,a)}}e.render=s.render||Ie}_t(e),bt(),Do(e),yt(),rt()}function hl(e){return new Proxy(e.attrs,{get(t,n){return be(e,"get","$attrs"),t[n]}})}function ii(e){const t=s=>{e.exposed=s||{}};let n;return{get attrs(){return n||(n=hl(e))},slots:e.slots,emit:e.emit,expose:t}}function as(e){if(e.exposed)return e.exposeProxy||(e.exposeProxy=new Proxy(vr(vt(e.exposed)),{get(t,n){if(n in t)return t[n];if(n in Tt)return Tt[n](e)},has(t,n){return n in t||n in Tt}}))}function pl(e,t=!0){return j(e)?e.displayName||e.name:e.name||t&&e.__name}function gl(e){return j(e)&&"__vccOpts"in e}const ve=(e,t)=>ao(e,t,St);function Lc(){return ml().slots}function ml(){const e=ni();return e.setupContext||(e.setupContext=ii(e))}function Bn(e,t,n){const s=arguments.length;return s===2?ee(t)&&!N(t)?nn(t)?le(e,null,[t]):le(e,t):le(e,null,t):(s>3?n=Array.prototype.slice.call(arguments,2):s===3&&nn(n)&&(n=[n]),le(e,t,n))}const _l=Symbol(""),bl=()=>ht(_l),yl="3.2.45",wl="http://www.w3.org/2000/svg",tt=typeof document<"u"?document:null,Us=tt&&tt.createElement("template"),Cl={insert:(e,t,n)=>{t.insertBefore(e,n||null)},remove:e=>{const t=e.parentNode;t&&t.removeChild(e)},createElement:(e,t,n,s)=>{const r=t?tt.createElementNS(wl,e):tt.createElement(e,n?{is:n}:void 0);return e==="select"&&s&&s.multiple!=null&&r.setAttribute("multiple",s.multiple),r},createText:e=>tt.createTextNode(e),createComment:e=>tt.createComment(e),setText:(e,t)=>{e.nodeValue=t},setElementText:(e,t)=>{e.textContent=t},parentNode:e=>e.parentNode,nextSibling:e=>e.nextSibling,querySelector:e=>tt.querySelector(e),setScopeId(e,t){e.setAttribute(t,"")},insertStaticContent(e,t,n,s,r,i){const o=n?n.previousSibling:t.lastChild;if(r&&(r===i||r.nextSibling))for(;t.insertBefore(r.cloneNode(!0),n),!(r===i||!(r=r.nextSibling)););else{Us.innerHTML=s?`${e}`:e;const l=Us.content;if(s){const f=l.firstChild;for(;f.firstChild;)l.appendChild(f.firstChild);l.removeChild(f)}t.insertBefore(l,n)}return[o?o.nextSibling:t.firstChild,n?n.previousSibling:t.lastChild]}};function xl(e,t,n){const s=e._vtc;s&&(t=(t?[t,...s]:[...s]).join(" ")),t==null?e.removeAttribute("class"):n?e.setAttribute("class",t):e.className=t}function El(e,t,n){const s=e.style,r=re(n);if(n&&!r){for(const i in n)Dn(s,i,n[i]);if(t&&!re(t))for(const i in t)n[i]==null&&Dn(s,i,"")}else{const i=s.display;r?t!==n&&(s.cssText=n):t&&e.removeAttribute("style"),"_vod"in e&&(s.display=i)}}const js=/\s*!important$/;function Dn(e,t,n){if(N(n))n.forEach(s=>Dn(e,t,s));else if(n==null&&(n=""),t.startsWith("--"))e.setProperty(t,n);else{const s=vl(e,t);js.test(n)?e.setProperty(it(s),n.replace(js,""),"important"):e[s]=n}}const Bs=["Webkit","Moz","ms"],vn={};function vl(e,t){const n=vn[t];if(n)return n;let s=Se(t);if(s!=="filter"&&s in e)return vn[t]=s;s=on(s);for(let r=0;rTn||(Fl.then(()=>Tn=0),Tn=Date.now());function Sl(e,t){const n=s=>{if(!s._vts)s._vts=Date.now();else if(s._vts<=n.attached)return;xe(Ll(s,n.value),t,5,[s])};return n.value=e,n.attached=Pl(),n}function Ll(e,t){if(N(t)){const n=e.stopImmediatePropagation;return e.stopImmediatePropagation=()=>{n.call(e),e._stopped=!0},t.map(s=>r=>!r._stopped&&s&&s(r))}else return t}const ks=/^on[a-z]/,Nl=(e,t,n,s,r=!1,i,o,l,f)=>{t==="class"?xl(e,s,r):t==="style"?El(e,n,s):Lt(t)?qn(t)||Ol(e,t,n,s,o):(t[0]==="."?(t=t.slice(1),!0):t[0]==="^"?(t=t.slice(1),!1):Hl(e,t,s,r))?Al(e,t,s,i,o,l,f):(t==="true-value"?e._trueValue=s:t==="false-value"&&(e._falseValue=s),Tl(e,t,s,r))};function Hl(e,t,n,s){return s?!!(t==="innerHTML"||t==="textContent"||t in e&&ks.test(t)&&j(n)):t==="spellcheck"||t==="draggable"||t==="translate"||t==="form"||t==="list"&&e.tagName==="INPUT"||t==="type"&&e.tagName==="TEXTAREA"||ks.test(t)&&re(n)?!1:t in e}const Be="transition",Ct="animation",oi=(e,{slots:t})=>Bn(Sr,$l(e),t);oi.displayName="Transition";const li={name:String,type:String,css:{type:Boolean,default:!0},duration:[String,Number,Object],enterFromClass:String,enterActiveClass:String,enterToClass:String,appearFromClass:String,appearActiveClass:String,appearToClass:String,leaveFromClass:String,leaveActiveClass:String,leaveToClass:String};oi.props=ce({},Sr.props,li);const Xe=(e,t=[])=>{N(e)?e.forEach(n=>n(...t)):e&&e(...t)},Ws=e=>e?N(e)?e.some(t=>t.length>1):e.length>1:!1;function $l(e){const t={};for(const I in e)I in li||(t[I]=e[I]);if(e.css===!1)return t;const{name:n="v",type:s,duration:r,enterFromClass:i=`${n}-enter-from`,enterActiveClass:o=`${n}-enter-active`,enterToClass:l=`${n}-enter-to`,appearFromClass:f=i,appearActiveClass:a=o,appearToClass:d=l,leaveFromClass:p=`${n}-leave-from`,leaveActiveClass:y=`${n}-leave-active`,leaveToClass:R=`${n}-leave-to`}=e,L=Ul(r),M=L&&L[0],W=L&&L[1],{onBeforeEnter:_,onEnter:E,onEnterCancelled:A,onLeave:$,onLeaveCancelled:D,onBeforeAppear:Z=_,onAppear:q=E,onAppearCancelled:T=A}=t,B=(I,K,F)=>{Ze(I,K?d:l),Ze(I,K?a:o),F&&F()},U=(I,K)=>{I._isLeaving=!1,Ze(I,p),Ze(I,R),Ze(I,y),K&&K()},z=I=>(K,F)=>{const pe=I?q:E,J=()=>B(K,I,F);Xe(pe,[K,J]),qs(()=>{Ze(K,I?f:i),De(K,I?d:l),Ws(pe)||Vs(K,s,M,J)})};return ce(t,{onBeforeEnter(I){Xe(_,[I]),De(I,i),De(I,o)},onBeforeAppear(I){Xe(Z,[I]),De(I,f),De(I,a)},onEnter:z(!1),onAppear:z(!0),onLeave(I,K){I._isLeaving=!0;const F=()=>U(I,K);De(I,p),Dl(),De(I,y),qs(()=>{I._isLeaving&&(Ze(I,p),De(I,R),Ws($)||Vs(I,s,W,F))}),Xe($,[I,F])},onEnterCancelled(I){B(I,!1),Xe(A,[I])},onAppearCancelled(I){B(I,!0),Xe(T,[I])},onLeaveCancelled(I){U(I),Xe(D,[I])}})}function Ul(e){if(e==null)return null;if(ee(e))return[An(e.enter),An(e.leave)];{const t=An(e);return[t,t]}}function An(e){return Jn(e)}function De(e,t){t.split(/\s+/).forEach(n=>n&&e.classList.add(n)),(e._vtc||(e._vtc=new Set)).add(t)}function Ze(e,t){t.split(/\s+/).forEach(s=>s&&e.classList.remove(s));const{_vtc:n}=e;n&&(n.delete(t),n.size||(e._vtc=void 0))}function qs(e){requestAnimationFrame(()=>{requestAnimationFrame(e)})}let jl=0;function Vs(e,t,n,s){const r=e._endId=++jl,i=()=>{r===e._endId&&s()};if(n)return setTimeout(i,n);const{type:o,timeout:l,propCount:f}=Bl(e,t);if(!o)return s();const a=o+"end";let d=0;const p=()=>{e.removeEventListener(a,y),i()},y=R=>{R.target===e&&++d>=f&&p()};setTimeout(()=>{d(n[L]||"").split(", "),r=s(`${Be}Delay`),i=s(`${Be}Duration`),o=zs(r,i),l=s(`${Ct}Delay`),f=s(`${Ct}Duration`),a=zs(l,f);let d=null,p=0,y=0;t===Be?o>0&&(d=Be,p=o,y=i.length):t===Ct?a>0&&(d=Ct,p=a,y=f.length):(p=Math.max(o,a),d=p>0?o>a?Be:Ct:null,y=d?d===Be?i.length:f.length:0);const R=d===Be&&/\b(transform|all)(,|$)/.test(s(`${Be}Property`).toString());return{type:d,timeout:p,propCount:y,hasTransform:R}}function zs(e,t){for(;e.lengthYs(n)+Ys(e[s])))}function Ys(e){return Number(e.slice(0,-1).replace(",","."))*1e3}function Dl(){return document.body.offsetHeight}const Kl=["ctrl","shift","alt","meta"],kl={stop:e=>e.stopPropagation(),prevent:e=>e.preventDefault(),self:e=>e.target!==e.currentTarget,ctrl:e=>!e.ctrlKey,shift:e=>!e.shiftKey,alt:e=>!e.altKey,meta:e=>!e.metaKey,left:e=>"button"in e&&e.button!==0,middle:e=>"button"in e&&e.button!==1,right:e=>"button"in e&&e.button!==2,exact:(e,t)=>Kl.some(n=>e[`${n}Key`]&&!t.includes(n))},Nc=(e,t)=>(n,...s)=>{for(let r=0;rn=>{if(!("key"in n))return;const s=it(n.key);if(t.some(r=>r===s||Wl[r]===s))return e(n)},ql=ce({patchProp:Nl},Cl);let Rn,Js=!1;function Vl(){return Rn=Js?Rn:el(ql),Js=!0,Rn}const $c=(...e)=>{const t=Vl().createApp(...e),{mount:n}=t;return t.mount=s=>{const r=zl(s);if(r)return n(r,!0,r instanceof SVGElement)},t};function zl(e){return re(e)?document.querySelector(e):e}const Uc=(e,t)=>{const n=e.__vccOpts||e;for(const[s,r]of t)n[s]=r;return n},Yl="modulepreload",Jl=function(e){return"/"+e},Xs={},jc=function(t,n,s){if(!n||n.length===0)return t();const r=document.getElementsByTagName("link");return Promise.all(n.map(i=>{if(i=Jl(i),i in Xs)return;Xs[i]=!0;const o=i.endsWith(".css"),l=o?'[rel="stylesheet"]':"";if(!!s)for(let d=r.length-1;d>=0;d--){const p=r[d];if(p.href===i&&(!o||p.rel==="stylesheet"))return}else if(document.querySelector(`link[href="${i}"]${l}`))return;const a=document.createElement("link");if(a.rel=o?"stylesheet":Yl,o||(a.as="script",a.crossOrigin=""),a.href=i,document.head.appendChild(a),o)return new Promise((d,p)=>{a.addEventListener("load",d),a.addEventListener("error",()=>p(new Error(`Unable to preload CSS for ${i}`)))})})).then(()=>t())},Xl=window.__VP_SITE_DATA__,ci=/^[a-z]+:/i,Bc=/^pathname:\/\//,Dc="vitepress-theme-appearance",fi=/#.*$/,Zl=/(index)?\.(md|html)$/,Ce=typeof document<"u",ui={relativePath:"",filePath:"",title:"404",description:"Not Found",headers:[],frontmatter:{sidebar:!1,layout:"page"},lastUpdated:0,isNotFound:!0};function Ql(e,t,n=!1){if(t===void 0)return!1;if(e=Zs(`/${e}`),n)return new RegExp(t).test(e);if(Zs(t)!==e)return!1;const s=t.match(fi);return s?(Ce?location.hash:"")===s[0]:!0}function Zs(e){return decodeURI(e).replace(fi,"").replace(Zl,"")}function Gl(e){return ci.test(e)}function ec(e,t){var s,r,i,o,l,f,a;const n=Object.keys(e.locales).find(d=>d!=="root"&&!Gl(d)&&Ql(t,`/${d}/`,!0))||"root";return Object.assign({},e,{localeIndex:n,lang:((s=e.locales[n])==null?void 0:s.lang)??e.lang,dir:((r=e.locales[n])==null?void 0:r.dir)??e.dir,title:((i=e.locales[n])==null?void 0:i.title)??e.title,titleTemplate:((o=e.locales[n])==null?void 0:o.titleTemplate)??e.titleTemplate,description:((l=e.locales[n])==null?void 0:l.description)??e.description,head:di(e.head,((f=e.locales[n])==null?void 0:f.head)??[]),themeConfig:{...e.themeConfig,...(a=e.locales[n])==null?void 0:a.themeConfig}})}function ai(e,t){const n=t.title||e.title,s=t.titleTemplate??e.titleTemplate;if(typeof s=="string"&&s.includes(":title"))return s.replace(/:title/g,n);const r=tc(e.title,s);return`${n}${r}`}function tc(e,t){return t===!1?"":t===!0||t===void 0?` | ${e}`:e===t?"":` | ${t}`}function nc(e,t){const[n,s]=t;if(n!=="meta")return!1;const r=Object.entries(s)[0];return r==null?!1:e.some(([i,o])=>i===n&&o[r[0]]===r[1])}function di(e,t){return[...e.filter(n=>!nc(t,n)),...t]}const sc=/[\u0000-\u001F"#$&*+,:;<=>?[\]^`{|}\u007F]/g,rc=/^[a-z]:/i;function Qs(e){const t=rc.exec(e),n=t?t[0]:"";return n+e.slice(n.length).replace(sc,"_").replace(/(^|\/)_+(?=[^/]*$)/,"$1")}const ic=Symbol(),nt=ro(Xl);function Kc(e){const t=ve(()=>ec(nt.value,e.data.relativePath));return{site:t,theme:ve(()=>t.value.themeConfig),page:ve(()=>e.data),frontmatter:ve(()=>e.data.frontmatter),params:ve(()=>e.data.params),lang:ve(()=>t.value.lang),dir:ve(()=>t.value.dir),localeIndex:ve(()=>t.value.localeIndex||"root"),title:ve(()=>ai(t.value,e.data)),description:ve(()=>e.data.description||t.value.description),isDark:xr(!1)}}function kc(){const e=ht(ic);if(!e)throw new Error("vitepress data not properly injected in app");return e}function oc(e,t){return`${e}${t}`.replace(/\/+/g,"/")}function Gs(e){return ci.test(e)||e.startsWith(".")?e:oc(nt.value.base,e)}function lc(e){let t=e.replace(/\.html$/,"");if(t=decodeURIComponent(t),t=t.replace(/\/$/,"/index"),Ce){const n="/";t=Qs(t.slice(n.length).replace(/\//g,"_")||"index")+".md";let s=__VP_HASH_MAP__[t.toLowerCase()];s||(t=t.endsWith("_index.md")?t.slice(0,-9)+".md":t.slice(0,-3)+"_index.md",s=__VP_HASH_MAP__[t.toLowerCase()]),t=`${n}assets/${t}.${s}.js`}else t=`./${Qs(t.slice(1).replace(/\//g,"_"))}.md.js`;return t}let Jt=[];function Wc(e){Jt.push(e),mn(()=>{Jt=Jt.filter(t=>t!==e)})}const cc=Symbol(),er="http://a.com",fc=()=>({path:"/",component:null,data:ui});function qc(e,t){const n=cn(fc()),s={route:n,go:r};async function r(l=Ce?location.href:"/"){var a,d;await((a=s.onBeforeRouteChange)==null?void 0:a.call(s,l));const f=new URL(l,er);nt.value.cleanUrls||!f.pathname.endsWith("/")&&!f.pathname.endsWith(".html")&&(f.pathname+=".html",l=f.pathname+f.search+f.hash),Ce&&l!==location.href&&(history.replaceState({scrollPosition:window.scrollY},document.title),history.pushState(null,"",l)),await o(l),await((d=s.onAfterRouteChanged)==null?void 0:d.call(s,l))}let i=null;async function o(l,f=0,a=!1){const d=new URL(l,er),p=i=d.pathname;try{let y=await e(p);if(i===p){i=null;const{default:R,__pageData:L}=y;if(!R)throw new Error(`Invalid route component: ${R}`);n.path=Ce?p:Gs(p),n.component=vt(R),n.data=vt(L),Ce&&Rr(()=>{let M=nt.value.base+L.relativePath.replace(/(?:(^|\/)index)?\.md$/,"$1");if(!nt.value.cleanUrls&&!M.endsWith("/")&&(M+=".html"),M!==d.pathname&&(d.pathname=M,l=M+d.search+d.hash,history.replaceState(null,"",l)),d.hash&&!f){let W=null;try{W=document.querySelector(decodeURIComponent(d.hash))}catch(_){console.warn(_)}if(W){tr(W,d.hash);return}}window.scrollTo(0,f)})}}catch(y){if(!/fetch/.test(y.message)&&!/^\/404(\.html|\/)?$/.test(l)&&console.error(y),!a)try{const R=await fetch(nt.value.base+"hashmap.json");window.__VP_HASH_MAP__=await R.json(),await o(l,f,!0);return}catch{}i===p&&(i=null,n.path=Ce?p:Gs(p),n.component=t?vt(t):null,n.data=ui)}}return Ce&&(window.addEventListener("click",l=>{if(l.target.closest("button"))return;const a=l.target.closest("a");if(a&&!a.closest(".vp-raw")&&(a instanceof SVGElement||!a.download)){const{target:d}=a,{href:p,origin:y,pathname:R,hash:L,search:M}=new URL(a.href instanceof SVGAnimatedString?a.href.animVal:a.href,a.baseURI),W=window.location,_=R.match(/\.\w+$/);!l.ctrlKey&&!l.shiftKey&&!l.altKey&&!l.metaKey&&d!=="_blank"&&y===W.origin&&!(_&&_[0]!==".html")&&(l.preventDefault(),R===W.pathname&&M===W.search?L&&(L!==W.hash&&(history.pushState(null,"",L),window.dispatchEvent(new Event("hashchange"))),tr(a,L,a.classList.contains("header-anchor"))):r(p))}},{capture:!0}),window.addEventListener("popstate",l=>{o(location.href,l.state&&l.state.scrollPosition||0)}),window.addEventListener("hashchange",l=>{l.preventDefault()})),s}function uc(){const e=ht(cc);if(!e)throw new Error("useRouter() is called without provider.");return e}function hi(){return uc().route}function tr(e,t,n=!1){let s=null;try{s=e.classList.contains("header-anchor")?e:document.querySelector(decodeURIComponent(t))}catch(r){console.warn(r)}if(s){const r=nt.value.scrollOffset;let i=0;if(typeof r=="number")i=r;else if(typeof r=="string")i=nr(r);else if(Array.isArray(r))for(const f of r){const a=nr(f);if(a){i=a;break}}const o=parseInt(window.getComputedStyle(s).paddingTop,10),l=window.scrollY+s.getBoundingClientRect().top-i+o;!n||Math.abs(l-window.scrollY)>window.innerHeight?window.scrollTo(0,l):window.scrollTo({left:0,top:l,behavior:"smooth"})}}function nr(e){const t=document.querySelector(e);if(!t)return 0;const n=t.getBoundingClientRect().bottom;return n<0?0:n+24}const sr=()=>Jt.forEach(e=>e()),Vc=Hr({name:"VitePressContent",props:{as:{type:[Object,String],default:"div"}},setup(e){const t=hi();return()=>Bn(e.as,{style:{position:"relative"}},[t.component?Bn(t.component,{onVnodeMounted:sr,onVnodeUpdated:sr}):"404 Page Not Found"])}});function zc(e,t){let n=[],s=!0;const r=i=>{if(s){s=!1;return}n.forEach(o=>document.head.removeChild(o)),n=[],i.forEach(o=>{const l=ac(o);document.head.appendChild(l),n.push(l)})};Ao(()=>{const i=e.data,o=t.value,l=i&&i.description,f=i&&i.frontmatter.head||[];document.title=ai(o,i),document.querySelector("meta[name=description]").setAttribute("content",l||o.description),r(di(o.head,hc(f)))})}function ac([e,t,n]){const s=document.createElement(e);for(const r in t)s.setAttribute(r,t[r]);return n&&(s.innerHTML=n),s}function dc(e){return e[0]==="meta"&&e[1]&&e[1].name==="description"}function hc(e){return e.filter(t=>!dc(t))}const In=new Set,pi=()=>document.createElement("link"),pc=e=>{const t=pi();t.rel="prefetch",t.href=e,document.head.appendChild(t)},gc=e=>{const t=new XMLHttpRequest;t.open("GET",e,t.withCredentials=!0),t.send()};let qt;const mc=Ce&&(qt=pi())&&qt.relList&&qt.relList.supports&&qt.relList.supports("prefetch")?pc:gc;function Yc(){if(!Ce||!window.IntersectionObserver)return;let e;if((e=navigator.connection)&&(e.saveData||/2g/.test(e.effectiveType)))return;const t=window.requestIdleCallback||setTimeout;let n=null;const s=()=>{n&&n.disconnect(),n=new IntersectionObserver(i=>{i.forEach(o=>{if(o.isIntersecting){const l=o.target;n.unobserve(l);const{pathname:f}=l;if(!In.has(f)){In.add(f);const a=lc(f);mc(a)}}})}),t(()=>{document.querySelectorAll("#app a").forEach(i=>{const{target:o}=i,{hostname:l,pathname:f}=new URL(i.href instanceof SVGAnimatedString?i.href.animVal:i.href,i.baseURI),a=f.match(/\.\w+$/);a&&a[0]!==".html"||o!=="_blank"&&l===location.hostname&&(f!==location.pathname?n.observe(i):In.add(f))})})};gn(s);const r=hi();zt(()=>r.path,s),mn(()=>{n&&n.disconnect()})}const Jc=Hr({setup(e,{slots:t}){const n=xr(!1);return gn(()=>{n.value=!0}),()=>n.value&&t.default?t.default():null}});function Xc(){if(Ce){const e=new Map;window.addEventListener("click",t=>{var s;const n=t.target;if(n.matches('div[class*="language-"] > button.copy')){const r=n.parentElement,i=(s=n.nextElementSibling)==null?void 0:s.nextElementSibling;if(!r||!i)return;const o=/language-(shellscript|shell|bash|sh|zsh)/.test(r.className);let l="";i.querySelectorAll("span.line:not(.diff.remove)").forEach(f=>l+=(f.textContent||"")+` -`),l=l.slice(0,-1),o&&(l=l.replace(/^ *(\$|>) /gm,"").trim()),_c(l).then(()=>{n.classList.add("copied"),clearTimeout(e.get(n));const f=setTimeout(()=>{n.classList.remove("copied"),n.blur(),e.delete(n)},2e3);e.set(n,f)})}})}}async function _c(e){try{return navigator.clipboard.writeText(e)}catch{const t=document.createElement("textarea"),n=document.activeElement;t.value=e,t.setAttribute("readonly",""),t.style.contain="strict",t.style.position="absolute",t.style.left="-9999px",t.style.fontSize="12pt";const s=document.getSelection(),r=s?s.rangeCount>0&&s.getRangeAt(0):null;document.body.appendChild(t),t.select(),t.selectionStart=0,t.selectionEnd=e.length,document.execCommand("copy"),document.body.removeChild(t),r&&(s.removeAllRanges(),s.addRange(r)),n&&n.focus()}}function Zc(){Ce&&window.addEventListener("click",e=>{var n,s;const t=e.target;if(t.matches(".vp-code-group input")){const r=(n=t.parentElement)==null?void 0:n.parentElement,i=Array.from((r==null?void 0:r.querySelectorAll("input"))||[]).indexOf(t),o=r==null?void 0:r.querySelector('div[class*="language-"].active'),l=(s=r==null?void 0:r.querySelectorAll('div[class*="language-"]:not(.language-id)'))==null?void 0:s[i];o&&l&&o!==l&&(o.classList.remove("active"),l.classList.add("active"))}})}export{Tc as $,Qr as A,yo as B,Sc as C,ll as D,le as E,he as F,Ac as G,kn as H,Rc as I,ci as J,Ce as K,Ic as L,Ec as M,vc as N,Pc as O,Bc as P,Dc as Q,ht as R,To as S,oi as T,No as U,Wc as V,ro as W,Rr as X,Hc as Y,Mc as Z,Uc as _,ti as a,Nc as a0,Lc as a1,zc as a2,Yc as a3,Xc as a4,Zc as a5,Bn as a6,cc as a7,Kc as a8,ic as a9,Vc as aa,Jc as ab,nt as ac,$c as ad,qc as ae,lc as af,jc as ag,oo as b,Fc as c,Hr as d,xc as e,wr as f,Cc as g,xr as h,yc as i,wc as j,Ao as k,ve as l,ni as m,Wn as n,Xr as o,gn as p,Gl as q,Oc as r,Gs as s,bc as t,kc as u,Ql as v,zt as w,hi as x,mn as y,ei as z}; diff --git a/assets/chunks/framework.ec8f7e8e.js b/assets/chunks/framework.ec8f7e8e.js new file mode 100644 index 000000000000..7b458a4102f2 --- /dev/null +++ b/assets/chunks/framework.ec8f7e8e.js @@ -0,0 +1,2 @@ +function ls(e,t){const n=Object.create(null),s=e.split(",");for(let r=0;r!!n[r.toLowerCase()]:r=>!!n[r]}const te={},pt=[],Re=()=>{},Qi=()=>!1,Zi=/^on[^a-z]/,Ut=e=>Zi.test(e),cs=e=>e.startsWith("onUpdate:"),oe=Object.assign,as=(e,t)=>{const n=e.indexOf(t);n>-1&&e.splice(n,1)},Gi=Object.prototype.hasOwnProperty,X=(e,t)=>Gi.call(e,t),D=Array.isArray,gt=e=>bn(e)==="[object Map]",Pr=e=>bn(e)==="[object Set]",K=e=>typeof e=="function",ne=e=>typeof e=="string",_n=e=>typeof e=="symbol",ee=e=>e!==null&&typeof e=="object",Ir=e=>(ee(e)||K(e))&&K(e.then)&&K(e.catch),Mr=Object.prototype.toString,bn=e=>Mr.call(e),eo=e=>bn(e).slice(8,-1),Fr=e=>bn(e)==="[object Object]",us=e=>ne(e)&&e!=="NaN"&&e[0]!=="-"&&""+parseInt(e,10)===e,It=ls(",key,ref,ref_for,ref_key,onVnodeBeforeMount,onVnodeMounted,onVnodeBeforeUpdate,onVnodeUpdated,onVnodeBeforeUnmount,onVnodeUnmounted"),vn=e=>{const t=Object.create(null);return n=>t[n]||(t[n]=e(n))},to=/-(\w)/g,Le=vn(e=>e.replace(to,(t,n)=>n?n.toUpperCase():"")),no=/\B([A-Z])/g,at=vn(e=>e.replace(no,"-$1").toLowerCase()),wn=vn(e=>e.charAt(0).toUpperCase()+e.slice(1)),rn=vn(e=>e?`on${wn(e)}`:""),ct=(e,t)=>!Object.is(e,t),$n=(e,t)=>{for(let n=0;n{Object.defineProperty(e,t,{configurable:!0,enumerable:!1,value:n})},so=e=>{const t=parseFloat(e);return isNaN(t)?e:t},ro=e=>{const t=ne(e)?Number(e):NaN;return isNaN(t)?e:t};let Hs;const zn=()=>Hs||(Hs=typeof globalThis<"u"?globalThis:typeof self<"u"?self:typeof window<"u"?window:typeof global<"u"?global:{});function fs(e){if(D(e)){const t={};for(let n=0;n{if(n){const s=n.split(oo);s.length>1&&(t[s[0].trim()]=s[1].trim())}}),t}function ds(e){let t="";if(ne(e))t=e;else if(D(e))for(let n=0;nne(e)?e:e==null?"":D(e)||ee(e)&&(e.toString===Mr||!K(e.toString))?JSON.stringify(e,Nr,2):String(e),Nr=(e,t)=>t&&t.__v_isRef?Nr(e,t.value):gt(t)?{[`Map(${t.size})`]:[...t.entries()].reduce((n,[s,r])=>(n[`${s} =>`]=r,n),{})}:Pr(t)?{[`Set(${t.size})`]:[...t.values()]}:ee(t)&&!D(t)&&!Fr(t)?String(t):t;let be;class fo{constructor(t=!1){this.detached=t,this._active=!0,this.effects=[],this.cleanups=[],this.parent=be,!t&&be&&(this.index=(be.scopes||(be.scopes=[])).push(this)-1)}get active(){return this._active}run(t){if(this._active){const n=be;try{return be=this,t()}finally{be=n}}}on(){be=this}off(){be=this.parent}stop(t){if(this._active){let n,s;for(n=0,s=this.effects.length;n{const t=new Set(e);return t.w=0,t.n=0,t},$r=e=>(e.w&Xe)>0,jr=e=>(e.n&Xe)>0,go=({deps:e})=>{if(e.length)for(let t=0;t{const{deps:t}=e;if(t.length){let n=0;for(let s=0;s{(d==="length"||!_n(d)&&d>=c)&&l.push(u)})}else switch(n!==void 0&&l.push(o.get(n)),t){case"add":D(e)?us(n)&&l.push(o.get("length")):(l.push(o.get(it)),gt(e)&&l.push(o.get(Jn)));break;case"delete":D(e)||(l.push(o.get(it)),gt(e)&&l.push(o.get(Jn)));break;case"set":gt(e)&&l.push(o.get(it));break}if(l.length===1)l[0]&&Xn(l[0]);else{const c=[];for(const u of l)u&&c.push(...u);Xn(hs(c))}}function Xn(e,t){const n=D(e)?e:[...e];for(const s of n)s.computed&&js(s);for(const s of n)s.computed||js(s)}function js(e,t){(e!==Se||e.allowRecurse)&&(e.scheduler?e.scheduler():e.run())}function yo(e,t){var n;return(n=an.get(e))==null?void 0:n.get(t)}const _o=ls("__proto__,__v_isRef,__isVue"),kr=new Set(Object.getOwnPropertyNames(Symbol).filter(e=>e!=="arguments"&&e!=="caller").map(e=>Symbol[e]).filter(_n)),Ds=bo();function bo(){const e={};return["includes","indexOf","lastIndexOf"].forEach(t=>{e[t]=function(...n){const s=Q(this);for(let i=0,o=this.length;i{e[t]=function(...n){Et();const s=Q(this)[t].apply(this,n);return Tt(),s}}),e}function vo(e){const t=Q(this);return ye(t,"has",e),t.hasOwnProperty(e)}class Ur{constructor(t=!1,n=!1){this._isReadonly=t,this._shallow=n}get(t,n,s){const r=this._isReadonly,i=this._shallow;if(n==="__v_isReactive")return!r;if(n==="__v_isReadonly")return r;if(n==="__v_isShallow")return i;if(n==="__v_raw"&&s===(r?i?Fo:qr:i?Vr:Wr).get(t))return t;const o=D(t);if(!r){if(o&&X(Ds,n))return Reflect.get(Ds,n,s);if(n==="hasOwnProperty")return vo}const l=Reflect.get(t,n,s);return(_n(n)?kr.has(n):_o(n))||(r||ye(t,"get",n),i)?l:ce(l)?o&&us(n)?l:l.value:ee(l)?r?En(l):Cn(l):l}}class Kr extends Ur{constructor(t=!1){super(!1,t)}set(t,n,s,r){let i=t[n];if(vt(i)&&ce(i)&&!ce(s))return!1;if(!this._shallow&&(!un(s)&&!vt(s)&&(i=Q(i),s=Q(s)),!D(t)&&ce(i)&&!ce(s)))return i.value=s,!0;const o=D(t)&&us(n)?Number(n)e,xn=e=>Reflect.getPrototypeOf(e);function Vt(e,t,n=!1,s=!1){e=e.__v_raw;const r=Q(e),i=Q(t);n||(ct(t,i)&&ye(r,"get",t),ye(r,"get",i));const{has:o}=xn(r),l=s?gs:n?_s:Ht;if(o.call(r,t))return l(e.get(t));if(o.call(r,i))return l(e.get(i));e!==r&&e.get(t)}function qt(e,t=!1){const n=this.__v_raw,s=Q(n),r=Q(e);return t||(ct(e,r)&&ye(s,"has",e),ye(s,"has",r)),e===r?n.has(e):n.has(e)||n.has(r)}function zt(e,t=!1){return e=e.__v_raw,!t&&ye(Q(e),"iterate",it),Reflect.get(e,"size",e)}function Bs(e){e=Q(e);const t=Q(this);return xn(t).has.call(t,e)||(t.add(e),je(t,"add",e,e)),this}function ks(e,t){t=Q(t);const n=Q(this),{has:s,get:r}=xn(n);let i=s.call(n,e);i||(e=Q(e),i=s.call(n,e));const o=r.call(n,e);return n.set(e,t),i?ct(t,o)&&je(n,"set",e,t):je(n,"add",e,t),this}function Us(e){const t=Q(this),{has:n,get:s}=xn(t);let r=n.call(t,e);r||(e=Q(e),r=n.call(t,e)),s&&s.call(t,e);const i=t.delete(e);return r&&je(t,"delete",e,void 0),i}function Ks(){const e=Q(this),t=e.size!==0,n=e.clear();return t&&je(e,"clear",void 0,void 0),n}function Yt(e,t){return function(s,r){const i=this,o=i.__v_raw,l=Q(o),c=t?gs:e?_s:Ht;return!e&&ye(l,"iterate",it),o.forEach((u,d)=>s.call(r,c(u),c(d),i))}}function Jt(e,t,n){return function(...s){const r=this.__v_raw,i=Q(r),o=gt(i),l=e==="entries"||e===Symbol.iterator&&o,c=e==="keys"&&o,u=r[e](...s),d=n?gs:t?_s:Ht;return!t&&ye(i,"iterate",c?Jn:it),{next(){const{value:h,done:b}=u.next();return b?{value:h,done:b}:{value:l?[d(h[0]),d(h[1])]:d(h),done:b}},[Symbol.iterator](){return this}}}}function Be(e){return function(...t){return e==="delete"?!1:this}}function To(){const e={get(i){return Vt(this,i)},get size(){return zt(this)},has:qt,add:Bs,set:ks,delete:Us,clear:Ks,forEach:Yt(!1,!1)},t={get(i){return Vt(this,i,!1,!0)},get size(){return zt(this)},has:qt,add:Bs,set:ks,delete:Us,clear:Ks,forEach:Yt(!1,!0)},n={get(i){return Vt(this,i,!0)},get size(){return zt(this,!0)},has(i){return qt.call(this,i,!0)},add:Be("add"),set:Be("set"),delete:Be("delete"),clear:Be("clear"),forEach:Yt(!0,!1)},s={get(i){return Vt(this,i,!0,!0)},get size(){return zt(this,!0)},has(i){return qt.call(this,i,!0)},add:Be("add"),set:Be("set"),delete:Be("delete"),clear:Be("clear"),forEach:Yt(!0,!0)};return["keys","values","entries",Symbol.iterator].forEach(i=>{e[i]=Jt(i,!1,!1),n[i]=Jt(i,!0,!1),t[i]=Jt(i,!1,!0),s[i]=Jt(i,!0,!0)}),[e,n,t,s]}const[Ao,So,Oo,Ro]=To();function ms(e,t){const n=t?e?Ro:Oo:e?So:Ao;return(s,r,i)=>r==="__v_isReactive"?!e:r==="__v_isReadonly"?e:r==="__v_raw"?s:Reflect.get(X(n,r)&&r in s?n:s,r,i)}const Po={get:ms(!1,!1)},Io={get:ms(!1,!0)},Mo={get:ms(!0,!1)},Wr=new WeakMap,Vr=new WeakMap,qr=new WeakMap,Fo=new WeakMap;function Lo(e){switch(e){case"Object":case"Array":return 1;case"Map":case"Set":case"WeakMap":case"WeakSet":return 2;default:return 0}}function No(e){return e.__v_skip||!Object.isExtensible(e)?0:Lo(eo(e))}function Cn(e){return vt(e)?e:ys(e,!1,xo,Po,Wr)}function Ho(e){return ys(e,!1,Eo,Io,Vr)}function En(e){return ys(e,!0,Co,Mo,qr)}function ys(e,t,n,s,r){if(!ee(e)||e.__v_raw&&!(t&&e.__v_isReactive))return e;const i=r.get(e);if(i)return i;const o=No(e);if(o===0)return e;const l=new Proxy(e,o===2?s:n);return r.set(e,l),l}function mt(e){return vt(e)?mt(e.__v_raw):!!(e&&e.__v_isReactive)}function vt(e){return!!(e&&e.__v_isReadonly)}function un(e){return!!(e&&e.__v_isShallow)}function zr(e){return mt(e)||vt(e)}function Q(e){const t=e&&e.__v_raw;return t?Q(t):e}function Mt(e){return cn(e,"__v_skip",!0),e}const Ht=e=>ee(e)?Cn(e):e,_s=e=>ee(e)?En(e):e;function bs(e){qe&&Se&&(e=Q(e),Br(e.dep||(e.dep=hs())))}function vs(e,t){e=Q(e);const n=e.dep;n&&Xn(n)}function ce(e){return!!(e&&e.__v_isRef===!0)}function me(e){return Jr(e,!1)}function Yr(e){return Jr(e,!0)}function Jr(e,t){return ce(e)?e:new $o(e,t)}class $o{constructor(t,n){this.__v_isShallow=n,this.dep=void 0,this.__v_isRef=!0,this._rawValue=n?t:Q(t),this._value=n?t:Ht(t)}get value(){return bs(this),this._value}set value(t){const n=this.__v_isShallow||un(t)||vt(t);t=n?t:Q(t),ct(t,this._rawValue)&&(this._rawValue=t,this._value=n?t:Ht(t),vs(this))}}function ws(e){return ce(e)?e.value:e}const jo={get:(e,t,n)=>ws(Reflect.get(e,t,n)),set:(e,t,n,s)=>{const r=e[t];return ce(r)&&!ce(n)?(r.value=n,!0):Reflect.set(e,t,n,s)}};function Xr(e){return mt(e)?e:new Proxy(e,jo)}class Do{constructor(t){this.dep=void 0,this.__v_isRef=!0;const{get:n,set:s}=t(()=>bs(this),()=>vs(this));this._get=n,this._set=s}get value(){return this._get()}set value(t){this._set(t)}}function Bo(e){return new Do(e)}class ko{constructor(t,n,s){this._object=t,this._key=n,this._defaultValue=s,this.__v_isRef=!0}get value(){const t=this._object[this._key];return t===void 0?this._defaultValue:t}set value(t){this._object[this._key]=t}get dep(){return yo(Q(this._object),this._key)}}class Uo{constructor(t){this._getter=t,this.__v_isRef=!0,this.__v_isReadonly=!0}get value(){return this._getter()}}function Ko(e,t,n){return ce(e)?e:K(e)?new Uo(e):ee(e)&&arguments.length>1?Wo(e,t,n):me(e)}function Wo(e,t,n){const s=e[t];return ce(s)?s:new ko(e,t,n)}class Vo{constructor(t,n,s,r){this._setter=n,this.dep=void 0,this.__v_isRef=!0,this.__v_isReadonly=!1,this._dirty=!0,this.effect=new ps(t,()=>{this._dirty||(this._dirty=!0,vs(this))}),this.effect.computed=this,this.effect.active=this._cacheable=!r,this.__v_isReadonly=s}get value(){const t=Q(this);return bs(t),(t._dirty||!t._cacheable)&&(t._dirty=!1,t._value=t.effect.run()),t._value}set value(t){this._setter(t)}}function qo(e,t,n=!1){let s,r;const i=K(e);return i?(s=e,r=Re):(s=e.get,r=e.set),new Vo(s,r,i||!r,n)}function ze(e,t,n,s){let r;try{r=s?e(...s):e()}catch(i){Tn(i,t,n)}return r}function Ee(e,t,n,s){if(K(e)){const i=ze(e,t,n,s);return i&&Ir(i)&&i.catch(o=>{Tn(o,t,n)}),i}const r=[];for(let i=0;i>>1,r=fe[s],i=jt(r);iFe&&fe.splice(t,1)}function Xo(e){D(e)?yt.push(...e):(!$e||!$e.includes(e,e.allowRecurse?nt+1:nt))&&yt.push(e),Zr()}function Ws(e,t=$t?Fe+1:0){for(;tjt(n)-jt(s)),nt=0;nt<$e.length;nt++)$e[nt]();$e=null,nt=0}}const jt=e=>e.id==null?1/0:e.id,Qo=(e,t)=>{const n=jt(e)-jt(t);if(n===0){if(e.pre&&!t.pre)return-1;if(t.pre&&!e.pre)return 1}return n};function Gr(e){Qn=!1,$t=!0,fe.sort(Qo);const t=Re;try{for(Fe=0;Fene(E)?E.trim():E)),h&&(r=n.map(so))}let l,c=s[l=rn(t)]||s[l=rn(Le(t))];!c&&i&&(c=s[l=rn(at(t))]),c&&Ee(c,e,6,r);const u=s[l+"Once"];if(u){if(!e.emitted)e.emitted={};else if(e.emitted[l])return;e.emitted[l]=!0,Ee(u,e,6,r)}}function ei(e,t,n=!1){const s=t.emitsCache,r=s.get(e);if(r!==void 0)return r;const i=e.emits;let o={},l=!1;if(!K(e)){const c=u=>{const d=ei(u,t,!0);d&&(l=!0,oe(o,d))};!n&&t.mixins.length&&t.mixins.forEach(c),e.extends&&c(e.extends),e.mixins&&e.mixins.forEach(c)}return!i&&!l?(ee(e)&&s.set(e,null),null):(D(i)?i.forEach(c=>o[c]=null):oe(o,i),ee(e)&&s.set(e,o),o)}function Sn(e,t){return!e||!Ut(t)?!1:(t=t.slice(2).replace(/Once$/,""),X(e,t[0].toLowerCase()+t.slice(1))||X(e,at(t))||X(e,t))}let de=null,On=null;function dn(e){const t=de;return de=e,On=e&&e.type.__scopeId||null,t}function va(e){On=e}function wa(){On=null}function Go(e,t=de,n){if(!t||e._n)return e;const s=(...r)=>{s._d&&nr(-1);const i=dn(t);let o;try{o=e(...r)}finally{dn(i),s._d&&nr(1)}return o};return s._n=!0,s._c=!0,s._d=!0,s}function jn(e){const{type:t,vnode:n,proxy:s,withProxy:r,props:i,propsOptions:[o],slots:l,attrs:c,emit:u,render:d,renderCache:h,data:b,setupState:E,ctx:F,inheritAttrs:R}=e;let j,V;const z=dn(e);try{if(n.shapeFlag&4){const p=r||s;j=Ae(d.call(p,p,h,i,E,b,F)),V=c}else{const p=t;j=Ae(p.length>1?p(i,{attrs:c,slots:l,emit:u}):p(i,null)),V=t.props?c:el(c)}}catch(p){Nt.length=0,Tn(p,e,1),j=ue(ve)}let m=j;if(V&&R!==!1){const p=Object.keys(V),{shapeFlag:M}=m;p.length&&M&7&&(o&&p.some(cs)&&(V=tl(V,o)),m=Qe(m,V))}return n.dirs&&(m=Qe(m),m.dirs=m.dirs?m.dirs.concat(n.dirs):n.dirs),n.transition&&(m.transition=n.transition),j=m,dn(z),j}const el=e=>{let t;for(const n in e)(n==="class"||n==="style"||Ut(n))&&((t||(t={}))[n]=e[n]);return t},tl=(e,t)=>{const n={};for(const s in e)(!cs(s)||!(s.slice(9)in t))&&(n[s]=e[s]);return n};function nl(e,t,n){const{props:s,children:r,component:i}=e,{props:o,children:l,patchFlag:c}=t,u=i.emitsOptions;if(t.dirs||t.transition)return!0;if(n&&c>=0){if(c&1024)return!0;if(c&16)return s?Vs(s,o,u):!!o;if(c&8){const d=t.dynamicProps;for(let h=0;he.__isSuspense;function si(e,t){t&&t.pendingBranch?D(e)?t.effects.push(...e):t.effects.push(e):Xo(e)}function ri(e,t){return Rn(e,null,t)}function Ea(e,t){return Rn(e,null,{flush:"post"})}const Xt={};function Ye(e,t,n){return Rn(e,t,n)}function Rn(e,t,{immediate:n,deep:s,flush:r,onTrack:i,onTrigger:o}=te){var l;const c=Hr()===((l=le)==null?void 0:l.scope)?le:null;let u,d=!1,h=!1;if(ce(e)?(u=()=>e.value,d=un(e)):mt(e)?(u=()=>e,s=!0):D(e)?(h=!0,d=e.some(p=>mt(p)||un(p)),u=()=>e.map(p=>{if(ce(p))return p.value;if(mt(p))return ht(p);if(K(p))return ze(p,c,2)})):K(e)?t?u=()=>ze(e,c,2):u=()=>{if(!(c&&c.isUnmounted))return b&&b(),Ee(e,c,3,[E])}:u=Re,t&&s){const p=u;u=()=>ht(p())}let b,E=p=>{b=z.onStop=()=>{ze(p,c,4)}},F;if(Bt)if(E=Re,t?n&&Ee(t,c,3,[u(),h?[]:void 0,E]):u(),r==="sync"){const p=Zl();F=p.__watcherHandles||(p.__watcherHandles=[])}else return Re;let R=h?new Array(e.length).fill(Xt):Xt;const j=()=>{if(z.active)if(t){const p=z.run();(s||d||(h?p.some((M,U)=>ct(M,R[U])):ct(p,R)))&&(b&&b(),Ee(t,c,3,[p,R===Xt?void 0:h&&R[0]===Xt?[]:R,E]),R=p)}else z.run()};j.allowRecurse=!!t;let V;r==="sync"?V=j:r==="post"?V=()=>pe(j,c&&c.suspense):(j.pre=!0,c&&(j.id=c.uid),V=()=>Cs(j));const z=new ps(u,V);t?n?j():R=z.run():r==="post"?pe(z.run.bind(z),c&&c.suspense):z.run();const m=()=>{z.stop(),c&&c.scope&&as(c.scope.effects,z)};return F&&F.push(m),m}function il(e,t,n){const s=this.proxy,r=ne(e)?e.includes(".")?ii(s,e):()=>s[e]:e.bind(s,s);let i;K(t)?i=t:(i=t.handler,n=t);const o=le;xt(this);const l=Rn(r,i.bind(s),n);return o?xt(o):ot(),l}function ii(e,t){const n=t.split(".");return()=>{let s=e;for(let r=0;r{ht(n,t)});else if(Fr(e))for(const n in e)ht(e[n],t);return e}function Me(e,t,n,s){const r=e.dirs,i=t&&t.dirs;for(let o=0;o{e.isMounted=!0}),fi(()=>{e.isUnmounting=!0}),e}const we=[Function,Array],oi={mode:String,appear:Boolean,persisted:Boolean,onBeforeEnter:we,onEnter:we,onAfterEnter:we,onEnterCancelled:we,onBeforeLeave:we,onLeave:we,onAfterLeave:we,onLeaveCancelled:we,onBeforeAppear:we,onAppear:we,onAfterAppear:we,onAppearCancelled:we},ll={name:"BaseTransition",props:oi,setup(e,{slots:t}){const n=Ln(),s=ol();let r;return()=>{const i=t.default&&ci(t.default(),!0);if(!i||!i.length)return;let o=i[0];if(i.length>1){for(const R of i)if(R.type!==ve){o=R;break}}const l=Q(e),{mode:c}=l;if(s.isLeaving)return Dn(o);const u=zs(o);if(!u)return Dn(o);const d=Zn(u,l,s,n);Gn(u,d);const h=n.subTree,b=h&&zs(h);let E=!1;const{getTransitionKey:F}=u.type;if(F){const R=F();r===void 0?r=R:R!==r&&(r=R,E=!0)}if(b&&b.type!==ve&&(!st(u,b)||E)){const R=Zn(b,l,s,n);if(Gn(b,R),c==="out-in")return s.isLeaving=!0,R.afterLeave=()=>{s.isLeaving=!1,n.update.active!==!1&&n.update()},Dn(o);c==="in-out"&&u.type!==ve&&(R.delayLeave=(j,V,z)=>{const m=li(s,b);m[String(b.key)]=b,j[We]=()=>{V(),j[We]=void 0,delete d.delayedLeave},d.delayedLeave=z})}return o}}},cl=ll;function li(e,t){const{leavingVNodes:n}=e;let s=n.get(t.type);return s||(s=Object.create(null),n.set(t.type,s)),s}function Zn(e,t,n,s){const{appear:r,mode:i,persisted:o=!1,onBeforeEnter:l,onEnter:c,onAfterEnter:u,onEnterCancelled:d,onBeforeLeave:h,onLeave:b,onAfterLeave:E,onLeaveCancelled:F,onBeforeAppear:R,onAppear:j,onAfterAppear:V,onAppearCancelled:z}=t,m=String(e.key),p=li(n,e),M=(P,T)=>{P&&Ee(P,s,9,T)},U=(P,T)=>{const A=T[1];M(P,T),D(P)?P.every(W=>W.length<=1)&&A():P.length<=1&&A()},B={mode:i,persisted:o,beforeEnter(P){let T=l;if(!n.isMounted)if(r)T=R||l;else return;P[We]&&P[We](!0);const A=p[m];A&&st(e,A)&&A.el[We]&&A.el[We](),M(T,[P])},enter(P){let T=c,A=u,W=d;if(!n.isMounted)if(r)T=j||c,A=V||u,W=z||d;else return;let S=!1;const q=P[Qt]=ie=>{S||(S=!0,ie?M(W,[P]):M(A,[P]),B.delayedLeave&&B.delayedLeave(),P[Qt]=void 0)};T?U(T,[P,q]):q()},leave(P,T){const A=String(e.key);if(P[Qt]&&P[Qt](!0),n.isUnmounting)return T();M(h,[P]);let W=!1;const S=P[We]=q=>{W||(W=!0,T(),q?M(F,[P]):M(E,[P]),P[We]=void 0,p[A]===e&&delete p[A])};p[A]=e,b?U(b,[P,S]):S()},clone(P){return Zn(P,t,n,s)}};return B}function Dn(e){if(Pn(e))return e=Qe(e),e.children=null,e}function zs(e){return Pn(e)?e.children?e.children[0]:void 0:e}function Gn(e,t){e.shapeFlag&6&&e.component?Gn(e.component.subTree,t):e.shapeFlag&128?(e.ssContent.transition=t.clone(e.ssContent),e.ssFallback.transition=t.clone(e.ssFallback)):e.transition=t}function ci(e,t=!1,n){let s=[],r=0;for(let i=0;i1)for(let i=0;ioe({name:e.name},t,{setup:e}))():e}const _t=e=>!!e.type.__asyncLoader,Pn=e=>e.type.__isKeepAlive;function al(e,t){ui(e,"a",t)}function ul(e,t){ui(e,"da",t)}function ui(e,t,n=le){const s=e.__wdc||(e.__wdc=()=>{let r=n;for(;r;){if(r.isDeactivated)return;r=r.parent}return e()});if(In(t,s,n),n){let r=n.parent;for(;r&&r.parent;)Pn(r.parent.vnode)&&fl(s,t,n,r),r=r.parent}}function fl(e,t,n,s){const r=In(t,e,s,!0);Mn(()=>{as(s[t],r)},n)}function In(e,t,n=le,s=!1){if(n){const r=n[e]||(n[e]=[]),i=t.__weh||(t.__weh=(...o)=>{if(n.isUnmounted)return;Et(),xt(n);const l=Ee(t,n,e,o);return ot(),Tt(),l});return s?r.unshift(i):r.push(i),i}}const De=e=>(t,n=le)=>(!Bt||e==="sp")&&In(e,(...s)=>t(...s),n),dl=De("bm"),At=De("m"),hl=De("bu"),pl=De("u"),fi=De("bum"),Mn=De("um"),gl=De("sp"),ml=De("rtg"),yl=De("rtc");function _l(e,t=le){In("ec",e,t)}function Ta(e,t,n,s){let r;const i=n&&n[s];if(D(e)||ne(e)){r=new Array(e.length);for(let o=0,l=e.length;ot(o,l,void 0,i&&i[l]));else{const o=Object.keys(e);r=new Array(o.length);for(let l=0,c=o.length;lmn(t)?!(t.type===ve||t.type===ge&&!di(t.children)):!0)?e:null}function Sa(e,t){const n={};for(const s in e)n[t&&/[A-Z]/.test(s)?`on:${s}`:rn(s)]=e[s];return n}const es=e=>e?Oi(e)?Rs(e)||e.proxy:es(e.parent):null,Ft=oe(Object.create(null),{$:e=>e,$el:e=>e.vnode.el,$data:e=>e.data,$props:e=>e.props,$attrs:e=>e.attrs,$slots:e=>e.slots,$refs:e=>e.refs,$parent:e=>es(e.parent),$root:e=>es(e.root),$emit:e=>e.emit,$options:e=>Ts(e),$forceUpdate:e=>e.f||(e.f=()=>Cs(e.update)),$nextTick:e=>e.n||(e.n=An.bind(e.proxy)),$watch:e=>il.bind(e)}),Bn=(e,t)=>e!==te&&!e.__isScriptSetup&&X(e,t),bl={get({_:e},t){const{ctx:n,setupState:s,data:r,props:i,accessCache:o,type:l,appContext:c}=e;let u;if(t[0]!=="$"){const E=o[t];if(E!==void 0)switch(E){case 1:return s[t];case 2:return r[t];case 4:return n[t];case 3:return i[t]}else{if(Bn(s,t))return o[t]=1,s[t];if(r!==te&&X(r,t))return o[t]=2,r[t];if((u=e.propsOptions[0])&&X(u,t))return o[t]=3,i[t];if(n!==te&&X(n,t))return o[t]=4,n[t];ts&&(o[t]=0)}}const d=Ft[t];let h,b;if(d)return t==="$attrs"&&ye(e,"get",t),d(e);if((h=l.__cssModules)&&(h=h[t]))return h;if(n!==te&&X(n,t))return o[t]=4,n[t];if(b=c.config.globalProperties,X(b,t))return b[t]},set({_:e},t,n){const{data:s,setupState:r,ctx:i}=e;return Bn(r,t)?(r[t]=n,!0):s!==te&&X(s,t)?(s[t]=n,!0):X(e.props,t)||t[0]==="$"&&t.slice(1)in e?!1:(i[t]=n,!0)},has({_:{data:e,setupState:t,accessCache:n,ctx:s,appContext:r,propsOptions:i}},o){let l;return!!n[o]||e!==te&&X(e,o)||Bn(t,o)||(l=i[0])&&X(l,o)||X(s,o)||X(Ft,o)||X(r.config.globalProperties,o)},defineProperty(e,t,n){return n.get!=null?e._.accessCache[t]=0:X(n,"value")&&this.set(e,t,n.value,null),Reflect.defineProperty(e,t,n)}};function Oa(){return vl().slots}function vl(){const e=Ln();return e.setupContext||(e.setupContext=Pi(e))}function Ys(e){return D(e)?e.reduce((t,n)=>(t[n]=null,t),{}):e}let ts=!0;function wl(e){const t=Ts(e),n=e.proxy,s=e.ctx;ts=!1,t.beforeCreate&&Js(t.beforeCreate,e,"bc");const{data:r,computed:i,methods:o,watch:l,provide:c,inject:u,created:d,beforeMount:h,mounted:b,beforeUpdate:E,updated:F,activated:R,deactivated:j,beforeDestroy:V,beforeUnmount:z,destroyed:m,unmounted:p,render:M,renderTracked:U,renderTriggered:B,errorCaptured:P,serverPrefetch:T,expose:A,inheritAttrs:W,components:S,directives:q,filters:ie}=t;if(u&&xl(u,s,null),o)for(const J in o){const $=o[J];K($)&&(s[J]=$.bind(n))}if(r){const J=r.call(n,n);ee(J)&&(e.data=Cn(J))}if(ts=!0,i)for(const J in i){const $=i[J],Ne=K($)?$.bind(n,n):K($.get)?$.get.bind(n,n):Re,Kt=!K($)&&K($.set)?$.set.bind(n):Re,Ze=re({get:Ne,set:Kt});Object.defineProperty(s,J,{enumerable:!0,configurable:!0,get:()=>Ze.value,set:Pe=>Ze.value=Pe})}if(l)for(const J in l)hi(l[J],s,n,J);if(c){const J=K(c)?c.call(n):c;Reflect.ownKeys(J).forEach($=>{Ol($,J[$])})}d&&Js(d,e,"c");function L(J,$){D($)?$.forEach(Ne=>J(Ne.bind(n))):$&&J($.bind(n))}if(L(dl,h),L(At,b),L(hl,E),L(pl,F),L(al,R),L(ul,j),L(_l,P),L(yl,U),L(ml,B),L(fi,z),L(Mn,p),L(gl,T),D(A))if(A.length){const J=e.exposed||(e.exposed={});A.forEach($=>{Object.defineProperty(J,$,{get:()=>n[$],set:Ne=>n[$]=Ne})})}else e.exposed||(e.exposed={});M&&e.render===Re&&(e.render=M),W!=null&&(e.inheritAttrs=W),S&&(e.components=S),q&&(e.directives=q)}function xl(e,t,n=Re){D(e)&&(e=ns(e));for(const s in e){const r=e[s];let i;ee(r)?"default"in r?i=bt(r.from||s,r.default,!0):i=bt(r.from||s):i=bt(r),ce(i)?Object.defineProperty(t,s,{enumerable:!0,configurable:!0,get:()=>i.value,set:o=>i.value=o}):t[s]=i}}function Js(e,t,n){Ee(D(e)?e.map(s=>s.bind(t.proxy)):e.bind(t.proxy),t,n)}function hi(e,t,n,s){const r=s.includes(".")?ii(n,s):()=>n[s];if(ne(e)){const i=t[e];K(i)&&Ye(r,i)}else if(K(e))Ye(r,e.bind(n));else if(ee(e))if(D(e))e.forEach(i=>hi(i,t,n,s));else{const i=K(e.handler)?e.handler.bind(n):t[e.handler];K(i)&&Ye(r,i,e)}}function Ts(e){const t=e.type,{mixins:n,extends:s}=t,{mixins:r,optionsCache:i,config:{optionMergeStrategies:o}}=e.appContext,l=i.get(t);let c;return l?c=l:!r.length&&!n&&!s?c=t:(c={},r.length&&r.forEach(u=>hn(c,u,o,!0)),hn(c,t,o)),ee(t)&&i.set(t,c),c}function hn(e,t,n,s=!1){const{mixins:r,extends:i}=t;i&&hn(e,i,n,!0),r&&r.forEach(o=>hn(e,o,n,!0));for(const o in t)if(!(s&&o==="expose")){const l=Cl[o]||n&&n[o];e[o]=l?l(e[o],t[o]):t[o]}return e}const Cl={data:Xs,props:Qs,emits:Qs,methods:Pt,computed:Pt,beforeCreate:he,created:he,beforeMount:he,mounted:he,beforeUpdate:he,updated:he,beforeDestroy:he,beforeUnmount:he,destroyed:he,unmounted:he,activated:he,deactivated:he,errorCaptured:he,serverPrefetch:he,components:Pt,directives:Pt,watch:Tl,provide:Xs,inject:El};function Xs(e,t){return t?e?function(){return oe(K(e)?e.call(this,this):e,K(t)?t.call(this,this):t)}:t:e}function El(e,t){return Pt(ns(e),ns(t))}function ns(e){if(D(e)){const t={};for(let n=0;n1)return n&&K(t)?t.call(s&&s.proxy):t}}function Rl(e,t,n,s=!1){const r={},i={};cn(i,Fn,1),e.propsDefaults=Object.create(null),gi(e,t,r,i);for(const o in e.propsOptions[0])o in r||(r[o]=void 0);n?e.props=s?r:Ho(r):e.type.props?e.props=r:e.props=i,e.attrs=i}function Pl(e,t,n,s){const{props:r,attrs:i,vnode:{patchFlag:o}}=e,l=Q(r),[c]=e.propsOptions;let u=!1;if((s||o>0)&&!(o&16)){if(o&8){const d=e.vnode.dynamicProps;for(let h=0;h{c=!0;const[b,E]=mi(h,t,!0);oe(o,b),E&&l.push(...E)};!n&&t.mixins.length&&t.mixins.forEach(d),e.extends&&d(e.extends),e.mixins&&e.mixins.forEach(d)}if(!i&&!c)return ee(e)&&s.set(e,pt),pt;if(D(i))for(let d=0;d-1,E[1]=R<0||F-1||X(E,"default"))&&l.push(h)}}}const u=[o,l];return ee(e)&&s.set(e,u),u}function Zs(e){return e[0]!=="$"}function Gs(e){const t=e&&e.toString().match(/^\s*(function|class) (\w+)/);return t?t[2]:e===null?"null":""}function er(e,t){return Gs(e)===Gs(t)}function tr(e,t){return D(t)?t.findIndex(n=>er(n,e)):K(t)&&er(t,e)?0:-1}const yi=e=>e[0]==="_"||e==="$stable",As=e=>D(e)?e.map(Ae):[Ae(e)],Il=(e,t,n)=>{if(t._n)return t;const s=Go((...r)=>As(t(...r)),n);return s._c=!1,s},_i=(e,t,n)=>{const s=e._ctx;for(const r in e){if(yi(r))continue;const i=e[r];if(K(i))t[r]=Il(r,i,s);else if(i!=null){const o=As(i);t[r]=()=>o}}},bi=(e,t)=>{const n=As(t);e.slots.default=()=>n},Ml=(e,t)=>{if(e.vnode.shapeFlag&32){const n=t._;n?(e.slots=Q(t),cn(t,"_",n)):_i(t,e.slots={})}else e.slots={},t&&bi(e,t);cn(e.slots,Fn,1)},Fl=(e,t,n)=>{const{vnode:s,slots:r}=e;let i=!0,o=te;if(s.shapeFlag&32){const l=t._;l?n&&l===1?i=!1:(oe(r,t),!n&&l===1&&delete r._):(i=!t.$stable,_i(t,r)),o=t}else t&&(bi(e,t),o={default:1});if(i)for(const l in r)!yi(l)&&o[l]==null&&delete r[l]};function gn(e,t,n,s,r=!1){if(D(e)){e.forEach((b,E)=>gn(b,t&&(D(t)?t[E]:t),n,s,r));return}if(_t(s)&&!r)return;const i=s.shapeFlag&4?Rs(s.component)||s.component.proxy:s.el,o=r?null:i,{i:l,r:c}=e,u=t&&t.r,d=l.refs===te?l.refs={}:l.refs,h=l.setupState;if(u!=null&&u!==c&&(ne(u)?(d[u]=null,X(h,u)&&(h[u]=null)):ce(u)&&(u.value=null)),K(c))ze(c,l,12,[o,d]);else{const b=ne(c),E=ce(c);if(b||E){const F=()=>{if(e.f){const R=b?X(h,c)?h[c]:d[c]:c.value;r?D(R)&&as(R,i):D(R)?R.includes(i)||R.push(i):b?(d[c]=[i],X(h,c)&&(h[c]=d[c])):(c.value=[i],e.k&&(d[e.k]=c.value))}else b?(d[c]=o,X(h,c)&&(h[c]=o)):E&&(c.value=o,e.k&&(d[e.k]=o))};o?(F.id=-1,pe(F,n)):F()}}}let ke=!1;const Zt=e=>/svg/.test(e.namespaceURI)&&e.tagName!=="foreignObject",Gt=e=>e.nodeType===8;function Ll(e){const{mt:t,p:n,o:{patchProp:s,createText:r,nextSibling:i,parentNode:o,remove:l,insert:c,createComment:u}}=e,d=(m,p)=>{if(!p.hasChildNodes()){n(null,m,p),fn(),p._vnode=m;return}ke=!1,h(p.firstChild,m,null,null,null),fn(),p._vnode=m,ke&&console.error("Hydration completed but contains mismatches.")},h=(m,p,M,U,B,P=!1)=>{const T=Gt(m)&&m.data==="[",A=()=>R(m,p,M,U,B,T),{type:W,ref:S,shapeFlag:q,patchFlag:ie}=p;let ae=m.nodeType;p.el=m,ie===-2&&(P=!1,p.dynamicChildren=null);let L=null;switch(W){case wt:ae!==3?p.children===""?(c(p.el=r(""),o(m),m),L=m):L=A():(m.data!==p.children&&(ke=!0,m.data=p.children),L=i(m));break;case ve:z(m)?(L=i(m),V(p.el=m.content.firstChild,m,M)):ae!==8||T?L=A():L=i(m);break;case Lt:if(T&&(m=i(m),ae=m.nodeType),ae===1||ae===3){L=m;const J=!p.children.length;for(let $=0;${P=P||!!p.dynamicChildren;const{type:T,props:A,patchFlag:W,shapeFlag:S,dirs:q,transition:ie}=p,ae=T==="input"&&q||T==="option";if(ae||W!==-1){if(q&&Me(p,null,M,"created"),A)if(ae||!P||W&48)for(const $ in A)(ae&&$.endsWith("value")||Ut($)&&!It($))&&s(m,$,null,A[$],!1,void 0,M);else A.onClick&&s(m,"onClick",null,A.onClick,!1,void 0,M);let L;(L=A&&A.onVnodeBeforeMount)&&xe(L,M,p);let J=!1;if(z(m)){J=vi(U,ie)&&M&&M.vnode.props&&M.vnode.props.appear;const $=m.content.firstChild;J&&ie.beforeEnter($),V($,m,M),p.el=m=$}if(q&&Me(p,null,M,"beforeMount"),((L=A&&A.onVnodeMounted)||q||J)&&si(()=>{L&&xe(L,M,p),J&&ie.enter(m),q&&Me(p,null,M,"mounted")},U),S&16&&!(A&&(A.innerHTML||A.textContent))){let $=E(m.firstChild,p,m,M,U,B,P);for(;$;){ke=!0;const Ne=$;$=$.nextSibling,l(Ne)}}else S&8&&m.textContent!==p.children&&(ke=!0,m.textContent=p.children)}return m.nextSibling},E=(m,p,M,U,B,P,T)=>{T=T||!!p.dynamicChildren;const A=p.children,W=A.length;for(let S=0;S{const{slotScopeIds:T}=p;T&&(B=B?B.concat(T):T);const A=o(m),W=E(i(m),p,A,M,U,B,P);return W&&Gt(W)&&W.data==="]"?i(p.anchor=W):(ke=!0,c(p.anchor=u("]"),A,W),W)},R=(m,p,M,U,B,P)=>{if(ke=!0,p.el=null,P){const W=j(m);for(;;){const S=i(m);if(S&&S!==W)l(S);else break}}const T=i(m),A=o(m);return l(m),n(null,p,A,T,M,U,Zt(A),B),T},j=(m,p="[",M="]")=>{let U=0;for(;m;)if(m=i(m),m&&Gt(m)&&(m.data===p&&U++,m.data===M)){if(U===0)return i(m);U--}return m},V=(m,p,M)=>{const U=p.parentNode;U&&U.replaceChild(m,p);let B=M;for(;B;)B.vnode.el===p&&(B.vnode.el=B.subTree.el=m),B=B.parent},z=m=>m.nodeType===1&&m.tagName.toLowerCase()==="template";return[d,h]}const pe=si;function Nl(e){return Hl(e,Ll)}function Hl(e,t){const n=zn();n.__VUE__=!0;const{insert:s,remove:r,patchProp:i,createElement:o,createText:l,createComment:c,setText:u,setElementText:d,parentNode:h,nextSibling:b,setScopeId:E=Re,insertStaticContent:F}=e,R=(a,f,g,y=null,_=null,x=null,O=!1,w=null,C=!!f.dynamicChildren)=>{if(a===f)return;a&&!st(a,f)&&(y=Wt(a),Pe(a,_,x,!0),a=null),f.patchFlag===-2&&(C=!1,f.dynamicChildren=null);const{type:v,ref:N,shapeFlag:I}=f;switch(v){case wt:j(a,f,g,y);break;case ve:V(a,f,g,y);break;case Lt:a==null&&z(f,g,y,O);break;case ge:S(a,f,g,y,_,x,O,w,C);break;default:I&1?M(a,f,g,y,_,x,O,w,C):I&6?q(a,f,g,y,_,x,O,w,C):(I&64||I&128)&&v.process(a,f,g,y,_,x,O,w,C,ut)}N!=null&&_&&gn(N,a&&a.ref,x,f||a,!f)},j=(a,f,g,y)=>{if(a==null)s(f.el=l(f.children),g,y);else{const _=f.el=a.el;f.children!==a.children&&u(_,f.children)}},V=(a,f,g,y)=>{a==null?s(f.el=c(f.children||""),g,y):f.el=a.el},z=(a,f,g,y)=>{[a.el,a.anchor]=F(a.children,f,g,y,a.el,a.anchor)},m=({el:a,anchor:f},g,y)=>{let _;for(;a&&a!==f;)_=b(a),s(a,g,y),a=_;s(f,g,y)},p=({el:a,anchor:f})=>{let g;for(;a&&a!==f;)g=b(a),r(a),a=g;r(f)},M=(a,f,g,y,_,x,O,w,C)=>{O=O||f.type==="svg",a==null?U(f,g,y,_,x,O,w,C):T(a,f,_,x,O,w,C)},U=(a,f,g,y,_,x,O,w)=>{let C,v;const{type:N,props:I,shapeFlag:H,transition:k,dirs:Y}=a;if(C=a.el=o(a.type,x,I&&I.is,I),H&8?d(C,a.children):H&16&&P(a.children,C,null,y,_,x&&N!=="foreignObject",O,w),Y&&Me(a,null,y,"created"),B(C,a,a.scopeId,O,y),I){for(const Z in I)Z!=="value"&&!It(Z)&&i(C,Z,null,I[Z],x,a.children,y,_,He);"value"in I&&i(C,"value",null,I.value),(v=I.onVnodeBeforeMount)&&xe(v,y,a)}Y&&Me(a,null,y,"beforeMount");const G=vi(_,k);G&&k.beforeEnter(C),s(C,f,g),((v=I&&I.onVnodeMounted)||G||Y)&&pe(()=>{v&&xe(v,y,a),G&&k.enter(C),Y&&Me(a,null,y,"mounted")},_)},B=(a,f,g,y,_)=>{if(g&&E(a,g),y)for(let x=0;x{for(let v=C;v{const w=f.el=a.el;let{patchFlag:C,dynamicChildren:v,dirs:N}=f;C|=a.patchFlag&16;const I=a.props||te,H=f.props||te;let k;g&&Ge(g,!1),(k=H.onVnodeBeforeUpdate)&&xe(k,g,f,a),N&&Me(f,a,g,"beforeUpdate"),g&&Ge(g,!0);const Y=_&&f.type!=="foreignObject";if(v?A(a.dynamicChildren,v,w,g,y,Y,x):O||$(a,f,w,null,g,y,Y,x,!1),C>0){if(C&16)W(w,f,I,H,g,y,_);else if(C&2&&I.class!==H.class&&i(w,"class",null,H.class,_),C&4&&i(w,"style",I.style,H.style,_),C&8){const G=f.dynamicProps;for(let Z=0;Z{k&&xe(k,g,f,a),N&&Me(f,a,g,"updated")},y)},A=(a,f,g,y,_,x,O)=>{for(let w=0;w{if(g!==y){if(g!==te)for(const w in g)!It(w)&&!(w in y)&&i(a,w,g[w],null,O,f.children,_,x,He);for(const w in y){if(It(w))continue;const C=y[w],v=g[w];C!==v&&w!=="value"&&i(a,w,v,C,O,f.children,_,x,He)}"value"in y&&i(a,"value",g.value,y.value)}},S=(a,f,g,y,_,x,O,w,C)=>{const v=f.el=a?a.el:l(""),N=f.anchor=a?a.anchor:l("");let{patchFlag:I,dynamicChildren:H,slotScopeIds:k}=f;k&&(w=w?w.concat(k):k),a==null?(s(v,g,y),s(N,g,y),P(f.children,g,N,_,x,O,w,C)):I>0&&I&64&&H&&a.dynamicChildren?(A(a.dynamicChildren,H,g,_,x,O,w),(f.key!=null||_&&f===_.subTree)&&wi(a,f,!0)):$(a,f,g,N,_,x,O,w,C)},q=(a,f,g,y,_,x,O,w,C)=>{f.slotScopeIds=w,a==null?f.shapeFlag&512?_.ctx.activate(f,g,y,O,C):ie(f,g,y,_,x,O,C):ae(a,f,C)},ie=(a,f,g,y,_,x,O)=>{const w=a.component=Vl(a,y,_);if(Pn(a)&&(w.ctx.renderer=ut),ql(w),w.asyncDep){if(_&&_.registerDep(w,L),!a.el){const C=w.subTree=ue(ve);V(null,C,f,g)}return}L(w,a,f,g,_,x,O)},ae=(a,f,g)=>{const y=f.component=a.component;if(nl(a,f,g))if(y.asyncDep&&!y.asyncResolved){J(y,f,g);return}else y.next=f,Jo(y.update),y.update();else f.el=a.el,y.vnode=f},L=(a,f,g,y,_,x,O)=>{const w=()=>{if(a.isMounted){let{next:N,bu:I,u:H,parent:k,vnode:Y}=a,G=N,Z;Ge(a,!1),N?(N.el=Y.el,J(a,N,O)):N=Y,I&&$n(I),(Z=N.props&&N.props.onVnodeBeforeUpdate)&&xe(Z,k,N,Y),Ge(a,!0);const se=jn(a),Te=a.subTree;a.subTree=se,R(Te,se,h(Te.el),Wt(Te),a,_,x),N.el=se.el,G===null&&sl(a,se.el),H&&pe(H,_),(Z=N.props&&N.props.onVnodeUpdated)&&pe(()=>xe(Z,k,N,Y),_)}else{let N;const{el:I,props:H}=f,{bm:k,m:Y,parent:G}=a,Z=_t(f);if(Ge(a,!1),k&&$n(k),!Z&&(N=H&&H.onVnodeBeforeMount)&&xe(N,G,f),Ge(a,!0),I&&Hn){const se=()=>{a.subTree=jn(a),Hn(I,a.subTree,a,_,null)};Z?f.type.__asyncLoader().then(()=>!a.isUnmounted&&se()):se()}else{const se=a.subTree=jn(a);R(null,se,g,y,a,_,x),f.el=se.el}if(Y&&pe(Y,_),!Z&&(N=H&&H.onVnodeMounted)){const se=f;pe(()=>xe(N,G,se),_)}(f.shapeFlag&256||G&&_t(G.vnode)&&G.vnode.shapeFlag&256)&&a.a&&pe(a.a,_),a.isMounted=!0,f=g=y=null}},C=a.effect=new ps(w,()=>Cs(v),a.scope),v=a.update=()=>C.run();v.id=a.uid,Ge(a,!0),v()},J=(a,f,g)=>{f.component=a;const y=a.vnode.props;a.vnode=f,a.next=null,Pl(a,f.props,y,g),Fl(a,f.children,g),Et(),Ws(),Tt()},$=(a,f,g,y,_,x,O,w,C=!1)=>{const v=a&&a.children,N=a?a.shapeFlag:0,I=f.children,{patchFlag:H,shapeFlag:k}=f;if(H>0){if(H&128){Kt(v,I,g,y,_,x,O,w,C);return}else if(H&256){Ne(v,I,g,y,_,x,O,w,C);return}}k&8?(N&16&&He(v,_,x),I!==v&&d(g,I)):N&16?k&16?Kt(v,I,g,y,_,x,O,w,C):He(v,_,x,!0):(N&8&&d(g,""),k&16&&P(I,g,y,_,x,O,w,C))},Ne=(a,f,g,y,_,x,O,w,C)=>{a=a||pt,f=f||pt;const v=a.length,N=f.length,I=Math.min(v,N);let H;for(H=0;HN?He(a,_,x,!0,!1,I):P(f,g,y,_,x,O,w,C,I)},Kt=(a,f,g,y,_,x,O,w,C)=>{let v=0;const N=f.length;let I=a.length-1,H=N-1;for(;v<=I&&v<=H;){const k=a[v],Y=f[v]=C?Ve(f[v]):Ae(f[v]);if(st(k,Y))R(k,Y,g,null,_,x,O,w,C);else break;v++}for(;v<=I&&v<=H;){const k=a[I],Y=f[H]=C?Ve(f[H]):Ae(f[H]);if(st(k,Y))R(k,Y,g,null,_,x,O,w,C);else break;I--,H--}if(v>I){if(v<=H){const k=H+1,Y=kH)for(;v<=I;)Pe(a[v],_,x,!0),v++;else{const k=v,Y=v,G=new Map;for(v=Y;v<=H;v++){const _e=f[v]=C?Ve(f[v]):Ae(f[v]);_e.key!=null&&G.set(_e.key,v)}let Z,se=0;const Te=H-Y+1;let ft=!1,Fs=0;const St=new Array(Te);for(v=0;v=Te){Pe(_e,_,x,!0);continue}let Ie;if(_e.key!=null)Ie=G.get(_e.key);else for(Z=Y;Z<=H;Z++)if(St[Z-Y]===0&&st(_e,f[Z])){Ie=Z;break}Ie===void 0?Pe(_e,_,x,!0):(St[Ie-Y]=v+1,Ie>=Fs?Fs=Ie:ft=!0,R(_e,f[Ie],g,null,_,x,O,w,C),se++)}const Ls=ft?$l(St):pt;for(Z=Ls.length-1,v=Te-1;v>=0;v--){const _e=Y+v,Ie=f[_e],Ns=_e+1{const{el:x,type:O,transition:w,children:C,shapeFlag:v}=a;if(v&6){Ze(a.component.subTree,f,g,y);return}if(v&128){a.suspense.move(f,g,y);return}if(v&64){O.move(a,f,g,ut);return}if(O===ge){s(x,f,g);for(let I=0;Iw.enter(x),_);else{const{leave:I,delayLeave:H,afterLeave:k}=w,Y=()=>s(x,f,g),G=()=>{I(x,()=>{Y(),k&&k()})};H?H(x,Y,G):G()}else s(x,f,g)},Pe=(a,f,g,y=!1,_=!1)=>{const{type:x,props:O,ref:w,children:C,dynamicChildren:v,shapeFlag:N,patchFlag:I,dirs:H}=a;if(w!=null&&gn(w,null,g,a,!0),N&256){f.ctx.deactivate(a);return}const k=N&1&&H,Y=!_t(a);let G;if(Y&&(G=O&&O.onVnodeBeforeUnmount)&&xe(G,f,a),N&6)Xi(a.component,g,y);else{if(N&128){a.suspense.unmount(g,y);return}k&&Me(a,null,f,"beforeUnmount"),N&64?a.type.remove(a,f,g,_,ut,y):v&&(x!==ge||I>0&&I&64)?He(v,f,g,!1,!0):(x===ge&&I&384||!_&&N&16)&&He(C,f,g),y&&Is(a)}(Y&&(G=O&&O.onVnodeUnmounted)||k)&&pe(()=>{G&&xe(G,f,a),k&&Me(a,null,f,"unmounted")},g)},Is=a=>{const{type:f,el:g,anchor:y,transition:_}=a;if(f===ge){Ji(g,y);return}if(f===Lt){p(a);return}const x=()=>{r(g),_&&!_.persisted&&_.afterLeave&&_.afterLeave()};if(a.shapeFlag&1&&_&&!_.persisted){const{leave:O,delayLeave:w}=_,C=()=>O(g,x);w?w(a.el,x,C):C()}else x()},Ji=(a,f)=>{let g;for(;a!==f;)g=b(a),r(a),a=g;r(f)},Xi=(a,f,g)=>{const{bum:y,scope:_,update:x,subTree:O,um:w}=a;y&&$n(y),_.stop(),x&&(x.active=!1,Pe(O,a,f,g)),w&&pe(w,f),pe(()=>{a.isUnmounted=!0},f),f&&f.pendingBranch&&!f.isUnmounted&&a.asyncDep&&!a.asyncResolved&&a.suspenseId===f.pendingId&&(f.deps--,f.deps===0&&f.resolve())},He=(a,f,g,y=!1,_=!1,x=0)=>{for(let O=x;Oa.shapeFlag&6?Wt(a.component.subTree):a.shapeFlag&128?a.suspense.next():b(a.anchor||a.el),Ms=(a,f,g)=>{a==null?f._vnode&&Pe(f._vnode,null,null,!0):R(f._vnode||null,a,f,null,null,null,g),Ws(),fn(),f._vnode=a},ut={p:R,um:Pe,m:Ze,r:Is,mt:ie,mc:P,pc:$,pbc:A,n:Wt,o:e};let Nn,Hn;return t&&([Nn,Hn]=t(ut)),{render:Ms,hydrate:Nn,createApp:Sl(Ms,Nn)}}function Ge({effect:e,update:t},n){e.allowRecurse=t.allowRecurse=n}function vi(e,t){return(!e||e&&!e.pendingBranch)&&t&&!t.persisted}function wi(e,t,n=!1){const s=e.children,r=t.children;if(D(s)&&D(r))for(let i=0;i>1,e[n[l]]0&&(t[s]=n[i-1]),n[i]=s)}}for(i=n.length,o=n[i-1];i-- >0;)n[i]=o,o=t[o];return n}const jl=e=>e.__isTeleport,ge=Symbol.for("v-fgt"),wt=Symbol.for("v-txt"),ve=Symbol.for("v-cmt"),Lt=Symbol.for("v-stc"),Nt=[];let Oe=null;function xi(e=!1){Nt.push(Oe=e?null:[])}function Dl(){Nt.pop(),Oe=Nt[Nt.length-1]||null}let Dt=1;function nr(e){Dt+=e}function Ci(e){return e.dynamicChildren=Dt>0?Oe||pt:null,Dl(),Dt>0&&Oe&&Oe.push(e),e}function Ra(e,t,n,s,r,i){return Ci(Ai(e,t,n,s,r,i,!0))}function Ei(e,t,n,s,r){return Ci(ue(e,t,n,s,r,!0))}function mn(e){return e?e.__v_isVNode===!0:!1}function st(e,t){return e.type===t.type&&e.key===t.key}const Fn="__vInternal",Ti=({key:e})=>e??null,on=({ref:e,ref_key:t,ref_for:n})=>(typeof e=="number"&&(e=""+e),e!=null?ne(e)||ce(e)||K(e)?{i:de,r:e,k:t,f:!!n}:e:null);function Ai(e,t=null,n=null,s=0,r=null,i=e===ge?0:1,o=!1,l=!1){const c={__v_isVNode:!0,__v_skip:!0,type:e,props:t,key:t&&Ti(t),ref:t&&on(t),scopeId:On,slotScopeIds:null,children:n,component:null,suspense:null,ssContent:null,ssFallback:null,dirs:null,transition:null,el:null,anchor:null,target:null,targetAnchor:null,staticCount:0,shapeFlag:i,patchFlag:s,dynamicProps:r,dynamicChildren:null,appContext:null,ctx:de};return l?(Ss(c,n),i&128&&e.normalize(c)):n&&(c.shapeFlag|=ne(n)?8:16),Dt>0&&!o&&Oe&&(c.patchFlag>0||i&6)&&c.patchFlag!==32&&Oe.push(c),c}const ue=Bl;function Bl(e,t=null,n=null,s=0,r=null,i=!1){if((!e||e===ti)&&(e=ve),mn(e)){const l=Qe(e,t,!0);return n&&Ss(l,n),Dt>0&&!i&&Oe&&(l.shapeFlag&6?Oe[Oe.indexOf(e)]=l:Oe.push(l)),l.patchFlag|=-2,l}if(Xl(e)&&(e=e.__vccOpts),t){t=kl(t);let{class:l,style:c}=t;l&&!ne(l)&&(t.class=ds(l)),ee(c)&&(zr(c)&&!D(c)&&(c=oe({},c)),t.style=fs(c))}const o=ne(e)?1:rl(e)?128:jl(e)?64:ee(e)?4:K(e)?2:0;return Ai(e,t,n,s,r,o,i,!0)}function kl(e){return e?zr(e)||Fn in e?oe({},e):e:null}function Qe(e,t,n=!1){const{props:s,ref:r,patchFlag:i,children:o}=e,l=t?Ul(s||{},t):s;return{__v_isVNode:!0,__v_skip:!0,type:e.type,props:l,key:l&&Ti(l),ref:t&&t.ref?n&&r?D(r)?r.concat(on(t)):[r,on(t)]:on(t):r,scopeId:e.scopeId,slotScopeIds:e.slotScopeIds,children:o,target:e.target,targetAnchor:e.targetAnchor,staticCount:e.staticCount,shapeFlag:e.shapeFlag,patchFlag:t&&e.type!==ge?i===-1?16:i|16:i,dynamicProps:e.dynamicProps,dynamicChildren:e.dynamicChildren,appContext:e.appContext,dirs:e.dirs,transition:e.transition,component:e.component,suspense:e.suspense,ssContent:e.ssContent&&Qe(e.ssContent),ssFallback:e.ssFallback&&Qe(e.ssFallback),el:e.el,anchor:e.anchor,ctx:e.ctx,ce:e.ce}}function Si(e=" ",t=0){return ue(wt,null,e,t)}function Pa(e,t){const n=ue(Lt,null,e);return n.staticCount=t,n}function Ia(e="",t=!1){return t?(xi(),Ei(ve,null,e)):ue(ve,null,e)}function Ae(e){return e==null||typeof e=="boolean"?ue(ve):D(e)?ue(ge,null,e.slice()):typeof e=="object"?Ve(e):ue(wt,null,String(e))}function Ve(e){return e.el===null&&e.patchFlag!==-1||e.memo?e:Qe(e)}function Ss(e,t){let n=0;const{shapeFlag:s}=e;if(t==null)t=null;else if(D(t))n=16;else if(typeof t=="object")if(s&65){const r=t.default;r&&(r._c&&(r._d=!1),Ss(e,r()),r._c&&(r._d=!0));return}else{n=32;const r=t._;!r&&!(Fn in t)?t._ctx=de:r===3&&de&&(de.slots._===1?t._=1:(t._=2,e.patchFlag|=1024))}else K(t)?(t={default:t,_ctx:de},n=32):(t=String(t),s&64?(n=16,t=[Si(t)]):n=8);e.children=t,e.shapeFlag|=n}function Ul(...e){const t={};for(let n=0;nle||de;let Os,dt,sr="__VUE_INSTANCE_SETTERS__";(dt=zn()[sr])||(dt=zn()[sr]=[]),dt.push(e=>le=e),Os=e=>{dt.length>1?dt.forEach(t=>t(e)):dt[0](e)};const xt=e=>{Os(e),e.scope.on()},ot=()=>{le&&le.scope.off(),Os(null)};function Oi(e){return e.vnode.shapeFlag&4}let Bt=!1;function ql(e,t=!1){Bt=t;const{props:n,children:s}=e.vnode,r=Oi(e);Rl(e,n,r,t),Ml(e,s);const i=r?zl(e,t):void 0;return Bt=!1,i}function zl(e,t){const n=e.type;e.accessCache=Object.create(null),e.proxy=Mt(new Proxy(e.ctx,bl));const{setup:s}=n;if(s){const r=e.setupContext=s.length>1?Pi(e):null;xt(e),Et();const i=ze(s,e,0,[e.props,r]);if(Tt(),ot(),Ir(i)){if(i.then(ot,ot),t)return i.then(o=>{rr(e,o,t)}).catch(o=>{Tn(o,e,0)});e.asyncDep=i}else rr(e,i,t)}else Ri(e,t)}function rr(e,t,n){K(t)?e.type.__ssrInlineRender?e.ssrRender=t:e.render=t:ee(t)&&(e.setupState=Xr(t)),Ri(e,n)}let ir;function Ri(e,t,n){const s=e.type;if(!e.render){if(!t&&ir&&!s.render){const r=s.template||Ts(e).template;if(r){const{isCustomElement:i,compilerOptions:o}=e.appContext.config,{delimiters:l,compilerOptions:c}=s,u=oe(oe({isCustomElement:i,delimiters:l},o),c);s.render=ir(r,u)}}e.render=s.render||Re}{xt(e),Et();try{wl(e)}finally{Tt(),ot()}}}function Yl(e){return e.attrsProxy||(e.attrsProxy=new Proxy(e.attrs,{get(t,n){return ye(e,"get","$attrs"),t[n]}}))}function Pi(e){const t=n=>{e.exposed=n||{}};return{get attrs(){return Yl(e)},slots:e.slots,emit:e.emit,expose:t}}function Rs(e){if(e.exposed)return e.exposeProxy||(e.exposeProxy=new Proxy(Xr(Mt(e.exposed)),{get(t,n){if(n in t)return t[n];if(n in Ft)return Ft[n](e)},has(t,n){return n in t||n in Ft}}))}function Jl(e,t=!0){return K(e)?e.displayName||e.name:e.name||t&&e.__name}function Xl(e){return K(e)&&"__vccOpts"in e}const re=(e,t)=>qo(e,t,Bt);function rs(e,t,n){const s=arguments.length;return s===2?ee(t)&&!D(t)?mn(t)?ue(e,null,[t]):ue(e,t):ue(e,null,t):(s>3?n=Array.prototype.slice.call(arguments,2):s===3&&mn(n)&&(n=[n]),ue(e,t,n))}const Ql=Symbol.for("v-scx"),Zl=()=>bt(Ql),Gl="3.3.8",ec="http://www.w3.org/2000/svg",rt=typeof document<"u"?document:null,or=rt&&rt.createElement("template"),tc={insert:(e,t,n)=>{t.insertBefore(e,n||null)},remove:e=>{const t=e.parentNode;t&&t.removeChild(e)},createElement:(e,t,n,s)=>{const r=t?rt.createElementNS(ec,e):rt.createElement(e,n?{is:n}:void 0);return e==="select"&&s&&s.multiple!=null&&r.setAttribute("multiple",s.multiple),r},createText:e=>rt.createTextNode(e),createComment:e=>rt.createComment(e),setText:(e,t)=>{e.nodeValue=t},setElementText:(e,t)=>{e.textContent=t},parentNode:e=>e.parentNode,nextSibling:e=>e.nextSibling,querySelector:e=>rt.querySelector(e),setScopeId(e,t){e.setAttribute(t,"")},insertStaticContent(e,t,n,s,r,i){const o=n?n.previousSibling:t.lastChild;if(r&&(r===i||r.nextSibling))for(;t.insertBefore(r.cloneNode(!0),n),!(r===i||!(r=r.nextSibling)););else{or.innerHTML=s?`${e}`:e;const l=or.content;if(s){const c=l.firstChild;for(;c.firstChild;)l.appendChild(c.firstChild);l.removeChild(c)}t.insertBefore(l,n)}return[o?o.nextSibling:t.firstChild,n?n.previousSibling:t.lastChild]}},Ue="transition",Ot="animation",kt=Symbol("_vtc"),Ii=(e,{slots:t})=>rs(cl,nc(e),t);Ii.displayName="Transition";const Mi={name:String,type:String,css:{type:Boolean,default:!0},duration:[String,Number,Object],enterFromClass:String,enterActiveClass:String,enterToClass:String,appearFromClass:String,appearActiveClass:String,appearToClass:String,leaveFromClass:String,leaveActiveClass:String,leaveToClass:String};Ii.props=oe({},oi,Mi);const et=(e,t=[])=>{D(e)?e.forEach(n=>n(...t)):e&&e(...t)},lr=e=>e?D(e)?e.some(t=>t.length>1):e.length>1:!1;function nc(e){const t={};for(const S in e)S in Mi||(t[S]=e[S]);if(e.css===!1)return t;const{name:n="v",type:s,duration:r,enterFromClass:i=`${n}-enter-from`,enterActiveClass:o=`${n}-enter-active`,enterToClass:l=`${n}-enter-to`,appearFromClass:c=i,appearActiveClass:u=o,appearToClass:d=l,leaveFromClass:h=`${n}-leave-from`,leaveActiveClass:b=`${n}-leave-active`,leaveToClass:E=`${n}-leave-to`}=e,F=sc(r),R=F&&F[0],j=F&&F[1],{onBeforeEnter:V,onEnter:z,onEnterCancelled:m,onLeave:p,onLeaveCancelled:M,onBeforeAppear:U=V,onAppear:B=z,onAppearCancelled:P=m}=t,T=(S,q,ie)=>{tt(S,q?d:l),tt(S,q?u:o),ie&&ie()},A=(S,q)=>{S._isLeaving=!1,tt(S,h),tt(S,E),tt(S,b),q&&q()},W=S=>(q,ie)=>{const ae=S?B:z,L=()=>T(q,S,ie);et(ae,[q,L]),cr(()=>{tt(q,S?c:i),Ke(q,S?d:l),lr(ae)||ar(q,s,R,L)})};return oe(t,{onBeforeEnter(S){et(V,[S]),Ke(S,i),Ke(S,o)},onBeforeAppear(S){et(U,[S]),Ke(S,c),Ke(S,u)},onEnter:W(!1),onAppear:W(!0),onLeave(S,q){S._isLeaving=!0;const ie=()=>A(S,q);Ke(S,h),oc(),Ke(S,b),cr(()=>{S._isLeaving&&(tt(S,h),Ke(S,E),lr(p)||ar(S,s,j,ie))}),et(p,[S,ie])},onEnterCancelled(S){T(S,!1),et(m,[S])},onAppearCancelled(S){T(S,!0),et(P,[S])},onLeaveCancelled(S){A(S),et(M,[S])}})}function sc(e){if(e==null)return null;if(ee(e))return[kn(e.enter),kn(e.leave)];{const t=kn(e);return[t,t]}}function kn(e){return ro(e)}function Ke(e,t){t.split(/\s+/).forEach(n=>n&&e.classList.add(n)),(e[kt]||(e[kt]=new Set)).add(t)}function tt(e,t){t.split(/\s+/).forEach(s=>s&&e.classList.remove(s));const n=e[kt];n&&(n.delete(t),n.size||(e[kt]=void 0))}function cr(e){requestAnimationFrame(()=>{requestAnimationFrame(e)})}let rc=0;function ar(e,t,n,s){const r=e._endId=++rc,i=()=>{r===e._endId&&s()};if(n)return setTimeout(i,n);const{type:o,timeout:l,propCount:c}=ic(e,t);if(!o)return s();const u=o+"end";let d=0;const h=()=>{e.removeEventListener(u,b),i()},b=E=>{E.target===e&&++d>=c&&h()};setTimeout(()=>{d(n[F]||"").split(", "),r=s(`${Ue}Delay`),i=s(`${Ue}Duration`),o=ur(r,i),l=s(`${Ot}Delay`),c=s(`${Ot}Duration`),u=ur(l,c);let d=null,h=0,b=0;t===Ue?o>0&&(d=Ue,h=o,b=i.length):t===Ot?u>0&&(d=Ot,h=u,b=c.length):(h=Math.max(o,u),d=h>0?o>u?Ue:Ot:null,b=d?d===Ue?i.length:c.length:0);const E=d===Ue&&/\b(transform|all)(,|$)/.test(s(`${Ue}Property`).toString());return{type:d,timeout:h,propCount:b,hasTransform:E}}function ur(e,t){for(;e.lengthfr(n)+fr(e[s])))}function fr(e){return e==="auto"?0:Number(e.slice(0,-1).replace(",","."))*1e3}function oc(){return document.body.offsetHeight}function lc(e,t,n){const s=e[kt];s&&(t=(t?[t,...s]:[...s]).join(" ")),t==null?e.removeAttribute("class"):n?e.setAttribute("class",t):e.className=t}const cc=Symbol("_vod");function ac(e,t,n){const s=e.style,r=ne(n);if(n&&!r){if(t&&!ne(t))for(const i in t)n[i]==null&&is(s,i,"");for(const i in n)is(s,i,n[i])}else{const i=s.display;r?t!==n&&(s.cssText=n):t&&e.removeAttribute("style"),cc in e&&(s.display=i)}}const dr=/\s*!important$/;function is(e,t,n){if(D(n))n.forEach(s=>is(e,t,s));else if(n==null&&(n=""),t.startsWith("--"))e.setProperty(t,n);else{const s=uc(e,t);dr.test(n)?e.setProperty(at(s),n.replace(dr,""),"important"):e[s]=n}}const hr=["Webkit","Moz","ms"],Un={};function uc(e,t){const n=Un[t];if(n)return n;let s=Le(t);if(s!=="filter"&&s in e)return Un[t]=s;s=wn(s);for(let r=0;rKn||(yc.then(()=>Kn=0),Kn=Date.now());function bc(e,t){const n=s=>{if(!s._vts)s._vts=Date.now();else if(s._vts<=n.attached)return;Ee(vc(s,n.value),t,5,[s])};return n.value=e,n.attached=_c(),n}function vc(e,t){if(D(t)){const n=e.stopImmediatePropagation;return e.stopImmediatePropagation=()=>{n.call(e),e._stopped=!0},t.map(s=>r=>!r._stopped&&s&&s(r))}else return t}const yr=/^on[a-z]/,wc=(e,t,n,s,r=!1,i,o,l,c)=>{t==="class"?lc(e,s,r):t==="style"?ac(e,n,s):Ut(t)?cs(t)||gc(e,t,n,s,o):(t[0]==="."?(t=t.slice(1),!0):t[0]==="^"?(t=t.slice(1),!1):xc(e,t,s,r))?dc(e,t,s,i,o,l,c):(t==="true-value"?e._trueValue=s:t==="false-value"&&(e._falseValue=s),fc(e,t,s,r))};function xc(e,t,n,s){return s?!!(t==="innerHTML"||t==="textContent"||t in e&&yr.test(t)&&K(n)):t==="spellcheck"||t==="draggable"||t==="translate"||t==="form"||t==="list"&&e.tagName==="INPUT"||t==="type"&&e.tagName==="TEXTAREA"||yr.test(t)&&ne(n)?!1:t in e}const Cc=["ctrl","shift","alt","meta"],Ec={stop:e=>e.stopPropagation(),prevent:e=>e.preventDefault(),self:e=>e.target!==e.currentTarget,ctrl:e=>!e.ctrlKey,shift:e=>!e.shiftKey,alt:e=>!e.altKey,meta:e=>!e.metaKey,left:e=>"button"in e&&e.button!==0,middle:e=>"button"in e&&e.button!==1,right:e=>"button"in e&&e.button!==2,exact:(e,t)=>Cc.some(n=>e[`${n}Key`]&&!t.includes(n))},Ma=(e,t)=>(n,...s)=>{for(let r=0;rn=>{if(!("key"in n))return;const s=at(n.key);if(t.some(r=>r===s||Tc[r]===s))return e(n)},Ac=oe({patchProp:wc},tc);let Wn,_r=!1;function Sc(){return Wn=_r?Wn:Nl(Ac),_r=!0,Wn}const La=(...e)=>{const t=Sc().createApp(...e),{mount:n}=t;return t.mount=s=>{const r=Oc(s);if(r)return n(r,!0,r instanceof SVGElement)},t};function Oc(e){return ne(e)?document.querySelector(e):e}const Na=(e,t)=>{const n=e.__vccOpts||e;for(const[s,r]of t)n[s]=r;return n},Rc="modulepreload",Pc=function(e){return"/"+e},br={},Ha=function(t,n,s){if(!n||n.length===0)return t();const r=document.getElementsByTagName("link");return Promise.all(n.map(i=>{if(i=Pc(i),i in br)return;br[i]=!0;const o=i.endsWith(".css"),l=o?'[rel="stylesheet"]':"";if(!!s)for(let d=r.length-1;d>=0;d--){const h=r[d];if(h.href===i&&(!o||h.rel==="stylesheet"))return}else if(document.querySelector(`link[href="${i}"]${l}`))return;const u=document.createElement("link");if(u.rel=o?"stylesheet":Rc,o||(u.as="script",u.crossOrigin=""),u.href=i,document.head.appendChild(u),o)return new Promise((d,h)=>{u.addEventListener("load",d),u.addEventListener("error",()=>h(new Error(`Unable to preload CSS for ${i}`)))})})).then(()=>t()).catch(i=>{const o=new Event("vite:preloadError",{cancelable:!0});if(o.payload=i,window.dispatchEvent(o),!o.defaultPrevented)throw i})},Ic=window.__VP_SITE_DATA__;function Ps(e){return Hr()?(po(e),!0):!1}function Je(e){return typeof e=="function"?e():ws(e)}function $a(e,t){const n=(t==null?void 0:t.computedGetter)===!1?ws:Je;return function(...s){return re(()=>e.apply(this,s.map(r=>n(r))))}}const Fi=typeof window<"u"&&typeof document<"u";typeof WorkerGlobalScope<"u"&&globalThis instanceof WorkerGlobalScope;const Mc=Object.prototype.toString,Fc=e=>Mc.call(e)==="[object Object]",Li=()=>{},vr=Lc();function Lc(){var e;return Fi&&((e=window==null?void 0:window.navigator)==null?void 0:e.userAgent)&&/iP(ad|hone|od)/.test(window.navigator.userAgent)}function Nc(e,t){function n(...s){return new Promise((r,i)=>{Promise.resolve(e(()=>t.apply(this,s),{fn:t,thisArg:this,args:s})).then(r).catch(i)})}return n}const Ni=e=>e();function Hc(e=Ni){const t=me(!0);function n(){t.value=!1}function s(){t.value=!0}const r=(...i)=>{t.value&&e(...i)};return{isActive:En(t),pause:n,resume:s,eventFilter:r}}function Hi(...e){if(e.length!==1)return Ko(...e);const t=e[0];return typeof t=="function"?En(Bo(()=>({get:t,set:Li}))):me(t)}function $c(e,t,n={}){const{eventFilter:s=Ni,...r}=n;return Ye(e,Nc(s,t),r)}function jc(e,t,n={}){const{eventFilter:s,...r}=n,{eventFilter:i,pause:o,resume:l,isActive:c}=Hc(s);return{stop:$c(e,t,{...r,eventFilter:i}),pause:o,resume:l,isActive:c}}function $i(e,t=!0){Ln()?At(e):t?e():An(e)}function ji(e){var t;const n=Je(e);return(t=n==null?void 0:n.$el)!=null?t:n}const Ct=Fi?window:void 0;function yn(...e){let t,n,s,r;if(typeof e[0]=="string"||Array.isArray(e[0])?([n,s,r]=e,t=Ct):[t,n,s,r]=e,!t)return Li;Array.isArray(n)||(n=[n]),Array.isArray(s)||(s=[s]);const i=[],o=()=>{i.forEach(d=>d()),i.length=0},l=(d,h,b,E)=>(d.addEventListener(h,b,E),()=>d.removeEventListener(h,b,E)),c=Ye(()=>[ji(t),Je(r)],([d,h])=>{if(o(),!d)return;const b=Fc(h)?{...h}:h;i.push(...n.flatMap(E=>s.map(F=>l(d,E,F,b))))},{immediate:!0,flush:"post"}),u=()=>{c(),o()};return Ps(u),u}function Dc(){const e=me(!1);return Ln()&&At(()=>{e.value=!0}),e}function Bc(e){const t=Dc();return re(()=>(t.value,!!e()))}function kc(e,t={}){const{window:n=Ct}=t,s=Bc(()=>n&&"matchMedia"in n&&typeof n.matchMedia=="function");let r;const i=me(!1),o=u=>{i.value=u.matches},l=()=>{r&&("removeEventListener"in r?r.removeEventListener("change",o):r.removeListener(o))},c=ri(()=>{s.value&&(l(),r=n.matchMedia(Je(e)),"addEventListener"in r?r.addEventListener("change",o):r.addListener(o),i.value=r.matches)});return Ps(()=>{c(),l(),r=void 0}),i}const en=typeof globalThis<"u"?globalThis:typeof window<"u"?window:typeof global<"u"?global:typeof self<"u"?self:{},tn="__vueuse_ssr_handlers__",Uc=Kc();function Kc(){return tn in en||(en[tn]=en[tn]||{}),en[tn]}function Di(e,t){return Uc[e]||t}function Wc(e){return e==null?"any":e instanceof Set?"set":e instanceof Map?"map":e instanceof Date?"date":typeof e=="boolean"?"boolean":typeof e=="string"?"string":typeof e=="object"?"object":Number.isNaN(e)?"any":"number"}const Vc={boolean:{read:e=>e==="true",write:e=>String(e)},object:{read:e=>JSON.parse(e),write:e=>JSON.stringify(e)},number:{read:e=>Number.parseFloat(e),write:e=>String(e)},any:{read:e=>e,write:e=>String(e)},string:{read:e=>e,write:e=>String(e)},map:{read:e=>new Map(JSON.parse(e)),write:e=>JSON.stringify(Array.from(e.entries()))},set:{read:e=>new Set(JSON.parse(e)),write:e=>JSON.stringify(Array.from(e))},date:{read:e=>new Date(e),write:e=>e.toISOString()}},wr="vueuse-storage";function qc(e,t,n,s={}){var r;const{flush:i="pre",deep:o=!0,listenToStorageChanges:l=!0,writeDefaults:c=!0,mergeDefaults:u=!1,shallow:d,window:h=Ct,eventFilter:b,onError:E=T=>{console.error(T)},initOnMounted:F}=s,R=(d?Yr:me)(typeof t=="function"?t():t);if(!n)try{n=Di("getDefaultStorage",()=>{var T;return(T=Ct)==null?void 0:T.localStorage})()}catch(T){E(T)}if(!n)return R;const j=Je(t),V=Wc(j),z=(r=s.serializer)!=null?r:Vc[V],{pause:m,resume:p}=jc(R,()=>M(R.value),{flush:i,deep:o,eventFilter:b});return h&&l&&$i(()=>{yn(h,"storage",P),yn(h,wr,B),F&&P()}),F||P(),R;function M(T){try{if(T==null)n.removeItem(e);else{const A=z.write(T),W=n.getItem(e);W!==A&&(n.setItem(e,A),h&&h.dispatchEvent(new CustomEvent(wr,{detail:{key:e,oldValue:W,newValue:A,storageArea:n}})))}}catch(A){E(A)}}function U(T){const A=T?T.newValue:n.getItem(e);if(A==null)return c&&j!==null&&n.setItem(e,z.write(j)),j;if(!T&&u){const W=z.read(A);return typeof u=="function"?u(W,j):V==="object"&&!Array.isArray(W)?{...j,...W}:W}else return typeof A!="string"?A:z.read(A)}function B(T){P(T.detail)}function P(T){if(!(T&&T.storageArea!==n)){if(T&&T.key==null){R.value=j;return}if(!(T&&T.key!==e)){m();try{(T==null?void 0:T.newValue)!==z.write(R.value)&&(R.value=U(T))}catch(A){E(A)}finally{T?An(p):p()}}}}}function zc(e){return kc("(prefers-color-scheme: dark)",e)}function Yc(e={}){const{selector:t="html",attribute:n="class",initialValue:s="auto",window:r=Ct,storage:i,storageKey:o="vueuse-color-scheme",listenToStorageChanges:l=!0,storageRef:c,emitAuto:u,disableTransition:d=!0}=e,h={auto:"",light:"light",dark:"dark",...e.modes||{}},b=zc({window:r}),E=re(()=>b.value?"dark":"light"),F=c||(o==null?Hi(s):qc(o,s,i,{window:r,listenToStorageChanges:l})),R=re(()=>F.value==="auto"?E.value:F.value),j=Di("updateHTMLAttrs",(p,M,U)=>{const B=typeof p=="string"?r==null?void 0:r.document.querySelector(p):ji(p);if(!B)return;let P;if(d){P=r.document.createElement("style");const T="*,*::before,*::after{-webkit-transition:none!important;-moz-transition:none!important;-o-transition:none!important;-ms-transition:none!important;transition:none!important}";P.appendChild(document.createTextNode(T)),r.document.head.appendChild(P)}if(M==="class"){const T=U.split(/\s/g);Object.values(h).flatMap(A=>(A||"").split(/\s/g)).filter(Boolean).forEach(A=>{T.includes(A)?B.classList.add(A):B.classList.remove(A)})}else B.setAttribute(M,U);d&&(r.getComputedStyle(P).opacity,document.head.removeChild(P))});function V(p){var M;j(t,n,(M=h[p])!=null?M:p)}function z(p){e.onChanged?e.onChanged(p,V):V(p)}Ye(R,z,{flush:"post",immediate:!0}),$i(()=>z(R.value));const m=re({get(){return u?F.value:R.value},set(p){F.value=p}});try{return Object.assign(m,{store:F,system:E,state:R})}catch{return m}}function Jc(e={}){const{valueDark:t="dark",valueLight:n=""}=e,s=Yc({...e,onChanged:(i,o)=>{var l;e.onChanged?(l=e.onChanged)==null||l.call(e,i==="dark",o,i):o(i)},modes:{dark:t,light:n}});return re({get(){return s.value==="dark"},set(i){const o=i?"dark":"light";s.system.value===o?s.value="auto":s.value=o}})}function Vn(e){return typeof Window<"u"&&e instanceof Window?e.document.documentElement:typeof Document<"u"&&e instanceof Document?e.documentElement:e}function Bi(e){const t=window.getComputedStyle(e);if(t.overflowX==="scroll"||t.overflowY==="scroll"||t.overflowX==="auto"&&e.clientWidth1?!0:(t.preventDefault&&t.preventDefault(),!1)}const nn=new WeakMap;function ja(e,t=!1){const n=me(t);let s=null,r;Ye(Hi(e),l=>{const c=Vn(Je(l));if(c){const u=c;nn.get(u)||nn.set(u,r),n.value&&(u.style.overflow="hidden")}},{immediate:!0});const i=()=>{const l=Vn(Je(e));!l||n.value||(vr&&(s=yn(l,"touchmove",c=>{Xc(c)},{passive:!1})),l.style.overflow="hidden",n.value=!0)},o=()=>{var l;const c=Vn(Je(e));!c||!n.value||(vr&&(s==null||s()),c.style.overflow=(l=nn.get(c))!=null?l:"",nn.delete(c),n.value=!1)};return Ps(o),re({get(){return n.value},set(l){l?i():o()}})}function Da(e={}){const{window:t=Ct,behavior:n="auto"}=e;if(!t)return{x:me(0),y:me(0)};const s=me(t.scrollX),r=me(t.scrollY),i=re({get(){return s.value},set(l){scrollTo({left:l,behavior:n})}}),o=re({get(){return r.value},set(l){scrollTo({top:l,behavior:n})}});return yn(t,"scroll",()=>{s.value=t.scrollX,r.value=t.scrollY},{capture:!1,passive:!0}),{x:i,y:o}}const ki=/^(?:[a-z]+:|\/\/)/i,Qc="vitepress-theme-appearance",Ui=/#.*$/,Zc=/(index)?\.(md|html)$/,Ce=typeof document<"u",Ki={relativePath:"",filePath:"",title:"404",description:"Not Found",headers:[],frontmatter:{sidebar:!1,layout:"page"},lastUpdated:0,isNotFound:!0};function Gc(e,t,n=!1){if(t===void 0)return!1;if(e=xr(`/${e}`),n)return new RegExp(t).test(e);if(xr(t)!==e)return!1;const s=t.match(Ui);return s?(Ce?location.hash:"")===s[0]:!0}function xr(e){return decodeURI(e).replace(Ui,"").replace(Zc,"")}function ea(e){return ki.test(e)}function ta(e,t){var s,r,i,o,l,c,u;const n=Object.keys(e.locales).find(d=>d!=="root"&&!ea(d)&&Gc(t,`/${d}/`,!0))||"root";return Object.assign({},e,{localeIndex:n,lang:((s=e.locales[n])==null?void 0:s.lang)??e.lang,dir:((r=e.locales[n])==null?void 0:r.dir)??e.dir,title:((i=e.locales[n])==null?void 0:i.title)??e.title,titleTemplate:((o=e.locales[n])==null?void 0:o.titleTemplate)??e.titleTemplate,description:((l=e.locales[n])==null?void 0:l.description)??e.description,head:Vi(e.head,((c=e.locales[n])==null?void 0:c.head)??[]),themeConfig:{...e.themeConfig,...(u=e.locales[n])==null?void 0:u.themeConfig}})}function Wi(e,t){const n=t.title||e.title,s=t.titleTemplate??e.titleTemplate;if(typeof s=="string"&&s.includes(":title"))return s.replace(/:title/g,n);const r=na(e.title,s);return`${n}${r}`}function na(e,t){return t===!1?"":t===!0||t===void 0?` | ${e}`:e===t?"":` | ${t}`}function sa(e,t){const[n,s]=t;if(n!=="meta")return!1;const r=Object.entries(s)[0];return r==null?!1:e.some(([i,o])=>i===n&&o[r[0]]===r[1])}function Vi(e,t){return[...e.filter(n=>!sa(t,n)),...t]}const ra=/[\u0000-\u001F"#$&*+,:;<=>?[\]^`{|}\u007F]/g,ia=/^[a-z]:/i;function Cr(e){const t=ia.exec(e),n=t?t[0]:"";return n+e.slice(n.length).replace(ra,"_").replace(/(^|\/)_+(?=[^/]*$)/,"$1")}const oa=Symbol(),lt=Yr(Ic);function Ba(e){const t=re(()=>ta(lt.value,e.data.relativePath)),n=t.value.appearance,s=n==="force-dark"?me(!0):n?Jc({storageKey:Qc,initialValue:()=>typeof n=="string"?n:"auto",...typeof n=="object"?n:{}}):me(!1);return{site:t,theme:re(()=>t.value.themeConfig),page:re(()=>e.data),frontmatter:re(()=>e.data.frontmatter),params:re(()=>e.data.params),lang:re(()=>t.value.lang),dir:re(()=>t.value.dir),localeIndex:re(()=>t.value.localeIndex||"root"),title:re(()=>Wi(t.value,e.data)),description:re(()=>e.data.description||t.value.description),isDark:s}}function la(){const e=bt(oa);if(!e)throw new Error("vitepress data not properly injected in app");return e}function ca(e,t){return`${e}${t}`.replace(/\/+/g,"/")}function Er(e){return ki.test(e)||!e.startsWith("/")?e:ca(lt.value.base,e)}function aa(e){let t=e.replace(/\.html$/,"");if(t=decodeURIComponent(t),t=t.replace(/\/$/,"/index"),Ce){const n="/";t=Cr(t.slice(n.length).replace(/\//g,"_")||"index")+".md";let s=__VP_HASH_MAP__[t.toLowerCase()];if(s||(t=t.endsWith("_index.md")?t.slice(0,-9)+".md":t.slice(0,-3)+"_index.md",s=__VP_HASH_MAP__[t.toLowerCase()]),!s)return null;t=`${n}assets/${t}.${s}.js`}else t=`./${Cr(t.slice(1).replace(/\//g,"_"))}.md.js`;return t}let ln=[];function ka(e){ln.push(e),Mn(()=>{ln=ln.filter(t=>t!==e)})}const ua=Symbol(),qi="http://a.com",fa=()=>({path:"/",component:null,data:Ki});function Ua(e,t){const n=Cn(fa()),s={route:n,go:r};async function r(l=Ce?location.href:"/"){var c,u;l=os(l),await((c=s.onBeforeRouteChange)==null?void 0:c.call(s,l))!==!1&&(Sr(l),await o(l),await((u=s.onAfterRouteChanged)==null?void 0:u.call(s,l)))}let i=null;async function o(l,c=0,u=!1){var b;if(await((b=s.onBeforePageLoad)==null?void 0:b.call(s,l))===!1)return;const d=new URL(l,qi),h=i=d.pathname;try{let E=await e(h);if(!E)throw new Error(`Page not found: ${h}`);if(i===h){i=null;const{default:F,__pageData:R}=E;if(!F)throw new Error(`Invalid route component: ${F}`);n.path=Ce?h:Er(h),n.component=Mt(F),n.data=Mt(R),Ce&&An(()=>{let j=lt.value.base+R.relativePath.replace(/(?:(^|\/)index)?\.md$/,"$1");if(!lt.value.cleanUrls&&!j.endsWith("/")&&(j+=".html"),j!==d.pathname&&(d.pathname=j,l=j+d.search+d.hash,history.replaceState(null,"",l)),d.hash&&!c){let V=null;try{V=document.getElementById(decodeURIComponent(d.hash).slice(1))}catch(z){console.warn(z)}if(V){Tr(V,d.hash);return}}window.scrollTo(0,c)})}}catch(E){if(!/fetch|Page not found/.test(E.message)&&!/^\/404(\.html|\/)?$/.test(l)&&console.error(E),!u)try{const F=await fetch(lt.value.base+"hashmap.json");window.__VP_HASH_MAP__=await F.json(),await o(l,c,!0);return}catch{}i===h&&(i=null,n.path=Ce?h:Er(h),n.component=t?Mt(t):null,n.data=Ki)}}return Ce&&(window.addEventListener("click",l=>{if(l.target.closest("button"))return;const u=l.target.closest("a");if(u&&!u.closest(".vp-raw")&&(u instanceof SVGElement||!u.download)){const{target:d}=u,{href:h,origin:b,pathname:E,hash:F,search:R}=new URL(u.href instanceof SVGAnimatedString?u.href.animVal:u.href,u.baseURI),j=window.location,V=E.match(/\.\w+$/);!l.ctrlKey&&!l.shiftKey&&!l.altKey&&!l.metaKey&&!d&&b===j.origin&&!(V&&V[0]!==".html")&&(l.preventDefault(),E===j.pathname&&R===j.search?(F!==j.hash&&(history.pushState(null,"",F),window.dispatchEvent(new Event("hashchange"))),F?Tr(u,F,u.classList.contains("header-anchor")):(Sr(h),window.scrollTo(0,0))):r(h))}},{capture:!0}),window.addEventListener("popstate",l=>{o(os(location.href),l.state&&l.state.scrollPosition||0)}),window.addEventListener("hashchange",l=>{l.preventDefault()})),s}function da(){const e=bt(ua);if(!e)throw new Error("useRouter() is called without provider.");return e}function zi(){return da().route}function Tr(e,t,n=!1){let s=null;try{s=e.classList.contains("header-anchor")?e:document.getElementById(decodeURIComponent(t).slice(1))}catch(r){console.warn(r)}if(s){let u=function(){!n||Math.abs(c-window.scrollY)>window.innerHeight?window.scrollTo(0,c):window.scrollTo({left:0,top:c,behavior:"smooth"})},r=lt.value.scrollOffset,i=0,o=24;if(typeof r=="object"&&"padding"in r&&(o=r.padding,r=r.selector),typeof r=="number")i=r;else if(typeof r=="string")i=Ar(r,o);else if(Array.isArray(r))for(const d of r){const h=Ar(d,o);if(h){i=h;break}}const l=parseInt(window.getComputedStyle(s).paddingTop,10),c=window.scrollY+s.getBoundingClientRect().top-i+l;requestAnimationFrame(u)}}function Ar(e,t){const n=document.querySelector(e);if(!n)return 0;const s=n.getBoundingClientRect().bottom;return s<0?0:s+t}function Sr(e){Ce&&e!==os(location.href)&&(history.replaceState({scrollPosition:window.scrollY},document.title),history.pushState(null,"",e))}function os(e){const t=new URL(e,qi);return t.pathname=t.pathname.replace(/(^|\/)index(\.html)?$/,"$1"),lt.value.cleanUrls?t.pathname=t.pathname.replace(/\.html$/,""):!t.pathname.endsWith("/")&&!t.pathname.endsWith(".html")&&(t.pathname+=".html"),t.pathname+t.search+t.hash}const Or=()=>ln.forEach(e=>e()),Ka=ai({name:"VitePressContent",props:{as:{type:[Object,String],default:"div"}},setup(e){const t=zi(),{site:n}=la();return()=>rs(e.as,n.value.contentProps??{style:{position:"relative"}},[t.component?rs(t.component,{onVnodeMounted:Or,onVnodeUpdated:Or}):"404 Page Not Found"])}}),Wa=ai({setup(e,{slots:t}){const n=me(!1);return At(()=>{n.value=!0}),()=>n.value&&t.default?t.default():null}});function Va(){Ce&&window.addEventListener("click",e=>{var n;const t=e.target;if(t.matches(".vp-code-group input")){const s=(n=t.parentElement)==null?void 0:n.parentElement;if(!s)return;const r=Array.from(s.querySelectorAll("input")).indexOf(t);if(r<0)return;const i=s.querySelector(".blocks");if(!i)return;const o=Array.from(i.children).find(u=>u.classList.contains("active"));if(!o)return;const l=i.children[r];if(!l||o===l)return;o.classList.remove("active"),l.classList.add("active");const c=s==null?void 0:s.querySelector(`label[for="${t.id}"]`);c==null||c.scrollIntoView({block:"nearest"})}})}function qa(){if(Ce){const e=new WeakMap;window.addEventListener("click",t=>{var s;const n=t.target;if(n.matches('div[class*="language-"] > button.copy')){const r=n.parentElement,i=(s=n.nextElementSibling)==null?void 0:s.nextElementSibling;if(!r||!i)return;const o=/language-(shellscript|shell|bash|sh|zsh)/.test(r.className);let l="";i.querySelectorAll("span.line:not(.diff.remove)").forEach(c=>l+=(c.textContent||"")+` +`),l=l.slice(0,-1),o&&(l=l.replace(/^ *(\$|>) /gm,"").trim()),ha(l).then(()=>{n.classList.add("copied"),clearTimeout(e.get(n));const c=setTimeout(()=>{n.classList.remove("copied"),n.blur(),e.delete(n)},2e3);e.set(n,c)})}})}}async function ha(e){try{return navigator.clipboard.writeText(e)}catch{const t=document.createElement("textarea"),n=document.activeElement;t.value=e,t.setAttribute("readonly",""),t.style.contain="strict",t.style.position="absolute",t.style.left="-9999px",t.style.fontSize="12pt";const s=document.getSelection(),r=s?s.rangeCount>0&&s.getRangeAt(0):null;document.body.appendChild(t),t.select(),t.selectionStart=0,t.selectionEnd=e.length,document.execCommand("copy"),document.body.removeChild(t),r&&(s.removeAllRanges(),s.addRange(r)),n&&n.focus()}}function za(e,t){let n=[],s=!0;const r=i=>{if(s){s=!1;return}const o=i.map(Rr);n.forEach((l,c)=>{const u=o.findIndex(d=>d==null?void 0:d.isEqualNode(l??null));u!==-1?delete o[u]:(l==null||l.remove(),delete n[c])}),o.forEach(l=>l&&document.head.appendChild(l)),n=[...n,...o].filter(Boolean)};ri(()=>{const i=e.data,o=t.value,l=i&&i.description,c=i&&i.frontmatter.head||[],u=Wi(o,i);u!==document.title&&(document.title=u);const d=l||o.description;let h=document.querySelector("meta[name=description]");h?h.getAttribute("content")!==d&&h.setAttribute("content",d):Rr(["meta",{name:"description",content:d}]),r(Vi(o.head,ga(c)))})}function Rr([e,t,n]){const s=document.createElement(e);for(const r in t)s.setAttribute(r,t[r]);return n&&(s.innerHTML=n),e==="script"&&!t.async&&(s.async=!1),s}function pa(e){return e[0]==="meta"&&e[1]&&e[1].name==="description"}function ga(e){return e.filter(t=>!pa(t))}const qn=new Set,Yi=()=>document.createElement("link"),ma=e=>{const t=Yi();t.rel="prefetch",t.href=e,document.head.appendChild(t)},ya=e=>{const t=new XMLHttpRequest;t.open("GET",e,t.withCredentials=!0),t.send()};let sn;const _a=Ce&&(sn=Yi())&&sn.relList&&sn.relList.supports&&sn.relList.supports("prefetch")?ma:ya;function Ya(){if(!Ce||!window.IntersectionObserver)return;let e;if((e=navigator.connection)&&(e.saveData||/2g/.test(e.effectiveType)))return;const t=window.requestIdleCallback||setTimeout;let n=null;const s=()=>{n&&n.disconnect(),n=new IntersectionObserver(i=>{i.forEach(o=>{if(o.isIntersecting){const l=o.target;n.unobserve(l);const{pathname:c}=l;if(!qn.has(c)){qn.add(c);const u=aa(c);u&&_a(u)}}})}),t(()=>{document.querySelectorAll("#app a").forEach(i=>{const{hostname:o,pathname:l}=new URL(i.href instanceof SVGAnimatedString?i.href.animVal:i.href,i.baseURI),c=l.match(/\.\w+$/);c&&c[0]!==".html"||i.target!=="_blank"&&o===location.hostname&&(l!==location.pathname?n.observe(i):qn.add(l))})})};At(s);const r=zi();Ye(()=>r.path,s),Mn(()=>{n&&n.disconnect()})}export{Oa as $,Ea as A,pl as B,xa as C,Ta as D,Yr as E,ge as F,ka as G,ue as H,Ca as I,ki as J,zi as K,Ul as L,bt as M,fs as N,An as O,Da as P,Pa as Q,En as R,$a as S,Ii as T,Ko as U,ja as V,Ol as W,Fa as X,Sa as Y,Ma as Z,Na as _,Si as a,za as a0,ua as a1,Ba as a2,oa as a3,Ka as a4,Wa as a5,lt as a6,La as a7,Ua as a8,aa as a9,Ha as aa,Ya as ab,qa as ac,Va as ad,rs as ae,Ei as b,Ra as c,ai as d,Ia as e,Er as f,re as g,me as h,ea as i,At as j,Ai as k,ws as l,wa as m,ds as n,xi as o,va as p,Gc as q,Aa as r,Ce as s,ba as t,la as u,kc as v,Go as w,Ye as x,ri as y,Mn as z}; diff --git a/assets/chunks/theme.4b73f3e8.js b/assets/chunks/theme.4b73f3e8.js new file mode 100644 index 000000000000..6ccdb7917805 --- /dev/null +++ b/assets/chunks/theme.4b73f3e8.js @@ -0,0 +1 @@ +import{d as g,o as a,c as i,r as u,n as N,a as x,t as L,_ as m,b as $,w as v,T as ce,e as f,u as He,i as ze,f as ue,g as k,h as S,j as G,k as c,l,p as H,m as z,q as O,s as K,v as re,x as U,y as te,z as de,A as Pe,B as De,C as j,F as M,D as A,E as _e,G as Y,H as h,I as E,J as Ve,K as se,L as Q,M as ne,N as Fe,O as Ee,P as we,Q as Oe,R as Ge,S as Ue,U as je,V as Le,W as Se,X as qe,Y as Re,Z as Ke,$ as We}from"./framework.ec8f7e8e.js";const Ye=g({__name:"VPBadge",props:{text:{},type:{default:"tip"}},setup(s){return(e,t)=>(a(),i("span",{class:N(["VPBadge",e.type])},[u(e.$slots,"default",{},()=>[x(L(e.text),1)],!0)],2))}});const Je=m(Ye,[["__scopeId","data-v-9613cc9f"]]),Ze={key:0,class:"VPBackdrop"},Qe=g({__name:"VPBackdrop",props:{show:{type:Boolean}},setup(s){return(e,t)=>(a(),$(ce,{name:"fade"},{default:v(()=>[e.show?(a(),i("div",Ze)):f("",!0)]),_:1}))}});const Xe=m(Qe,[["__scopeId","data-v-c79a1216"]]),P=He;function et(s,e){let t,n=!1;return()=>{t&&clearTimeout(t),n?t=setTimeout(s,e):(s(),(n=!0)&&setTimeout(()=>n=!1,e))}}function le(s){return/^\//.test(s)?s:`/${s}`}function J(s){const{pathname:e,search:t,hash:n,protocol:o}=new URL(s,"http://a.com");if(ze(s)||s.startsWith("#")||!o.startsWith("http")||/\.(?!html|md)\w+($|\?)/i.test(s))return s;const{site:r}=P(),d=e.endsWith("/")||e.endsWith(".html")?s:s.replace(/(?:(^\.+)\/)?.*$/,`$1${e.replace(/(\.md)?$/,r.value.cleanUrls?"":".html")}${t}${n}`);return ue(d)}function Z({removeCurrent:s=!0,correspondingLink:e=!1}={}){const{site:t,localeIndex:n,page:o,theme:r}=P(),d=k(()=>{var _,b;return{label:(_=t.value.locales[n.value])==null?void 0:_.label,link:((b=t.value.locales[n.value])==null?void 0:b.link)||(n.value==="root"?"/":`/${n.value}/`)}});return{localeLinks:k(()=>Object.entries(t.value.locales).flatMap(([_,b])=>s&&d.value.label===b.label?[]:{text:b.label,link:tt(b.link||(_==="root"?"/":`/${_}/`),r.value.i18nRouting!==!1&&e,o.value.relativePath.slice(d.value.link.length-1),!t.value.cleanUrls)})),currentLang:d}}function tt(s,e,t,n){return e?s.replace(/\/$/,"")+le(t.replace(/(^|\/)index\.md$/,"$1").replace(/\.md$/,n?".html":"")):s}const st=s=>(H("data-v-f87ff6e4"),s=s(),z(),s),nt={class:"NotFound"},ot={class:"code"},at={class:"title"},rt=st(()=>c("div",{class:"divider"},null,-1)),lt={class:"quote"},it={class:"action"},ct=["href","aria-label"],ut=g({__name:"NotFound",setup(s){const{site:e,theme:t}=P(),{localeLinks:n}=Z({removeCurrent:!1}),o=S("/");return G(()=>{var d;const r=window.location.pathname.replace(e.value.base,"").replace(/(^.*?\/).*$/,"/$1");n.value.length&&(o.value=((d=n.value.find(({link:p})=>p.startsWith(r)))==null?void 0:d.link)||n.value[0].link)}),(r,d)=>{var p,_,b,y,w;return a(),i("div",nt,[c("p",ot,L(((p=l(t).notFound)==null?void 0:p.code)??"404"),1),c("h1",at,L(((_=l(t).notFound)==null?void 0:_.title)??"PAGE NOT FOUND"),1),rt,c("blockquote",lt,L(((b=l(t).notFound)==null?void 0:b.quote)??"But if you don't change your direction, and if you keep looking, you may end up where you are heading."),1),c("div",it,[c("a",{class:"link",href:l(ue)(o.value),"aria-label":((y=l(t).notFound)==null?void 0:y.linkLabel)??"go to home"},L(((w=l(t).notFound)==null?void 0:w.linkText)??"Take me home"),9,ct)])])}}});const dt=m(ut,[["__scopeId","data-v-f87ff6e4"]]);function Me(s,e){if(Array.isArray(s))return X(s);if(s==null)return[];e=le(e);const t=Object.keys(s).sort((o,r)=>r.split("/").length-o.split("/").length).find(o=>e.startsWith(le(o))),n=t?s[t]:[];return Array.isArray(n)?X(n):X(n.items,n.base)}function _t(s){const e=[];let t=0;for(const n in s){const o=s[n];if(o.items){t=e.push(o);continue}e[t]||e.push({items:[]}),e[t].items.push(o)}return e}function vt(s){const e=[];function t(n){for(const o of n)o.text&&o.link&&e.push({text:o.text,link:o.link,docFooterText:o.docFooterText}),o.items&&t(o.items)}return t(s),e}function ie(s,e){return Array.isArray(e)?e.some(t=>ie(s,t)):O(s,e.link)?!0:e.items?ie(s,e.items):!1}function X(s,e){return[...s].map(t=>{const n={...t},o=n.base||e;return o&&n.link&&(n.link=o+n.link),n.items&&(n.items=X(n.items,o)),n})}function D(){const{frontmatter:s,page:e,theme:t}=P(),n=re("(min-width: 960px)"),o=S(!1),r=k(()=>{const T=t.value.sidebar,V=e.value.relativePath;return T?Me(T,V):[]}),d=S(r.value);U(r,(T,V)=>{JSON.stringify(T)!==JSON.stringify(V)&&(d.value=r.value)});const p=k(()=>s.value.sidebar!==!1&&d.value.length>0&&s.value.layout!=="home"),_=k(()=>b?s.value.aside==null?t.value.aside==="left":s.value.aside==="left":!1),b=k(()=>s.value.layout==="home"?!1:s.value.aside!=null?!!s.value.aside:t.value.aside!==!1),y=k(()=>p.value&&n.value),w=k(()=>p.value?_t(d.value):[]);function C(){o.value=!0}function I(){o.value=!1}function B(){o.value?I():C()}return{isOpen:o,sidebar:d,sidebarGroups:w,hasSidebar:p,hasAside:b,leftAside:_,isSidebarEnabled:y,open:C,close:I,toggle:B}}function pt(s,e){let t;te(()=>{t=s.value?document.activeElement:void 0}),G(()=>{window.addEventListener("keyup",n)}),de(()=>{window.removeEventListener("keyup",n)});function n(o){o.key==="Escape"&&s.value&&(e(),t==null||t.focus())}}const Ne=S(K?location.hash:"");K&&window.addEventListener("hashchange",()=>{Ne.value=location.hash});function ht(s){const{page:e}=P(),t=S(!1),n=k(()=>s.value.collapsed!=null),o=k(()=>!!s.value.link),r=S(!1),d=()=>{r.value=O(e.value.relativePath,s.value.link)};U([e,s,Ne],d),G(d);const p=k(()=>r.value?!0:s.value.items?ie(e.value.relativePath,s.value.items):!1),_=k(()=>!!(s.value.items&&s.value.items.length));te(()=>{t.value=!!(n.value&&s.value.collapsed)}),Pe(()=>{(r.value||p.value)&&(t.value=!1)});function b(){n.value&&(t.value=!t.value)}return{collapsed:t,collapsible:n,isLink:o,isActiveLink:r,hasActiveLink:p,hasChildren:_,toggle:b}}function ft(){const{hasSidebar:s}=D(),e=re("(min-width: 960px)"),t=re("(min-width: 1280px)");return{isAsideEnabled:k(()=>!t.value&&!e.value?!1:s.value?t.value:e.value)}}const mt=71;function ve(s){return typeof s.outline=="object"&&!Array.isArray(s.outline)&&s.outline.label||s.outlineTitle||"On this page"}function pe(s){const e=[...document.querySelectorAll(".VPDoc :where(h1,h2,h3,h4,h5,h6)")].filter(t=>t.id&&t.hasChildNodes()).map(t=>{const n=Number(t.tagName[1]);return{title:gt(t),link:"#"+t.id,level:n}});return bt(e,s)}function gt(s){let e="";for(const t of s.childNodes)if(t.nodeType===1){if(t.classList.contains("VPBadge")||t.classList.contains("header-anchor"))continue;e+=t.textContent}else t.nodeType===3&&(e+=t.textContent);return e.trim()}function bt(s,e){if(e===!1)return[];const t=(typeof e=="object"&&!Array.isArray(e)?e.level:e)||2,[n,o]=typeof t=="number"?[t,t]:t==="deep"?[2,6]:t;s=s.filter(d=>d.level>=n&&d.level<=o);const r=[];e:for(let d=0;d=0;_--){const b=s[_];if(b.level{requestAnimationFrame(r),window.addEventListener("scroll",n)}),De(()=>{d(location.hash)}),de(()=>{window.removeEventListener("scroll",n)});function r(){if(!t.value)return;const p=[].slice.call(s.value.querySelectorAll(".outline-link")),_=[].slice.call(document.querySelectorAll(".content .header-anchor")).filter(I=>p.some(B=>B.hash===I.hash&&I.offsetParent!==null)),b=window.scrollY,y=window.innerHeight,w=document.body.offsetHeight,C=Math.abs(b+y-w)<1;if(_.length&&C){d(_[_.length-1].hash);return}for(let I=0;I<_.length;I++){const B=_[I],T=_[I+1],[V,q]=kt(I,B,T);if(V){d(q);return}}}function d(p){o&&o.classList.remove("active"),p==null?o=null:o=s.value.querySelector(`a[href="${decodeURIComponent(p)}"]`);const _=o;_?(_.classList.add("active"),e.value.style.top=_.offsetTop+33+"px",e.value.style.opacity="1"):(e.value.style.top="33px",e.value.style.opacity="0")}}function ke(s){return s.parentElement.offsetTop-mt}function kt(s,e,t){const n=window.scrollY;return s===0&&n===0?[!0,null]:n{const o=j("VPDocOutlineItem",!0);return a(),i("ul",{class:N(t.root?"root":"nested")},[(a(!0),i(M,null,A(t.headers,({children:r,link:d,title:p})=>(a(),i("li",null,[c("a",{class:"outline-link",href:d,onClick:e,title:p},L(p),9,yt),r!=null&&r.length?(a(),$(o,{key:0,headers:r},null,8,["headers"])):f("",!0)]))),256))],2)}}});const he=m(Pt,[["__scopeId","data-v-d0ee3533"]]),Vt=s=>(H("data-v-d330b1bb"),s=s(),z(),s),wt={class:"content"},Lt={class:"outline-title",role:"heading","aria-level":"2"},St={"aria-labelledby":"doc-outline-aria-label"},Mt=Vt(()=>c("span",{class:"visually-hidden",id:"doc-outline-aria-label"}," Table of Contents for current page ",-1)),Nt=g({__name:"VPDocAsideOutline",setup(s){const{frontmatter:e,theme:t}=P(),n=_e([]);Y(()=>{n.value=pe(e.value.outline??t.value.outline)});const o=S(),r=S();return $t(o,r),(d,p)=>(a(),i("div",{class:N(["VPDocAsideOutline",{"has-outline":n.value.length>0}]),ref_key:"container",ref:o,role:"navigation"},[c("div",wt,[c("div",{class:"outline-marker",ref_key:"marker",ref:r},null,512),c("div",Lt,L(l(ve)(l(t))),1),c("nav",St,[Mt,h(he,{headers:n.value,root:!0},null,8,["headers"])])])],2))}});const It=m(Nt,[["__scopeId","data-v-d330b1bb"]]),Ct={class:"VPDocAsideCarbonAds"},Tt=g({__name:"VPDocAsideCarbonAds",props:{carbonAds:{}},setup(s){const e=()=>null;return(t,n)=>(a(),i("div",Ct,[h(l(e),{"carbon-ads":t.carbonAds},null,8,["carbon-ads"])]))}}),Bt=s=>(H("data-v-3f215769"),s=s(),z(),s),At={class:"VPDocAside"},xt=Bt(()=>c("div",{class:"spacer"},null,-1)),Ht=g({__name:"VPDocAside",setup(s){const{theme:e}=P();return(t,n)=>(a(),i("div",At,[u(t.$slots,"aside-top",{},void 0,!0),u(t.$slots,"aside-outline-before",{},void 0,!0),h(It),u(t.$slots,"aside-outline-after",{},void 0,!0),xt,u(t.$slots,"aside-ads-before",{},void 0,!0),l(e).carbonAds?(a(),$(Tt,{key:0,"carbon-ads":l(e).carbonAds},null,8,["carbon-ads"])):f("",!0),u(t.$slots,"aside-ads-after",{},void 0,!0),u(t.$slots,"aside-bottom",{},void 0,!0)]))}});const zt=m(Ht,[["__scopeId","data-v-3f215769"]]);function Dt(){const{theme:s,page:e}=P();return k(()=>{const{text:t="Edit this page",pattern:n=""}=s.value.editLink||{};let o;return typeof n=="function"?o=n(e.value):o=n.replace(/:path/g,e.value.filePath),{url:o,text:t}})}function Ft(){const{page:s,theme:e,frontmatter:t}=P();return k(()=>{var _,b,y,w,C,I,B,T;const n=Me(e.value.sidebar,s.value.relativePath),o=vt(n),r=o.findIndex(V=>O(s.value.relativePath,V.link)),d=((_=e.value.docFooter)==null?void 0:_.prev)===!1&&!t.value.prev||t.value.prev===!1,p=((b=e.value.docFooter)==null?void 0:b.next)===!1&&!t.value.next||t.value.next===!1;return{prev:d?void 0:{text:(typeof t.value.prev=="string"?t.value.prev:typeof t.value.prev=="object"?t.value.prev.text:void 0)??((y=o[r-1])==null?void 0:y.docFooterText)??((w=o[r-1])==null?void 0:w.text),link:(typeof t.value.prev=="object"?t.value.prev.link:void 0)??((C=o[r-1])==null?void 0:C.link)},next:p?void 0:{text:(typeof t.value.next=="string"?t.value.next:typeof t.value.next=="object"?t.value.next.text:void 0)??((I=o[r+1])==null?void 0:I.docFooterText)??((B=o[r+1])==null?void 0:B.text),link:(typeof t.value.next=="object"?t.value.next.link:void 0)??((T=o[r+1])==null?void 0:T.link)}}})}const Et={},Ot={xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 24 24"},Gt=c("path",{d:"M18,23H4c-1.7,0-3-1.3-3-3V6c0-1.7,1.3-3,3-3h7c0.6,0,1,0.4,1,1s-0.4,1-1,1H4C3.4,5,3,5.4,3,6v14c0,0.6,0.4,1,1,1h14c0.6,0,1-0.4,1-1v-7c0-0.6,0.4-1,1-1s1,0.4,1,1v7C21,21.7,19.7,23,18,23z"},null,-1),Ut=c("path",{d:"M8,17c-0.3,0-0.5-0.1-0.7-0.3C7,16.5,6.9,16.1,7,15.8l1-4c0-0.2,0.1-0.3,0.3-0.5l9.5-9.5c1.2-1.2,3.2-1.2,4.4,0c1.2,1.2,1.2,3.2,0,4.4l-9.5,9.5c-0.1,0.1-0.3,0.2-0.5,0.3l-4,1C8.2,17,8.1,17,8,17zM9.9,12.5l-0.5,2.1l2.1-0.5l9.3-9.3c0.4-0.4,0.4-1.1,0-1.6c-0.4-0.4-1.2-0.4-1.6,0l0,0L9.9,12.5z M18.5,2.5L18.5,2.5L18.5,2.5z"},null,-1),jt=[Gt,Ut];function qt(s,e){return a(),i("svg",Ot,jt)}const Rt=m(Et,[["render",qt]]),F=g({__name:"VPLink",props:{tag:{},href:{},noIcon:{type:Boolean},target:{},rel:{}},setup(s){const e=s,t=k(()=>e.tag??(e.href?"a":"span")),n=k(()=>e.href&&Ve.test(e.href));return(o,r)=>(a(),$(E(t.value),{class:N(["VPLink",{link:o.href,"vp-external-link-icon":n.value,"no-icon":o.noIcon}]),href:o.href?l(J)(o.href):void 0,target:o.target??(n.value?"_blank":void 0),rel:o.rel??(n.value?"noreferrer":void 0)},{default:v(()=>[u(o.$slots,"default")]),_:3},8,["class","href","target","rel"]))}}),Kt={class:"VPLastUpdated"},Wt=["datetime"],Yt=g({__name:"VPDocFooterLastUpdated",setup(s){const{theme:e,page:t,frontmatter:n,lang:o}=P(),r=k(()=>new Date(n.value.lastUpdated??t.value.lastUpdated)),d=k(()=>r.value.toISOString()),p=S("");return G(()=>{te(()=>{var _,b,y;p.value=new Intl.DateTimeFormat((b=(_=e.value.lastUpdated)==null?void 0:_.formatOptions)!=null&&b.forceLocale?o.value:void 0,((y=e.value.lastUpdated)==null?void 0:y.formatOptions)??{dateStyle:"short",timeStyle:"short"}).format(r.value)})}),(_,b)=>{var y;return a(),i("p",Kt,[x(L(((y=l(e).lastUpdated)==null?void 0:y.text)||l(e).lastUpdatedText||"Last updated")+": ",1),c("time",{datetime:d.value},L(p.value),9,Wt)])}}});const Jt=m(Yt,[["__scopeId","data-v-7e05ebdb"]]),Zt={key:0,class:"VPDocFooter"},Qt={key:0,class:"edit-info"},Xt={key:0,class:"edit-link"},es={key:1,class:"last-updated"},ts={key:1,class:"prev-next"},ss={class:"pager"},ns=["href"],os=["innerHTML"],as=["innerHTML"],rs={class:"pager"},ls=["href"],is=["innerHTML"],cs=["innerHTML"],us=g({__name:"VPDocFooter",setup(s){const{theme:e,page:t,frontmatter:n}=P(),o=Dt(),r=Ft(),d=k(()=>e.value.editLink&&n.value.editLink!==!1),p=k(()=>t.value.lastUpdated&&n.value.lastUpdated!==!1),_=k(()=>d.value||p.value||r.value.prev||r.value.next);return(b,y)=>{var w,C,I,B,T,V;return _.value?(a(),i("footer",Zt,[u(b.$slots,"doc-footer-before",{},void 0,!0),d.value||p.value?(a(),i("div",Qt,[d.value?(a(),i("div",Xt,[h(F,{class:"edit-link-button",href:l(o).url,"no-icon":!0},{default:v(()=>[h(Rt,{class:"edit-link-icon","aria-label":"edit icon"}),x(" "+L(l(o).text),1)]),_:1},8,["href"])])):f("",!0),p.value?(a(),i("div",es,[h(Jt)])):f("",!0)])):f("",!0),(w=l(r).prev)!=null&&w.link||(C=l(r).next)!=null&&C.link?(a(),i("nav",ts,[c("div",ss,[(I=l(r).prev)!=null&&I.link?(a(),i("a",{key:0,class:"pager-link prev",href:l(J)(l(r).prev.link)},[c("span",{class:"desc",innerHTML:((B=l(e).docFooter)==null?void 0:B.prev)||"Previous page"},null,8,os),c("span",{class:"title",innerHTML:l(r).prev.text},null,8,as)],8,ns)):f("",!0)]),c("div",rs,[(T=l(r).next)!=null&&T.link?(a(),i("a",{key:0,class:"pager-link next",href:l(J)(l(r).next.link)},[c("span",{class:"desc",innerHTML:((V=l(e).docFooter)==null?void 0:V.next)||"Next page"},null,8,is),c("span",{class:"title",innerHTML:l(r).next.text},null,8,cs)],8,ls)):f("",!0)])])):f("",!0)])):f("",!0)}}});const ds=m(us,[["__scopeId","data-v-ef5dee53"]]),_s={},vs={xmlns:"http://www.w3.org/2000/svg","aria-hidden":"true",focusable:"false",viewBox:"0 0 24 24"},ps=c("path",{d:"M9,19c-0.3,0-0.5-0.1-0.7-0.3c-0.4-0.4-0.4-1,0-1.4l5.3-5.3L8.3,6.7c-0.4-0.4-0.4-1,0-1.4s1-0.4,1.4,0l6,6c0.4,0.4,0.4,1,0,1.4l-6,6C9.5,18.9,9.3,19,9,19z"},null,-1),hs=[ps];function fs(s,e){return a(),i("svg",vs,hs)}const fe=m(_s,[["render",fs]]),ms={key:0,class:"VPDocOutlineDropdown"},gs={key:0,class:"items"},bs=g({__name:"VPDocOutlineDropdown",setup(s){const{frontmatter:e,theme:t}=P(),n=S(!1);Y(()=>{n.value=!1});const o=_e([]);return Y(()=>{o.value=pe(e.value.outline??t.value.outline)}),(r,d)=>o.value.length>0?(a(),i("div",ms,[c("button",{onClick:d[0]||(d[0]=p=>n.value=!n.value),class:N({open:n.value})},[x(L(l(ve)(l(t)))+" ",1),h(fe,{class:"icon"})],2),n.value?(a(),i("div",gs,[h(he,{headers:o.value},null,8,["headers"])])):f("",!0)])):f("",!0)}});const $s=m(bs,[["__scopeId","data-v-eadfb36b"]]),ks=s=>(H("data-v-6b87e69f"),s=s(),z(),s),ys={class:"container"},Ps=ks(()=>c("div",{class:"aside-curtain"},null,-1)),Vs={class:"aside-container"},ws={class:"aside-content"},Ls={class:"content"},Ss={class:"content-container"},Ms={class:"main"},Ns=g({__name:"VPDoc",setup(s){const{theme:e}=P(),t=se(),{hasSidebar:n,hasAside:o,leftAside:r}=D(),d=k(()=>t.path.replace(/[./]+/g,"_").replace(/_html$/,""));return(p,_)=>{const b=j("Content");return a(),i("div",{class:N(["VPDoc",{"has-sidebar":l(n),"has-aside":l(o)}])},[u(p.$slots,"doc-top",{},void 0,!0),c("div",ys,[l(o)?(a(),i("div",{key:0,class:N(["aside",{"left-aside":l(r)}])},[Ps,c("div",Vs,[c("div",ws,[h(zt,null,{"aside-top":v(()=>[u(p.$slots,"aside-top",{},void 0,!0)]),"aside-bottom":v(()=>[u(p.$slots,"aside-bottom",{},void 0,!0)]),"aside-outline-before":v(()=>[u(p.$slots,"aside-outline-before",{},void 0,!0)]),"aside-outline-after":v(()=>[u(p.$slots,"aside-outline-after",{},void 0,!0)]),"aside-ads-before":v(()=>[u(p.$slots,"aside-ads-before",{},void 0,!0)]),"aside-ads-after":v(()=>[u(p.$slots,"aside-ads-after",{},void 0,!0)]),_:3})])])],2)):f("",!0),c("div",Ls,[c("div",Ss,[u(p.$slots,"doc-before",{},void 0,!0),h($s),c("main",Ms,[h(b,{class:N(["vp-doc",[d.value,l(e).externalLinkIcon&&"external-link-icon-enabled"]])},null,8,["class"])]),h(ds,null,{"doc-footer-before":v(()=>[u(p.$slots,"doc-footer-before",{},void 0,!0)]),_:3}),u(p.$slots,"doc-after",{},void 0,!0)])])]),u(p.$slots,"doc-bottom",{},void 0,!0)],2)}}});const Is=m(Ns,[["__scopeId","data-v-6b87e69f"]]),Cs=g({__name:"VPButton",props:{tag:{},size:{default:"medium"},theme:{default:"brand"},text:{},href:{}},setup(s){const e=s,t=k(()=>e.href&&Ve.test(e.href)),n=k(()=>e.tag||e.href?"a":"button");return(o,r)=>(a(),$(E(n.value),{class:N(["VPButton",[o.size,o.theme]]),href:o.href?l(J)(o.href):void 0,target:t.value?"_blank":void 0,rel:t.value?"noreferrer":void 0},{default:v(()=>[x(L(o.text),1)]),_:1},8,["class","href","target","rel"]))}});const Ts=m(Cs,[["__scopeId","data-v-c1c5efc1"]]),Bs=["src","alt"],As=g({inheritAttrs:!1,__name:"VPImage",props:{image:{},alt:{}},setup(s){return(e,t)=>{const n=j("VPImage",!0);return e.image?(a(),i(M,{key:0},[typeof e.image=="string"||"src"in e.image?(a(),i("img",Q({key:0,class:"VPImage"},typeof e.image=="string"?e.$attrs:{...e.image,...e.$attrs},{src:l(ue)(typeof e.image=="string"?e.image:e.image.src),alt:e.alt??(typeof e.image=="string"?"":e.image.alt||"")}),null,16,Bs)):(a(),i(M,{key:1},[h(n,Q({class:"dark",image:e.image.dark,alt:e.image.alt},e.$attrs),null,16,["image","alt"]),h(n,Q({class:"light",image:e.image.light,alt:e.image.alt},e.$attrs),null,16,["image","alt"])],64))],64)):f("",!0)}}});const ee=m(As,[["__scopeId","data-v-8426fc1a"]]),xs=s=>(H("data-v-da5d1713"),s=s(),z(),s),Hs={class:"container"},zs={class:"main"},Ds={key:0,class:"name"},Fs=["innerHTML"],Es=["innerHTML"],Os=["innerHTML"],Gs={key:0,class:"actions"},Us={key:0,class:"image"},js={class:"image-container"},qs=xs(()=>c("div",{class:"image-bg"},null,-1)),Rs=g({__name:"VPHero",props:{name:{},text:{},tagline:{},image:{},actions:{}},setup(s){const e=ne("hero-image-slot-exists");return(t,n)=>(a(),i("div",{class:N(["VPHero",{"has-image":t.image||l(e)}])},[c("div",Hs,[c("div",zs,[u(t.$slots,"home-hero-info",{},()=>[t.name?(a(),i("h1",Ds,[c("span",{innerHTML:t.name,class:"clip"},null,8,Fs)])):f("",!0),t.text?(a(),i("p",{key:1,innerHTML:t.text,class:"text"},null,8,Es)):f("",!0),t.tagline?(a(),i("p",{key:2,innerHTML:t.tagline,class:"tagline"},null,8,Os)):f("",!0)],!0),t.actions?(a(),i("div",Gs,[(a(!0),i(M,null,A(t.actions,o=>(a(),i("div",{key:o.link,class:"action"},[h(Ts,{tag:"a",size:"medium",theme:o.theme,text:o.text,href:o.link},null,8,["theme","text","href"])]))),128))])):f("",!0)]),t.image||l(e)?(a(),i("div",Us,[c("div",js,[qs,u(t.$slots,"home-hero-image",{},()=>[t.image?(a(),$(ee,{key:0,class:"image-src",image:t.image},null,8,["image"])):f("",!0)],!0)])])):f("",!0)])],2))}});const Ks=m(Rs,[["__scopeId","data-v-da5d1713"]]),Ws=g({__name:"VPHomeHero",setup(s){const{frontmatter:e}=P();return(t,n)=>l(e).hero?(a(),$(Ks,{key:0,class:"VPHomeHero",name:l(e).hero.name,text:l(e).hero.text,tagline:l(e).hero.tagline,image:l(e).hero.image,actions:l(e).hero.actions},{"home-hero-info":v(()=>[u(t.$slots,"home-hero-info")]),"home-hero-image":v(()=>[u(t.$slots,"home-hero-image")]),_:3},8,["name","text","tagline","image","actions"])):f("",!0)}}),Ys={},Js={xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 24 24"},Zs=c("path",{d:"M19.9,12.4c0.1-0.2,0.1-0.5,0-0.8c-0.1-0.1-0.1-0.2-0.2-0.3l-7-7c-0.4-0.4-1-0.4-1.4,0s-0.4,1,0,1.4l5.3,5.3H5c-0.6,0-1,0.4-1,1s0.4,1,1,1h11.6l-5.3,5.3c-0.4,0.4-0.4,1,0,1.4c0.2,0.2,0.5,0.3,0.7,0.3s0.5-0.1,0.7-0.3l7-7C19.8,12.6,19.9,12.5,19.9,12.4z"},null,-1),Qs=[Zs];function Xs(s,e){return a(),i("svg",Js,Qs)}const en=m(Ys,[["render",Xs]]),tn={class:"box"},sn={key:0,class:"icon"},nn=["innerHTML"],on=["innerHTML"],an=["innerHTML"],rn={key:4,class:"link-text"},ln={class:"link-text-value"},cn=g({__name:"VPFeature",props:{icon:{},title:{},details:{},link:{},linkText:{},rel:{},target:{}},setup(s){return(e,t)=>(a(),$(F,{class:"VPFeature",href:e.link,rel:e.rel,target:e.target,"no-icon":!0,tag:e.link?"a":"div"},{default:v(()=>[c("article",tn,[typeof e.icon=="object"&&e.icon.wrap?(a(),i("div",sn,[h(ee,{image:e.icon,alt:e.icon.alt,height:e.icon.height||48,width:e.icon.width||48},null,8,["image","alt","height","width"])])):typeof e.icon=="object"?(a(),$(ee,{key:1,image:e.icon,alt:e.icon.alt,height:e.icon.height||48,width:e.icon.width||48},null,8,["image","alt","height","width"])):e.icon?(a(),i("div",{key:2,class:"icon",innerHTML:e.icon},null,8,nn)):f("",!0),c("h2",{class:"title",innerHTML:e.title},null,8,on),e.details?(a(),i("p",{key:3,class:"details",innerHTML:e.details},null,8,an)):f("",!0),e.linkText?(a(),i("div",rn,[c("p",ln,[x(L(e.linkText)+" ",1),h(en,{class:"link-text-icon"})])])):f("",!0)])]),_:1},8,["href","rel","target","tag"]))}});const un=m(cn,[["__scopeId","data-v-33204567"]]),dn={key:0,class:"VPFeatures"},_n={class:"container"},vn={class:"items"},pn=g({__name:"VPFeatures",props:{features:{}},setup(s){const e=s,t=k(()=>{const n=e.features.length;if(n){if(n===2)return"grid-2";if(n===3)return"grid-3";if(n%3===0)return"grid-6";if(n>3)return"grid-4"}else return});return(n,o)=>n.features?(a(),i("div",dn,[c("div",_n,[c("div",vn,[(a(!0),i(M,null,A(n.features,r=>(a(),i("div",{key:r.title,class:N(["item",[t.value]])},[h(un,{icon:r.icon,title:r.title,details:r.details,link:r.link,"link-text":r.linkText,rel:r.rel,target:r.target},null,8,["icon","title","details","link","link-text","rel","target"])],2))),128))])])])):f("",!0)}});const hn=m(pn,[["__scopeId","data-v-a6181336"]]),fn=g({__name:"VPHomeFeatures",setup(s){const{frontmatter:e}=P();return(t,n)=>l(e).features?(a(),$(hn,{key:0,class:"VPHomeFeatures",features:l(e).features},null,8,["features"])):f("",!0)}}),mn={class:"VPHome"},gn=g({__name:"VPHome",setup(s){return(e,t)=>{const n=j("Content");return a(),i("div",mn,[u(e.$slots,"home-hero-before",{},void 0,!0),h(Ws,null,{"home-hero-info":v(()=>[u(e.$slots,"home-hero-info",{},void 0,!0)]),"home-hero-image":v(()=>[u(e.$slots,"home-hero-image",{},void 0,!0)]),_:3}),u(e.$slots,"home-hero-after",{},void 0,!0),u(e.$slots,"home-features-before",{},void 0,!0),h(fn),u(e.$slots,"home-features-after",{},void 0,!0),h(n)])}}});const bn=m(gn,[["__scopeId","data-v-d82743a8"]]),$n={},kn={class:"VPPage"};function yn(s,e){const t=j("Content");return a(),i("div",kn,[u(s.$slots,"page-top"),h(t),u(s.$slots,"page-bottom")])}const Pn=m($n,[["render",yn]]),Vn=g({__name:"VPContent",setup(s){const{page:e,frontmatter:t}=P(),{hasSidebar:n}=D();return(o,r)=>(a(),i("div",{class:N(["VPContent",{"has-sidebar":l(n),"is-home":l(t).layout==="home"}]),id:"VPContent"},[l(e).isNotFound?u(o.$slots,"not-found",{key:0},()=>[h(dt)],!0):l(t).layout==="page"?(a(),$(Pn,{key:1},{"page-top":v(()=>[u(o.$slots,"page-top",{},void 0,!0)]),"page-bottom":v(()=>[u(o.$slots,"page-bottom",{},void 0,!0)]),_:3})):l(t).layout==="home"?(a(),$(bn,{key:2},{"home-hero-before":v(()=>[u(o.$slots,"home-hero-before",{},void 0,!0)]),"home-hero-info":v(()=>[u(o.$slots,"home-hero-info",{},void 0,!0)]),"home-hero-image":v(()=>[u(o.$slots,"home-hero-image",{},void 0,!0)]),"home-hero-after":v(()=>[u(o.$slots,"home-hero-after",{},void 0,!0)]),"home-features-before":v(()=>[u(o.$slots,"home-features-before",{},void 0,!0)]),"home-features-after":v(()=>[u(o.$slots,"home-features-after",{},void 0,!0)]),_:3})):l(t).layout&&l(t).layout!=="doc"?(a(),$(E(l(t).layout),{key:3})):(a(),$(Is,{key:4},{"doc-top":v(()=>[u(o.$slots,"doc-top",{},void 0,!0)]),"doc-bottom":v(()=>[u(o.$slots,"doc-bottom",{},void 0,!0)]),"doc-footer-before":v(()=>[u(o.$slots,"doc-footer-before",{},void 0,!0)]),"doc-before":v(()=>[u(o.$slots,"doc-before",{},void 0,!0)]),"doc-after":v(()=>[u(o.$slots,"doc-after",{},void 0,!0)]),"aside-top":v(()=>[u(o.$slots,"aside-top",{},void 0,!0)]),"aside-outline-before":v(()=>[u(o.$slots,"aside-outline-before",{},void 0,!0)]),"aside-outline-after":v(()=>[u(o.$slots,"aside-outline-after",{},void 0,!0)]),"aside-ads-before":v(()=>[u(o.$slots,"aside-ads-before",{},void 0,!0)]),"aside-ads-after":v(()=>[u(o.$slots,"aside-ads-after",{},void 0,!0)]),"aside-bottom":v(()=>[u(o.$slots,"aside-bottom",{},void 0,!0)]),_:3}))],2))}});const wn=m(Vn,[["__scopeId","data-v-669faec9"]]),Ln={class:"container"},Sn=["innerHTML"],Mn=["innerHTML"],Nn=g({__name:"VPFooter",setup(s){const{theme:e,frontmatter:t}=P(),{hasSidebar:n}=D();return(o,r)=>l(e).footer&&l(t).footer!==!1?(a(),i("footer",{key:0,class:N(["VPFooter",{"has-sidebar":l(n)}])},[c("div",Ln,[l(e).footer.message?(a(),i("p",{key:0,class:"message",innerHTML:l(e).footer.message},null,8,Sn)):f("",!0),l(e).footer.copyright?(a(),i("p",{key:1,class:"copyright",innerHTML:l(e).footer.copyright},null,8,Mn)):f("",!0)])],2)):f("",!0)}});const In=m(Nn,[["__scopeId","data-v-e03eb2e1"]]),Cn={class:"header"},Tn={class:"outline"},Bn=g({__name:"VPLocalNavOutlineDropdown",props:{headers:{},navHeight:{}},setup(s){const e=s,{theme:t}=P(),n=S(!1),o=S(0),r=S();Y(()=>{n.value=!1});function d(){n.value=!n.value,o.value=window.innerHeight+Math.min(window.scrollY-e.navHeight,0)}function p(b){b.target.classList.contains("outline-link")&&(r.value&&(r.value.style.transition="none"),Ee(()=>{n.value=!1}))}function _(){n.value=!1,window.scrollTo({top:0,left:0,behavior:"smooth"})}return(b,y)=>(a(),i("div",{class:"VPLocalNavOutlineDropdown",style:Fe({"--vp-vh":o.value+"px"})},[b.headers.length>0?(a(),i("button",{key:0,onClick:d,class:N({open:n.value})},[x(L(l(ve)(l(t)))+" ",1),h(fe,{class:"icon"})],2)):(a(),i("button",{key:1,onClick:_},L(l(t).returnToTopLabel||"Return to top"),1)),h(ce,{name:"flyout"},{default:v(()=>[n.value?(a(),i("div",{key:0,ref_key:"items",ref:r,class:"items",onClick:p},[c("div",Cn,[c("a",{class:"top-link",href:"#",onClick:_},L(l(t).returnToTopLabel||"Return to top"),1)]),c("div",Tn,[h(he,{headers:b.headers},null,8,["headers"])])],512)):f("",!0)]),_:1})],4))}});const An=m(Bn,[["__scopeId","data-v-1c15a60a"]]),xn={},Hn={xmlns:"http://www.w3.org/2000/svg","aria-hidden":"true",focusable:"false",viewBox:"0 0 24 24"},zn=c("path",{d:"M17,11H3c-0.6,0-1-0.4-1-1s0.4-1,1-1h14c0.6,0,1,0.4,1,1S17.6,11,17,11z"},null,-1),Dn=c("path",{d:"M21,7H3C2.4,7,2,6.6,2,6s0.4-1,1-1h18c0.6,0,1,0.4,1,1S21.6,7,21,7z"},null,-1),Fn=c("path",{d:"M21,15H3c-0.6,0-1-0.4-1-1s0.4-1,1-1h18c0.6,0,1,0.4,1,1S21.6,15,21,15z"},null,-1),En=c("path",{d:"M17,19H3c-0.6,0-1-0.4-1-1s0.4-1,1-1h14c0.6,0,1,0.4,1,1S17.6,19,17,19z"},null,-1),On=[zn,Dn,Fn,En];function Gn(s,e){return a(),i("svg",Hn,On)}const Un=m(xn,[["render",Gn]]),jn=["aria-expanded"],qn={class:"menu-text"},Rn=g({__name:"VPLocalNav",props:{open:{type:Boolean}},emits:["open-menu"],setup(s){const{theme:e,frontmatter:t}=P(),{hasSidebar:n}=D(),{y:o}=we(),r=_e([]),d=S(0);G(()=>{d.value=parseInt(getComputedStyle(document.documentElement).getPropertyValue("--vp-nav-height"))}),Y(()=>{r.value=pe(t.value.outline??e.value.outline)});const p=k(()=>r.value.length===0&&!n.value),_=k(()=>({VPLocalNav:!0,fixed:p.value,"reached-top":o.value>=d.value}));return(b,y)=>l(t).layout!=="home"&&(!p.value||l(o)>=d.value)?(a(),i("div",{key:0,class:N(_.value)},[l(n)?(a(),i("button",{key:0,class:"menu","aria-expanded":b.open,"aria-controls":"VPSidebarNav",onClick:y[0]||(y[0]=w=>b.$emit("open-menu"))},[h(Un,{class:"menu-icon"}),c("span",qn,L(l(e).sidebarMenuLabel||"Menu"),1)],8,jn)):f("",!0),h(An,{headers:r.value,navHeight:d.value},null,8,["headers","navHeight"])],2)):f("",!0)}});const Kn=m(Rn,[["__scopeId","data-v-79c8c1df"]]);function Wn(){const s=S(!1);function e(){s.value=!0,window.addEventListener("resize",o)}function t(){s.value=!1,window.removeEventListener("resize",o)}function n(){s.value?t():e()}function o(){window.outerWidth>=768&&t()}const r=se();return U(()=>r.path,t),{isScreenOpen:s,openScreen:e,closeScreen:t,toggleScreen:n}}const Yn={},Jn={class:"VPSwitch",type:"button",role:"switch"},Zn={class:"check"},Qn={key:0,class:"icon"};function Xn(s,e){return a(),i("button",Jn,[c("span",Zn,[s.$slots.default?(a(),i("span",Qn,[u(s.$slots,"default",{},void 0,!0)])):f("",!0)])])}const eo=m(Yn,[["render",Xn],["__scopeId","data-v-b1685198"]]),to={},so={xmlns:"http://www.w3.org/2000/svg","aria-hidden":"true",focusable:"false",viewBox:"0 0 24 24"},no=c("path",{d:"M12.1,22c-0.3,0-0.6,0-0.9,0c-5.5-0.5-9.5-5.4-9-10.9c0.4-4.8,4.2-8.6,9-9c0.4,0,0.8,0.2,1,0.5c0.2,0.3,0.2,0.8-0.1,1.1c-2,2.7-1.4,6.4,1.3,8.4c2.1,1.6,5,1.6,7.1,0c0.3-0.2,0.7-0.3,1.1-0.1c0.3,0.2,0.5,0.6,0.5,1c-0.2,2.7-1.5,5.1-3.6,6.8C16.6,21.2,14.4,22,12.1,22zM9.3,4.4c-2.9,1-5,3.6-5.2,6.8c-0.4,4.4,2.8,8.3,7.2,8.7c2.1,0.2,4.2-0.4,5.8-1.8c1.1-0.9,1.9-2.1,2.4-3.4c-2.5,0.9-5.3,0.5-7.5-1.1C9.2,11.4,8.1,7.7,9.3,4.4z"},null,-1),oo=[no];function ao(s,e){return a(),i("svg",so,oo)}const ro=m(to,[["render",ao]]),lo={},io={xmlns:"http://www.w3.org/2000/svg","aria-hidden":"true",focusable:"false",viewBox:"0 0 24 24"},co=Oe('',9),uo=[co];function _o(s,e){return a(),i("svg",io,uo)}const vo=m(lo,[["render",_o]]),po=g({__name:"VPSwitchAppearance",setup(s){const{isDark:e}=P(),t=ne("toggle-appearance",()=>{e.value=!e.value});return(n,o)=>(a(),$(eo,{title:"toggle dark mode",class:"VPSwitchAppearance","aria-checked":l(e),onClick:l(t)},{default:v(()=>[h(vo,{class:"sun"}),h(ro,{class:"moon"})]),_:1},8,["aria-checked","onClick"]))}});const me=m(po,[["__scopeId","data-v-ce54a7d1"]]),ho={key:0,class:"VPNavBarAppearance"},fo=g({__name:"VPNavBarAppearance",setup(s){const{site:e}=P();return(t,n)=>l(e).appearance&&l(e).appearance!=="force-dark"?(a(),i("div",ho,[h(me)])):f("",!0)}});const mo=m(fo,[["__scopeId","data-v-e6aabb21"]]),ge=S();let Ie=!1,ae=0;function go(s){const e=S(!1);if(K){!Ie&&bo(),ae++;const t=U(ge,n=>{var o,r,d;n===s.el.value||(o=s.el.value)!=null&&o.contains(n)?(e.value=!0,(r=s.onFocus)==null||r.call(s)):(e.value=!1,(d=s.onBlur)==null||d.call(s))});de(()=>{t(),ae--,ae||$o()})}return Ge(e)}function bo(){document.addEventListener("focusin",Ce),Ie=!0,ge.value=document.activeElement}function $o(){document.removeEventListener("focusin",Ce)}function Ce(){ge.value=document.activeElement}const ko={},yo={xmlns:"http://www.w3.org/2000/svg","aria-hidden":"true",focusable:"false",viewBox:"0 0 24 24"},Po=c("path",{d:"M12,16c-0.3,0-0.5-0.1-0.7-0.3l-6-6c-0.4-0.4-0.4-1,0-1.4s1-0.4,1.4,0l5.3,5.3l5.3-5.3c0.4-0.4,1-0.4,1.4,0s0.4,1,0,1.4l-6,6C12.5,15.9,12.3,16,12,16z"},null,-1),Vo=[Po];function wo(s,e){return a(),i("svg",yo,Vo)}const Te=m(ko,[["render",wo]]),Lo={},So={xmlns:"http://www.w3.org/2000/svg","aria-hidden":"true",focusable:"false",viewBox:"0 0 24 24"},Mo=c("circle",{cx:"12",cy:"12",r:"2"},null,-1),No=c("circle",{cx:"19",cy:"12",r:"2"},null,-1),Io=c("circle",{cx:"5",cy:"12",r:"2"},null,-1),Co=[Mo,No,Io];function To(s,e){return a(),i("svg",So,Co)}const Bo=m(Lo,[["render",To]]),Ao={class:"VPMenuLink"},xo=g({__name:"VPMenuLink",props:{item:{}},setup(s){const{page:e}=P();return(t,n)=>(a(),i("div",Ao,[h(F,{class:N({active:l(O)(l(e).relativePath,t.item.activeMatch||t.item.link,!!t.item.activeMatch)}),href:t.item.link,target:t.item.target,rel:t.item.rel},{default:v(()=>[x(L(t.item.text),1)]),_:1},8,["class","href","target","rel"])]))}});const oe=m(xo,[["__scopeId","data-v-43f1e123"]]),Ho={class:"VPMenuGroup"},zo={key:0,class:"title"},Do=g({__name:"VPMenuGroup",props:{text:{},items:{}},setup(s){return(e,t)=>(a(),i("div",Ho,[e.text?(a(),i("p",zo,L(e.text),1)):f("",!0),(a(!0),i(M,null,A(e.items,n=>(a(),i(M,null,["link"in n?(a(),$(oe,{key:0,item:n},null,8,["item"])):f("",!0)],64))),256))]))}});const Fo=m(Do,[["__scopeId","data-v-69e747b5"]]),Eo={class:"VPMenu"},Oo={key:0,class:"items"},Go=g({__name:"VPMenu",props:{items:{}},setup(s){return(e,t)=>(a(),i("div",Eo,[e.items?(a(),i("div",Oo,[(a(!0),i(M,null,A(e.items,n=>(a(),i(M,{key:n.text},["link"in n?(a(),$(oe,{key:0,item:n},null,8,["item"])):(a(),$(Fo,{key:1,text:n.text,items:n.items},null,8,["text","items"]))],64))),128))])):f("",!0),u(e.$slots,"default",{},void 0,!0)]))}});const Uo=m(Go,[["__scopeId","data-v-e7ea1737"]]),jo=["aria-expanded","aria-label"],qo={key:0,class:"text"},Ro=["innerHTML"],Ko={class:"menu"},Wo=g({__name:"VPFlyout",props:{icon:{},button:{},label:{},items:{}},setup(s){const e=S(!1),t=S();go({el:t,onBlur:n});function n(){e.value=!1}return(o,r)=>(a(),i("div",{class:"VPFlyout",ref_key:"el",ref:t,onMouseenter:r[1]||(r[1]=d=>e.value=!0),onMouseleave:r[2]||(r[2]=d=>e.value=!1)},[c("button",{type:"button",class:"button","aria-haspopup":"true","aria-expanded":e.value,"aria-label":o.label,onClick:r[0]||(r[0]=d=>e.value=!e.value)},[o.button||o.icon?(a(),i("span",qo,[o.icon?(a(),$(E(o.icon),{key:0,class:"option-icon"})):f("",!0),o.button?(a(),i("span",{key:1,innerHTML:o.button},null,8,Ro)):f("",!0),h(Te,{class:"text-icon"})])):(a(),$(Bo,{key:1,class:"icon"}))],8,jo),c("div",Ko,[h(Uo,{items:o.items},{default:v(()=>[u(o.$slots,"default",{},void 0,!0)]),_:3},8,["items"])])],544))}});const be=m(Wo,[["__scopeId","data-v-9c007e85"]]),Yo={discord:'Discord',facebook:'Facebook',github:'GitHub',instagram:'Instagram',linkedin:'LinkedIn',mastodon:'Mastodon',slack:'Slack',twitter:'Twitter',x:'X',youtube:'YouTube'},Jo=["href","aria-label","innerHTML"],Zo=g({__name:"VPSocialLink",props:{icon:{},link:{},ariaLabel:{}},setup(s){const e=s,t=k(()=>typeof e.icon=="object"?e.icon.svg:Yo[e.icon]);return(n,o)=>(a(),i("a",{class:"VPSocialLink no-icon",href:n.link,"aria-label":n.ariaLabel??(typeof n.icon=="string"?n.icon:""),target:"_blank",rel:"noopener",innerHTML:t.value},null,8,Jo))}});const Qo=m(Zo,[["__scopeId","data-v-f80f8133"]]),Xo={class:"VPSocialLinks"},ea=g({__name:"VPSocialLinks",props:{links:{}},setup(s){return(e,t)=>(a(),i("div",Xo,[(a(!0),i(M,null,A(e.links,({link:n,icon:o,ariaLabel:r})=>(a(),$(Qo,{key:n,icon:o,link:n,ariaLabel:r},null,8,["icon","link","ariaLabel"]))),128))]))}});const $e=m(ea,[["__scopeId","data-v-7bc22406"]]),ta={key:0,class:"group translations"},sa={class:"trans-title"},na={key:1,class:"group"},oa={class:"item appearance"},aa={class:"label"},ra={class:"appearance-action"},la={key:2,class:"group"},ia={class:"item social-links"},ca=g({__name:"VPNavBarExtra",setup(s){const{site:e,theme:t}=P(),{localeLinks:n,currentLang:o}=Z({correspondingLink:!0}),r=k(()=>n.value.length&&o.value.label||e.value.appearance||t.value.socialLinks);return(d,p)=>r.value?(a(),$(be,{key:0,class:"VPNavBarExtra",label:"extra navigation"},{default:v(()=>[l(n).length&&l(o).label?(a(),i("div",ta,[c("p",sa,L(l(o).label),1),(a(!0),i(M,null,A(l(n),_=>(a(),$(oe,{key:_.link,item:_},null,8,["item"]))),128))])):f("",!0),l(e).appearance&&l(e).appearance!=="force-dark"?(a(),i("div",na,[c("div",oa,[c("p",aa,L(l(t).darkModeSwitchLabel||"Appearance"),1),c("div",ra,[h(me)])])])):f("",!0),l(t).socialLinks?(a(),i("div",la,[c("div",ia,[h($e,{class:"social-links-list",links:l(t).socialLinks},null,8,["links"])])])):f("",!0)]),_:1})):f("",!0)}});const ua=m(ca,[["__scopeId","data-v-d0bd9dde"]]),da=s=>(H("data-v-e5dd9c1c"),s=s(),z(),s),_a=["aria-expanded"],va=da(()=>c("span",{class:"container"},[c("span",{class:"top"}),c("span",{class:"middle"}),c("span",{class:"bottom"})],-1)),pa=[va],ha=g({__name:"VPNavBarHamburger",props:{active:{type:Boolean}},emits:["click"],setup(s){return(e,t)=>(a(),i("button",{type:"button",class:N(["VPNavBarHamburger",{active:e.active}]),"aria-label":"mobile navigation","aria-expanded":e.active,"aria-controls":"VPNavScreen",onClick:t[0]||(t[0]=n=>e.$emit("click"))},pa,10,_a))}});const fa=m(ha,[["__scopeId","data-v-e5dd9c1c"]]),ma=["innerHTML"],ga=g({__name:"VPNavBarMenuLink",props:{item:{}},setup(s){const{page:e}=P();return(t,n)=>(a(),$(F,{class:N({VPNavBarMenuLink:!0,active:l(O)(l(e).relativePath,t.item.activeMatch||t.item.link,!!t.item.activeMatch)}),href:t.item.link,target:t.item.target,rel:t.item.rel,tabindex:"0"},{default:v(()=>[c("span",{innerHTML:t.item.text},null,8,ma)]),_:1},8,["class","href","target","rel"]))}});const ba=m(ga,[["__scopeId","data-v-42ef59de"]]),$a=g({__name:"VPNavBarMenuGroup",props:{item:{}},setup(s){const e=s,{page:t}=P(),n=r=>"link"in r?O(t.value.relativePath,r.link,!!e.item.activeMatch):r.items.some(n),o=k(()=>n(e.item));return(r,d)=>(a(),$(be,{class:N({VPNavBarMenuGroup:!0,active:l(O)(l(t).relativePath,r.item.activeMatch,!!r.item.activeMatch)||o.value}),button:r.item.text,items:r.item.items},null,8,["class","button","items"]))}}),ka=s=>(H("data-v-7f418b0f"),s=s(),z(),s),ya={key:0,"aria-labelledby":"main-nav-aria-label",class:"VPNavBarMenu"},Pa=ka(()=>c("span",{id:"main-nav-aria-label",class:"visually-hidden"},"Main Navigation",-1)),Va=g({__name:"VPNavBarMenu",setup(s){const{theme:e}=P();return(t,n)=>l(e).nav?(a(),i("nav",ya,[Pa,(a(!0),i(M,null,A(l(e).nav,o=>(a(),i(M,{key:o.text},["link"in o?(a(),$(ba,{key:0,item:o},null,8,["item"])):(a(),$($a,{key:1,item:o},null,8,["item"]))],64))),128))])):f("",!0)}});const wa=m(Va,[["__scopeId","data-v-7f418b0f"]]);function La(s,e){const{localeIndex:t}=P();function n(o){var I,B;const r=o.split("."),d=s&&typeof s=="object",p=d&&((B=(I=s.locales)==null?void 0:I[t.value])==null?void 0:B.translations)||null,_=d&&s.translations||null;let b=p,y=_,w=e;const C=r.pop();for(const T of r){let V=null;const q=w==null?void 0:w[T];q&&(V=w=q);const W=y==null?void 0:y[T];W&&(V=y=W);const R=b==null?void 0:b[T];R&&(V=b=R),q||(w=V),W||(y=V),R||(b=V)}return(b==null?void 0:b[C])??(y==null?void 0:y[C])??(w==null?void 0:w[C])??""}return n}const Sa=["aria-label"],Ma={class:"DocSearch-Button-Container"},Na=c("svg",{class:"DocSearch-Search-Icon",width:"20",height:"20",viewBox:"0 0 20 20","aria-label":"search icon"},[c("path",{d:"M14.386 14.386l4.0877 4.0877-4.0877-4.0877c-2.9418 2.9419-7.7115 2.9419-10.6533 0-2.9419-2.9418-2.9419-7.7115 0-10.6533 2.9418-2.9419 7.7115-2.9419 10.6533 0 2.9419 2.9418 2.9419 7.7115 0 10.6533z",stroke:"currentColor",fill:"none","fill-rule":"evenodd","stroke-linecap":"round","stroke-linejoin":"round"})],-1),Ia={class:"DocSearch-Button-Placeholder"},Ca=c("span",{class:"DocSearch-Button-Keys"},[c("kbd",{class:"DocSearch-Button-Key"}),c("kbd",{class:"DocSearch-Button-Key"},"K")],-1),ye=g({__name:"VPNavBarSearchButton",setup(s){const{theme:e}=P(),t={button:{buttonText:"Search",buttonAriaLabel:"Search"}},n=Ue(La)(je(()=>{var o;return(o=e.value.search)==null?void 0:o.options}),t);return(o,r)=>(a(),i("button",{type:"button",class:"DocSearch DocSearch-Button","aria-label":l(n)("button.buttonAriaLabel")},[c("span",Ma,[Na,c("span",Ia,L(l(n)("button.buttonText")),1)]),Ca],8,Sa))}});const Ta={class:"VPNavBarSearch"},Ba={id:"local-search"},Aa={key:1,id:"docsearch"},xa=g({__name:"VPNavBarSearch",setup(s){const e=()=>null,t=()=>null,{theme:n}=P(),o=S(!1),r=S(!1);G(()=>{});function d(){o.value||(o.value=!0,setTimeout(p,16))}function p(){const y=new Event("keydown");y.key="k",y.metaKey=!0,window.dispatchEvent(y),setTimeout(()=>{document.querySelector(".DocSearch-Modal")||p()},16)}const _=S(!1),b="";return(y,w)=>{var C;return a(),i("div",Ta,[l(b)==="local"?(a(),i(M,{key:0},[_.value?(a(),$(l(e),{key:0,onClose:w[0]||(w[0]=I=>_.value=!1)})):f("",!0),c("div",Ba,[h(ye,{onClick:w[1]||(w[1]=I=>_.value=!0)})])],64)):l(b)==="algolia"?(a(),i(M,{key:1},[o.value?(a(),$(l(t),{key:0,algolia:((C=l(n).search)==null?void 0:C.options)??l(n).algolia,onVnodeBeforeMount:w[2]||(w[2]=I=>r.value=!0)},null,8,["algolia"])):f("",!0),r.value?f("",!0):(a(),i("div",Aa,[h(ye,{onClick:d})]))],64)):f("",!0)])}}});const Ha=g({__name:"VPNavBarSocialLinks",setup(s){const{theme:e}=P();return(t,n)=>l(e).socialLinks?(a(),$($e,{key:0,class:"VPNavBarSocialLinks",links:l(e).socialLinks},null,8,["links"])):f("",!0)}});const za=m(Ha,[["__scopeId","data-v-0394ad82"]]),Da=["href"],Fa=g({__name:"VPNavBarTitle",setup(s){const{site:e,theme:t}=P(),{hasSidebar:n}=D(),{currentLang:o}=Z();return(r,d)=>(a(),i("div",{class:N(["VPNavBarTitle",{"has-sidebar":l(n)}])},[c("a",{class:"title",href:l(t).logoLink??l(J)(l(o).link)},[u(r.$slots,"nav-bar-title-before",{},void 0,!0),l(t).logo?(a(),$(ee,{key:0,class:"logo",image:l(t).logo},null,8,["image"])):f("",!0),l(t).siteTitle?(a(),i(M,{key:1},[x(L(l(t).siteTitle),1)],64)):l(t).siteTitle===void 0?(a(),i(M,{key:2},[x(L(l(e).title),1)],64)):f("",!0),u(r.$slots,"nav-bar-title-after",{},void 0,!0)],8,Da)],2))}});const Ea=m(Fa,[["__scopeId","data-v-86d1bed8"]]),Oa={},Ga={xmlns:"http://www.w3.org/2000/svg","aria-hidden":"true",focusable:"false",viewBox:"0 0 24 24"},Ua=c("path",{d:"M0 0h24v24H0z",fill:"none"},null,-1),ja=c("path",{d:" M12.87 15.07l-2.54-2.51.03-.03c1.74-1.94 2.98-4.17 3.71-6.53H17V4h-7V2H8v2H1v1.99h11.17C11.5 7.92 10.44 9.75 9 11.35 8.07 10.32 7.3 9.19 6.69 8h-2c.73 1.63 1.73 3.17 2.98 4.56l-5.09 5.02L4 19l5-5 3.11 3.11.76-2.04zM18.5 10h-2L12 22h2l1.12-3h4.75L21 22h2l-4.5-12zm-2.62 7l1.62-4.33L19.12 17h-3.24z ",class:"css-c4d79v"},null,-1),qa=[Ua,ja];function Ra(s,e){return a(),i("svg",Ga,qa)}const Be=m(Oa,[["render",Ra]]),Ka={class:"items"},Wa={class:"title"},Ya=g({__name:"VPNavBarTranslations",setup(s){const{theme:e}=P(),{localeLinks:t,currentLang:n}=Z({correspondingLink:!0});return(o,r)=>l(t).length&&l(n).label?(a(),$(be,{key:0,class:"VPNavBarTranslations",icon:Be,label:l(e).langMenuLabel||"Change language"},{default:v(()=>[c("div",Ka,[c("p",Wa,L(l(n).label),1),(a(!0),i(M,null,A(l(t),d=>(a(),$(oe,{key:d.link,item:d},null,8,["item"]))),128))])]),_:1},8,["label"])):f("",!0)}});const Ja=m(Ya,[["__scopeId","data-v-74abcbb9"]]),Za=s=>(H("data-v-a0fd61f4"),s=s(),z(),s),Qa={class:"container"},Xa={class:"title"},er={class:"content"},tr=Za(()=>c("div",{class:"curtain"},null,-1)),sr={class:"content-body"},nr=g({__name:"VPNavBar",props:{isScreenOpen:{type:Boolean}},emits:["toggle-screen"],setup(s){const{y:e}=we(),{hasSidebar:t}=D(),{frontmatter:n}=P(),o=S({});return Pe(()=>{o.value={"has-sidebar":t.value,top:n.value.layout==="home"&&e.value===0}}),(r,d)=>(a(),i("div",{class:N(["VPNavBar",o.value])},[c("div",Qa,[c("div",Xa,[h(Ea,null,{"nav-bar-title-before":v(()=>[u(r.$slots,"nav-bar-title-before",{},void 0,!0)]),"nav-bar-title-after":v(()=>[u(r.$slots,"nav-bar-title-after",{},void 0,!0)]),_:3})]),c("div",er,[tr,c("div",sr,[u(r.$slots,"nav-bar-content-before",{},void 0,!0),h(xa,{class:"search"}),h(wa,{class:"menu"}),h(Ja,{class:"translations"}),h(mo,{class:"appearance"}),h(za,{class:"social-links"}),h(ua,{class:"extra"}),u(r.$slots,"nav-bar-content-after",{},void 0,!0),h(fa,{class:"hamburger",active:r.isScreenOpen,onClick:d[0]||(d[0]=p=>r.$emit("toggle-screen"))},null,8,["active"])])])])],2))}});const or=m(nr,[["__scopeId","data-v-a0fd61f4"]]),ar={key:0,class:"VPNavScreenAppearance"},rr={class:"text"},lr=g({__name:"VPNavScreenAppearance",setup(s){const{site:e,theme:t}=P();return(n,o)=>l(e).appearance&&l(e).appearance!=="force-dark"?(a(),i("div",ar,[c("p",rr,L(l(t).darkModeSwitchLabel||"Appearance"),1),h(me)])):f("",!0)}});const ir=m(lr,[["__scopeId","data-v-2d7af913"]]),cr=g({__name:"VPNavScreenMenuLink",props:{item:{}},setup(s){const e=ne("close-screen");return(t,n)=>(a(),$(F,{class:"VPNavScreenMenuLink",href:t.item.link,target:t.item.target,rel:t.item.rel,onClick:l(e)},{default:v(()=>[x(L(t.item.text),1)]),_:1},8,["href","target","rel","onClick"]))}});const ur=m(cr,[["__scopeId","data-v-05f27b2a"]]),dr={},_r={xmlns:"http://www.w3.org/2000/svg","aria-hidden":"true",focusable:"false",viewBox:"0 0 24 24"},vr=c("path",{d:"M18.9,10.9h-6v-6c0-0.6-0.4-1-1-1s-1,0.4-1,1v6h-6c-0.6,0-1,0.4-1,1s0.4,1,1,1h6v6c0,0.6,0.4,1,1,1s1-0.4,1-1v-6h6c0.6,0,1-0.4,1-1S19.5,10.9,18.9,10.9z"},null,-1),pr=[vr];function hr(s,e){return a(),i("svg",_r,pr)}const fr=m(dr,[["render",hr]]),mr=g({__name:"VPNavScreenMenuGroupLink",props:{item:{}},setup(s){const e=ne("close-screen");return(t,n)=>(a(),$(F,{class:"VPNavScreenMenuGroupLink",href:t.item.link,target:t.item.target,rel:t.item.rel,onClick:l(e)},{default:v(()=>[x(L(t.item.text),1)]),_:1},8,["href","target","rel","onClick"]))}});const Ae=m(mr,[["__scopeId","data-v-19976ae1"]]),gr={class:"VPNavScreenMenuGroupSection"},br={key:0,class:"title"},$r=g({__name:"VPNavScreenMenuGroupSection",props:{text:{},items:{}},setup(s){return(e,t)=>(a(),i("div",gr,[e.text?(a(),i("p",br,L(e.text),1)):f("",!0),(a(!0),i(M,null,A(e.items,n=>(a(),$(Ae,{key:n.text,item:n},null,8,["item"]))),128))]))}});const kr=m($r,[["__scopeId","data-v-8133b170"]]),yr=["aria-controls","aria-expanded"],Pr={class:"button-text"},Vr=["id"],wr={key:1,class:"group"},Lr=g({__name:"VPNavScreenMenuGroup",props:{text:{},items:{}},setup(s){const e=s,t=S(!1),n=k(()=>`NavScreenGroup-${e.text.replace(" ","-").toLowerCase()}`);function o(){t.value=!t.value}return(r,d)=>(a(),i("div",{class:N(["VPNavScreenMenuGroup",{open:t.value}])},[c("button",{class:"button","aria-controls":n.value,"aria-expanded":t.value,onClick:o},[c("span",Pr,L(r.text),1),h(fr,{class:"button-icon"})],8,yr),c("div",{id:n.value,class:"items"},[(a(!0),i(M,null,A(r.items,p=>(a(),i(M,{key:p.text},["link"in p?(a(),i("div",{key:p.text,class:"item"},[h(Ae,{item:p},null,8,["item"])])):(a(),i("div",wr,[h(kr,{text:p.text,items:p.items},null,8,["text","items"])]))],64))),128))],8,Vr)],2))}});const Sr=m(Lr,[["__scopeId","data-v-1ecb84e7"]]),Mr={key:0,class:"VPNavScreenMenu"},Nr=g({__name:"VPNavScreenMenu",setup(s){const{theme:e}=P();return(t,n)=>l(e).nav?(a(),i("nav",Mr,[(a(!0),i(M,null,A(l(e).nav,o=>(a(),i(M,{key:o.text},["link"in o?(a(),$(ur,{key:0,item:o},null,8,["item"])):(a(),$(Sr,{key:1,text:o.text||"",items:o.items},null,8,["text","items"]))],64))),128))])):f("",!0)}}),Ir=g({__name:"VPNavScreenSocialLinks",setup(s){const{theme:e}=P();return(t,n)=>l(e).socialLinks?(a(),$($e,{key:0,class:"VPNavScreenSocialLinks",links:l(e).socialLinks},null,8,["links"])):f("",!0)}}),Cr={class:"list"},Tr=g({__name:"VPNavScreenTranslations",setup(s){const{localeLinks:e,currentLang:t}=Z({correspondingLink:!0}),n=S(!1);function o(){n.value=!n.value}return(r,d)=>l(e).length&&l(t).label?(a(),i("div",{key:0,class:N(["VPNavScreenTranslations",{open:n.value}])},[c("button",{class:"title",onClick:o},[h(Be,{class:"icon lang"}),x(" "+L(l(t).label)+" ",1),h(Te,{class:"icon chevron"})]),c("ul",Cr,[(a(!0),i(M,null,A(l(e),p=>(a(),i("li",{key:p.link,class:"item"},[h(F,{class:"link",href:p.link},{default:v(()=>[x(L(p.text),1)]),_:2},1032,["href"])]))),128))])],2)):f("",!0)}});const Br=m(Tr,[["__scopeId","data-v-d72aa483"]]),Ar={class:"container"},xr=g({__name:"VPNavScreen",props:{open:{type:Boolean}},setup(s){const e=S(null),t=Le(K?document.body:null);return(n,o)=>(a(),$(ce,{name:"fade",onEnter:o[0]||(o[0]=r=>t.value=!0),onAfterLeave:o[1]||(o[1]=r=>t.value=!1)},{default:v(()=>[n.open?(a(),i("div",{key:0,class:"VPNavScreen",ref_key:"screen",ref:e,id:"VPNavScreen"},[c("div",Ar,[u(n.$slots,"nav-screen-content-before",{},void 0,!0),h(Nr,{class:"menu"}),h(Br,{class:"translations"}),h(ir,{class:"appearance"}),h(Ir,{class:"social-links"}),u(n.$slots,"nav-screen-content-after",{},void 0,!0)])],512)):f("",!0)]),_:3}))}});const Hr=m(xr,[["__scopeId","data-v-cc5739dd"]]),zr={key:0,class:"VPNav"},Dr=g({__name:"VPNav",setup(s){const{isScreenOpen:e,closeScreen:t,toggleScreen:n}=Wn(),{frontmatter:o}=P(),r=k(()=>o.value.navbar!==!1);return Se("close-screen",t),te(()=>{K&&document.documentElement.classList.toggle("hide-nav",!r.value)}),(d,p)=>r.value?(a(),i("header",zr,[h(or,{"is-screen-open":l(e),onToggleScreen:l(n)},{"nav-bar-title-before":v(()=>[u(d.$slots,"nav-bar-title-before",{},void 0,!0)]),"nav-bar-title-after":v(()=>[u(d.$slots,"nav-bar-title-after",{},void 0,!0)]),"nav-bar-content-before":v(()=>[u(d.$slots,"nav-bar-content-before",{},void 0,!0)]),"nav-bar-content-after":v(()=>[u(d.$slots,"nav-bar-content-after",{},void 0,!0)]),_:3},8,["is-screen-open","onToggleScreen"]),h(Hr,{open:l(e)},{"nav-screen-content-before":v(()=>[u(d.$slots,"nav-screen-content-before",{},void 0,!0)]),"nav-screen-content-after":v(()=>[u(d.$slots,"nav-screen-content-after",{},void 0,!0)]),_:3},8,["open"])])):f("",!0)}});const Fr=m(Dr,[["__scopeId","data-v-ae24b3ad"]]),Er=s=>(H("data-v-e31bd47b"),s=s(),z(),s),Or=["role","tabindex"],Gr=Er(()=>c("div",{class:"indicator"},null,-1)),Ur=["onKeydown"],jr={key:1,class:"items"},qr=g({__name:"VPSidebarItem",props:{item:{},depth:{}},setup(s){const e=s,{collapsed:t,collapsible:n,isLink:o,isActiveLink:r,hasActiveLink:d,hasChildren:p,toggle:_}=ht(k(()=>e.item)),b=k(()=>p.value?"section":"div"),y=k(()=>o.value?"a":"div"),w=k(()=>p.value?e.depth+2===7?"p":`h${e.depth+2}`:"p"),C=k(()=>o.value?void 0:"button"),I=k(()=>[[`level-${e.depth}`],{collapsible:n.value},{collapsed:t.value},{"is-link":o.value},{"is-active":r.value},{"has-active":d.value}]);function B(V){"key"in V&&V.key!=="Enter"||!e.item.link&&_()}function T(){e.item.link&&_()}return(V,q)=>{const W=j("VPSidebarItem",!0);return a(),$(E(b.value),{class:N(["VPSidebarItem",I.value])},{default:v(()=>[V.item.text?(a(),i("div",Q({key:0,class:"item",role:C.value},Re(V.item.items?{click:B,keydown:B}:{},!0),{tabindex:V.item.items&&0}),[Gr,V.item.link?(a(),$(F,{key:0,tag:y.value,class:"link",href:V.item.link,rel:V.item.rel,target:V.item.target},{default:v(()=>[(a(),$(E(w.value),{class:"text",innerHTML:V.item.text},null,8,["innerHTML"]))]),_:1},8,["tag","href","rel","target"])):(a(),$(E(w.value),{key:1,class:"text",innerHTML:V.item.text},null,8,["innerHTML"])),V.item.collapsed!=null?(a(),i("div",{key:2,class:"caret",role:"button","aria-label":"toggle section",onClick:T,onKeydown:qe(T,["enter"]),tabindex:"0"},[h(fe,{class:"caret-icon"})],40,Ur)):f("",!0)],16,Or)):f("",!0),V.item.items&&V.item.items.length?(a(),i("div",jr,[V.depth<5?(a(!0),i(M,{key:0},A(V.item.items,R=>(a(),$(W,{key:R.text,item:R,depth:V.depth+1},null,8,["item","depth"]))),128)):f("",!0)])):f("",!0)]),_:1},8,["class"])}}});const Rr=m(qr,[["__scopeId","data-v-e31bd47b"]]),xe=s=>(H("data-v-b00e2fdd"),s=s(),z(),s),Kr=xe(()=>c("div",{class:"curtain"},null,-1)),Wr={class:"nav",id:"VPSidebarNav","aria-labelledby":"sidebar-aria-label",tabindex:"-1"},Yr=xe(()=>c("span",{class:"visually-hidden",id:"sidebar-aria-label"}," Sidebar Navigation ",-1)),Jr=g({__name:"VPSidebar",props:{open:{type:Boolean}},setup(s){const{sidebarGroups:e,hasSidebar:t}=D(),n=s,o=S(null),r=Le(K?document.body:null);return U([n,o],()=>{var d;n.open?(r.value=!0,(d=o.value)==null||d.focus()):r.value=!1},{immediate:!0,flush:"post"}),(d,p)=>l(t)?(a(),i("aside",{key:0,class:N(["VPSidebar",{open:d.open}]),ref_key:"navEl",ref:o,onClick:p[0]||(p[0]=Ke(()=>{},["stop"]))},[Kr,c("nav",Wr,[Yr,u(d.$slots,"sidebar-nav-before",{},void 0,!0),(a(!0),i(M,null,A(l(e),_=>(a(),i("div",{key:_.text,class:"group"},[h(Rr,{item:_,depth:0},null,8,["item"])]))),128)),u(d.$slots,"sidebar-nav-after",{},void 0,!0)])],2)):f("",!0)}});const Zr=m(Jr,[["__scopeId","data-v-b00e2fdd"]]),Qr=g({__name:"VPSkipLink",setup(s){const e=se(),t=S();U(()=>e.path,()=>t.value.focus());function n({target:o}){const r=document.getElementById(decodeURIComponent(o.hash).slice(1));if(r){const d=()=>{r.removeAttribute("tabindex"),r.removeEventListener("blur",d)};r.setAttribute("tabindex","-1"),r.addEventListener("blur",d),r.focus(),window.scrollTo(0,0)}}return(o,r)=>(a(),i(M,null,[c("span",{ref_key:"backToTop",ref:t,tabindex:"-1"},null,512),c("a",{href:"#VPContent",class:"VPSkipLink visually-hidden",onClick:n}," Skip to content ")],64))}});const Xr=m(Qr,[["__scopeId","data-v-0f60ec36"]]),el=g({__name:"Layout",setup(s){const{isOpen:e,open:t,close:n}=D(),o=se();U(()=>o.path,n),pt(e,n);const{frontmatter:r}=P(),d=We(),p=k(()=>!!d["home-hero-image"]);return Se("hero-image-slot-exists",p),(_,b)=>{const y=j("Content");return l(r).layout!==!1?(a(),i("div",{key:0,class:N(["Layout",l(r).pageClass])},[u(_.$slots,"layout-top",{},void 0,!0),h(Xr),h(Xe,{class:"backdrop",show:l(e),onClick:l(n)},null,8,["show","onClick"]),h(Fr,null,{"nav-bar-title-before":v(()=>[u(_.$slots,"nav-bar-title-before",{},void 0,!0)]),"nav-bar-title-after":v(()=>[u(_.$slots,"nav-bar-title-after",{},void 0,!0)]),"nav-bar-content-before":v(()=>[u(_.$slots,"nav-bar-content-before",{},void 0,!0)]),"nav-bar-content-after":v(()=>[u(_.$slots,"nav-bar-content-after",{},void 0,!0)]),"nav-screen-content-before":v(()=>[u(_.$slots,"nav-screen-content-before",{},void 0,!0)]),"nav-screen-content-after":v(()=>[u(_.$slots,"nav-screen-content-after",{},void 0,!0)]),_:3}),h(Kn,{open:l(e),onOpenMenu:l(t)},null,8,["open","onOpenMenu"]),h(Zr,{open:l(e)},{"sidebar-nav-before":v(()=>[u(_.$slots,"sidebar-nav-before",{},void 0,!0)]),"sidebar-nav-after":v(()=>[u(_.$slots,"sidebar-nav-after",{},void 0,!0)]),_:3},8,["open"]),h(wn,null,{"page-top":v(()=>[u(_.$slots,"page-top",{},void 0,!0)]),"page-bottom":v(()=>[u(_.$slots,"page-bottom",{},void 0,!0)]),"not-found":v(()=>[u(_.$slots,"not-found",{},void 0,!0)]),"home-hero-before":v(()=>[u(_.$slots,"home-hero-before",{},void 0,!0)]),"home-hero-info":v(()=>[u(_.$slots,"home-hero-info",{},void 0,!0)]),"home-hero-image":v(()=>[u(_.$slots,"home-hero-image",{},void 0,!0)]),"home-hero-after":v(()=>[u(_.$slots,"home-hero-after",{},void 0,!0)]),"home-features-before":v(()=>[u(_.$slots,"home-features-before",{},void 0,!0)]),"home-features-after":v(()=>[u(_.$slots,"home-features-after",{},void 0,!0)]),"doc-footer-before":v(()=>[u(_.$slots,"doc-footer-before",{},void 0,!0)]),"doc-before":v(()=>[u(_.$slots,"doc-before",{},void 0,!0)]),"doc-after":v(()=>[u(_.$slots,"doc-after",{},void 0,!0)]),"doc-top":v(()=>[u(_.$slots,"doc-top",{},void 0,!0)]),"doc-bottom":v(()=>[u(_.$slots,"doc-bottom",{},void 0,!0)]),"aside-top":v(()=>[u(_.$slots,"aside-top",{},void 0,!0)]),"aside-bottom":v(()=>[u(_.$slots,"aside-bottom",{},void 0,!0)]),"aside-outline-before":v(()=>[u(_.$slots,"aside-outline-before",{},void 0,!0)]),"aside-outline-after":v(()=>[u(_.$slots,"aside-outline-after",{},void 0,!0)]),"aside-ads-before":v(()=>[u(_.$slots,"aside-ads-before",{},void 0,!0)]),"aside-ads-after":v(()=>[u(_.$slots,"aside-ads-after",{},void 0,!0)]),_:3}),h(In),u(_.$slots,"layout-bottom",{},void 0,!0)],2)):(a(),$(y,{key:1}))}}});const tl=m(el,[["__scopeId","data-v-5a346dfe"]]);const nl={Layout:tl,enhanceApp:({app:s})=>{s.component("Badge",Je)}};export{nl as t}; diff --git a/assets/chunks/theme.85c31bd3.js b/assets/chunks/theme.85c31bd3.js deleted file mode 100644 index d1ada1e0f969..000000000000 --- a/assets/chunks/theme.85c31bd3.js +++ /dev/null @@ -1,7 +0,0 @@ -import{d as g,o as a,c as i,r,n as M,a as I,t as P,_ as p,u as Qe,b as c,e as Je,f as Ae,g as Ze,h as x,i as et,j as tt,w as q,k as Q,l as k,m as nt,p as F,q as st,P as ot,s as he,v as K,x as ee,y as fe,z as u,F as S,A as y,B as v,T as pe,C as m,D as se,E as h,G as R,H as He,I as U,J as Ee,K as at,L as N,M as H,N as E,O as ct,Q as Be,R as me,S as oe,U as lt,V as J,W as ge,X as it,Y as rt,Z as ut,$ as dt,a0 as _t,a1 as vt}from"./framework.43781440.js";const ht=g({__name:"VPBadge",props:{text:null,type:null},setup(e){return(t,n)=>(a(),i("span",{class:M(["VPBadge",e.type??"tip"])},[r(t.$slots,"default",{},()=>[I(P(e.text),1)],!0)],2))}});const ft=p(ht,[["__scopeId","data-v-350d3852"]]),V=Qe;function ze(e){return et()?(tt(e),!0):!1}function De(e){return typeof e=="function"?e():c(e)}const pt=typeof window<"u",Fe=()=>{};function mt(...e){if(e.length!==1)return Je(...e);const t=e[0];return typeof t=="function"?Ae(Ze(()=>({get:t,set:Fe}))):x(t)}function gt(e){var t;const n=De(e);return(t=n==null?void 0:n.$el)!=null?t:n}const ye=pt?window:void 0;function yt(...e){let t,n,s,o;if(typeof e[0]=="string"||Array.isArray(e[0])?([n,s,o]=e,t=ye):[t,n,s,o]=e,!t)return Fe;Array.isArray(n)||(n=[n]),Array.isArray(s)||(s=[s]);const l=[],d=()=>{l.forEach(L=>L()),l.length=0},f=(L,C,w,$)=>(L.addEventListener(C,w,$),()=>L.removeEventListener(C,w,$)),_=q(()=>[gt(t),De(o)],([L,C])=>{d(),L&&l.push(...n.flatMap(w=>s.map($=>f(L,w,$,C))))},{immediate:!0,flush:"post"}),b=()=>{_(),d()};return ze(b),b}function bt(){const e=x(!1);return nt()&&F(()=>{e.value=!0}),e}function kt(e){const t=bt();return k(()=>(t.value,!!e()))}function de(e,t={}){const{window:n=ye}=t,s=kt(()=>n&&"matchMedia"in n&&typeof n.matchMedia=="function");let o;const l=x(!1),d=()=>{o&&("removeEventListener"in o?o.removeEventListener("change",f):o.removeListener(f))},f=()=>{s.value&&(d(),o=n.matchMedia(mt(e).value),l.value=!!(o!=null&&o.matches),o&&("addEventListener"in o?o.addEventListener("change",f):o.addListener(f)))};return Q(f),ze(()=>d()),l}function $t({window:e=ye}={}){if(!e)return{x:x(0),y:x(0)};const t=x(e.scrollX),n=x(e.scrollY);return yt(e,"scroll",()=>{t.value=e.scrollX,n.value=e.scrollY},{capture:!1,passive:!0}),{x:t,y:n}}function Pt(e,t){let n,s=!1;return()=>{n&&clearTimeout(n),s?n=setTimeout(e,t):(e(),s=!0,setTimeout(()=>{s=!1},t))}}function _e(e){return/^\//.test(e)?e:`/${e}`}function Z(e){if(st(e))return e.replace(ot,"");const{site:t}=V(),{pathname:n,search:s,hash:o}=new URL(e,"http://example.com"),l=n.endsWith("/")||n.endsWith(".html")?e:e.replace(/(?:(^\.+)\/)?.*$/,`$1${n.replace(/(\.md)?$/,t.value.cleanUrls?"":".html")}${s}${o}`);return he(l)}function Oe(e,t){if(Array.isArray(e))return e;if(e==null)return[];t=_e(t);const n=Object.keys(e).sort((s,o)=>o.split("/").length-s.split("/").length).find(s=>t.startsWith(_e(s)));return n?e[n]:[]}function Vt(e){const t=[];let n=0;for(const s in e){const o=e[s];if(o.items){n=t.push(o);continue}t[n]||t.push({items:[]}),t[n].items.push(o)}return t}function wt(e){const t=[];function n(s){for(const o of s)o.text&&o.link&&t.push({text:o.text,link:o.link}),o.items&&n(o.items)}return n(e),t}function ve(e,t){return Array.isArray(t)?t.some(n=>ve(e,n)):K(e,t.link)?!0:t.items?ve(e,t.items):!1}function z(){const e=ee(),{theme:t,frontmatter:n}=V(),s=de("(min-width: 960px)"),o=x(!1),l=k(()=>{const B=t.value.sidebar,T=e.data.relativePath;return B?Oe(B,T):[]}),d=k(()=>n.value.sidebar!==!1&&l.value.length>0&&n.value.layout!=="home"),f=k(()=>_?n.value.aside==null?t.value.aside==="left":n.value.aside==="left":!1),_=k(()=>n.value.layout==="home"?!1:n.value.aside!=null?!!n.value.aside:t.value.aside!==!1),b=k(()=>d.value&&s.value),L=k(()=>d.value?Vt(l.value):[]);function C(){o.value=!0}function w(){o.value=!1}function $(){o.value?w():C()}return{isOpen:o,sidebar:l,sidebarGroups:L,hasSidebar:d,hasAside:_,leftAside:f,isSidebarEnabled:b,open:C,close:w,toggle:$}}function xt(e,t){let n;Q(()=>{n=e.value?document.activeElement:void 0}),F(()=>{window.addEventListener("keyup",s)}),fe(()=>{window.removeEventListener("keyup",s)});function s(o){o.key==="Escape"&&e.value&&(t(),n==null||n.focus())}}function St(e){const{page:t}=V(),n=x(!1),s=k(()=>e.value.collapsed!=null),o=k(()=>!!e.value.link),l=k(()=>K(t.value.relativePath,e.value.link)),d=k(()=>l.value?!0:e.value.items?ve(t.value.relativePath,e.value.items):!1),f=k(()=>!!(e.value.items&&e.value.items.length));Q(()=>{n.value=!!(s.value&&e.value.collapsed)}),Q(()=>{(l.value||d.value)&&(n.value=!1)});function _(){s.value&&(n.value=!n.value)}return{collapsed:n,collapsible:s,isLink:o,isActiveLink:l,hasActiveLink:d,hasChildren:f,toggle:_}}const Lt=g({__name:"VPSkipLink",setup(e){const t=ee(),n=x();q(()=>t.path,()=>n.value.focus());function s({target:o}){const l=document.querySelector(decodeURIComponent(o.hash));if(l){const d=()=>{l.removeAttribute("tabindex"),l.removeEventListener("blur",d)};l.setAttribute("tabindex","-1"),l.addEventListener("blur",d),l.focus(),window.scrollTo(0,0)}}return(o,l)=>(a(),i(S,null,[u("span",{ref_key:"backToTop",ref:n,tabindex:"-1"},null,512),u("a",{href:"#VPContent",class:"VPSkipLink visually-hidden",onClick:s}," Skip to content ")],64))}});const Mt=p(Lt,[["__scopeId","data-v-c8616af1"]]),Ct={key:0,class:"VPBackdrop"},Bt=g({__name:"VPBackdrop",props:{show:{type:Boolean}},setup(e){return(t,n)=>(a(),y(pe,{name:"fade"},{default:v(()=>[e.show?(a(),i("div",Ct)):m("",!0)]),_:1}))}});const It=p(Bt,[["__scopeId","data-v-c79a1216"]]);function Nt(){const e=x(!1);function t(){e.value=!0,window.addEventListener("resize",o)}function n(){e.value=!1,window.removeEventListener("resize",o)}function s(){e.value?n():t()}function o(){window.outerWidth>=768&&n()}const l=ee();return q(()=>l.path,n),{isScreenOpen:e,openScreen:t,closeScreen:n,toggleScreen:s}}function te({removeCurrent:e=!0,correspondingLink:t=!1}={}){const{site:n,localeIndex:s,page:o,theme:l}=V(),d=k(()=>{var _,b;return{label:(_=n.value.locales[s.value])==null?void 0:_.label,link:((b=n.value.locales[s.value])==null?void 0:b.link)||(s.value==="root"?"/":`/${s.value}/`)}});return{localeLinks:k(()=>Object.entries(n.value.locales).flatMap(([_,b])=>e&&d.value.label===b.label?[]:{text:b.label,link:Tt(b.link||(_==="root"?"/":`/${_}/`),l.value.i18nRouting!==!1&&t,o.value.relativePath.slice(d.value.link.length-1),!n.value.cleanUrls)})),currentLang:d}}function Tt(e,t,n,s){return t?e.replace(/\/$/,"")+_e(n.replace(/(^|\/)?index.md$/,"$1").replace(/\.md$/,s?".html":"")):e}const At=["src","alt"],Ht={inheritAttrs:!1},Et=g({...Ht,__name:"VPImage",props:{image:null,alt:null},setup(e){return(t,n)=>{const s=R("VPImage",!0);return e.image?(a(),i(S,{key:0},[typeof e.image=="string"||"src"in e.image?(a(),i("img",se({key:0,class:"VPImage"},typeof e.image=="string"?t.$attrs:{...e.image,...t.$attrs},{src:c(he)(typeof e.image=="string"?e.image:e.image.src),alt:e.alt??(typeof e.image=="string"?"":e.image.alt||"")}),null,16,At)):(a(),i(S,{key:1},[h(s,se({class:"dark",image:e.image.dark,alt:e.image.alt},t.$attrs),null,16,["image","alt"]),h(s,se({class:"light",image:e.image.light,alt:e.image.alt},t.$attrs),null,16,["image","alt"])],64))],64)):m("",!0)}}});const be=p(Et,[["__scopeId","data-v-6db2186b"]]),zt=["href"],Dt=g({__name:"VPNavBarTitle",setup(e){const{site:t,theme:n}=V(),{hasSidebar:s}=z(),{currentLang:o}=te();return(l,d)=>(a(),i("div",{class:M(["VPNavBarTitle",{"has-sidebar":c(s)}])},[u("a",{class:"title",href:c(Z)(c(o).link)},[r(l.$slots,"nav-bar-title-before",{},void 0,!0),c(n).logo?(a(),y(be,{key:0,class:"logo",image:c(n).logo},null,8,["image"])):m("",!0),c(n).siteTitle?(a(),i(S,{key:1},[I(P(c(n).siteTitle),1)],64)):c(n).siteTitle===void 0?(a(),i(S,{key:2},[I(P(c(t).title),1)],64)):m("",!0),r(l.$slots,"nav-bar-title-after",{},void 0,!0)],8,zt)],2))}});const Ft=p(Dt,[["__scopeId","data-v-f4ef19a3"]]);const Ot={type:"button",class:"DocSearch DocSearch-Button","aria-label":"Search"},Gt={class:"DocSearch-Button-Container"},Rt=u("svg",{class:"DocSearch-Search-Icon",width:"20",height:"20",viewBox:"0 0 20 20","aria-label":"search icon"},[u("path",{d:"M14.386 14.386l4.0877 4.0877-4.0877-4.0877c-2.9418 2.9419-7.7115 2.9419-10.6533 0-2.9419-2.9418-2.9419-7.7115 0-10.6533 2.9418-2.9419 7.7115-2.9419 10.6533 0 2.9419 2.9418 2.9419 7.7115 0 10.6533z",stroke:"currentColor",fill:"none","fill-rule":"evenodd","stroke-linecap":"round","stroke-linejoin":"round"})],-1),Ut={class:"DocSearch-Button-Placeholder"},jt=u("span",{class:"DocSearch-Button-Keys"},[u("kbd",{class:"DocSearch-Button-Key"}),u("kbd",{class:"DocSearch-Button-Key"},"K")],-1),Ie=g({__name:"VPNavBarSearchButton",props:{placeholder:null},setup(e){return(t,n)=>(a(),i("button",Ot,[u("span",Gt,[Rt,u("span",Ut,P(e.placeholder),1)]),jt]))}});const qt={id:"local-search"},Kt={key:1,id:"docsearch"},Wt=g({__name:"VPNavBarSearch",setup(e){const t=()=>null,n=()=>null,{theme:s,localeIndex:o}=V(),l=x(!1),d=k(()=>{var $,B,T,A,D,ne,W;const w=(($=s.value.search)==null?void 0:$.options)??s.value.algolia;return((D=(A=(T=(B=w==null?void 0:w.locales)==null?void 0:B[o.value])==null?void 0:T.translations)==null?void 0:A.button)==null?void 0:D.buttonText)||((W=(ne=w==null?void 0:w.translations)==null?void 0:ne.button)==null?void 0:W.buttonText)||"Search"});F(()=>{});function f(){l.value||(l.value=!0,setTimeout(_,16))}function _(){const w=new Event("keydown");w.key="k",w.metaKey=!0,window.dispatchEvent(w),setTimeout(()=>{document.querySelector(".DocSearch-Modal")||_()},16)}const b=x(!1),L=x("'Meta'");F(()=>{L.value=/(Mac|iPhone|iPod|iPad)/i.test(navigator.platform)?"'⌘'":"'Ctrl'"});const C="";return(w,$)=>{var B;return a(),i("div",{class:"VPNavBarSearch",style:He({"--vp-meta-key":L.value})},[c(C)==="local"?(a(),i(S,{key:0},[b.value?(a(),y(c(t),{key:0,placeholder:c(d),onClose:$[0]||($[0]=T=>b.value=!1)},null,8,["placeholder"])):m("",!0),u("div",qt,[h(Ie,{placeholder:c(d),onClick:$[1]||($[1]=T=>b.value=!0)},null,8,["placeholder"])])],64)):c(C)==="algolia"?(a(),i(S,{key:1},[l.value?(a(),y(c(n),{key:0,algolia:((B=c(s).search)==null?void 0:B.options)??c(s).algolia},null,8,["algolia"])):(a(),i("div",Kt,[h(Ie,{placeholder:c(d),onClick:f},null,8,["placeholder"])]))],64)):m("",!0)],4)}}});const Yt={},Xt={xmlns:"http://www.w3.org/2000/svg","aria-hidden":"true",focusable:"false",height:"24px",viewBox:"0 0 24 24",width:"24px"},Qt=u("path",{d:"M0 0h24v24H0V0z",fill:"none"},null,-1),Jt=u("path",{d:"M9 5v2h6.59L4 18.59 5.41 20 17 8.41V15h2V5H9z"},null,-1),Zt=[Qt,Jt];function en(e,t){return a(),i("svg",Xt,Zt)}const tn=p(Yt,[["render",en]]),nn=g({__name:"VPLink",props:{tag:null,href:null,noIcon:{type:Boolean},target:null,rel:null},setup(e){const t=e,n=k(()=>t.tag??t.href?"a":"span"),s=k(()=>t.href&&Ee.test(t.href));return(o,l)=>(a(),y(U(c(n)),{class:M(["VPLink",{link:e.href}]),href:e.href?c(Z)(e.href):void 0,target:e.target||(c(s)?"_blank":void 0),rel:e.rel||(c(s)?"noreferrer":void 0)},{default:v(()=>[r(o.$slots,"default",{},void 0,!0),c(s)&&!e.noIcon?(a(),y(tn,{key:0,class:"icon"})):m("",!0)]),_:3},8,["class","href","target","rel"]))}});const O=p(nn,[["__scopeId","data-v-8f4dc553"]]),sn=g({__name:"VPNavBarMenuLink",props:{item:null},setup(e){const{page:t}=V();return(n,s)=>(a(),y(O,{class:M({VPNavBarMenuLink:!0,active:c(K)(c(t).relativePath,e.item.activeMatch||e.item.link,!!e.item.activeMatch)}),href:e.item.link,target:e.item.target,rel:e.item.rel,tabindex:"0"},{default:v(()=>[I(P(e.item.text),1)]),_:1},8,["class","href","target","rel"]))}});const on=p(sn,[["__scopeId","data-v-37adc828"]]),ke=x();let Ge=!1,ue=0;function an(e){const t=x(!1);if(at){!Ge&&cn(),ue++;const n=q(ke,s=>{var o,l,d;s===e.el.value||(o=e.el.value)!=null&&o.contains(s)?(t.value=!0,(l=e.onFocus)==null||l.call(e)):(t.value=!1,(d=e.onBlur)==null||d.call(e))});fe(()=>{n(),ue--,ue||ln()})}return Ae(t)}function cn(){document.addEventListener("focusin",Re),Ge=!0,ke.value=document.activeElement}function ln(){document.removeEventListener("focusin",Re)}function Re(){ke.value=document.activeElement}const rn={},un={xmlns:"http://www.w3.org/2000/svg","aria-hidden":"true",focusable:"false",viewBox:"0 0 24 24"},dn=u("path",{d:"M12,16c-0.3,0-0.5-0.1-0.7-0.3l-6-6c-0.4-0.4-0.4-1,0-1.4s1-0.4,1.4,0l5.3,5.3l5.3-5.3c0.4-0.4,1-0.4,1.4,0s0.4,1,0,1.4l-6,6C12.5,15.9,12.3,16,12,16z"},null,-1),_n=[dn];function vn(e,t){return a(),i("svg",un,_n)}const Ue=p(rn,[["render",vn]]),hn={},fn={xmlns:"http://www.w3.org/2000/svg","aria-hidden":"true",focusable:"false",viewBox:"0 0 24 24"},pn=u("circle",{cx:"12",cy:"12",r:"2"},null,-1),mn=u("circle",{cx:"19",cy:"12",r:"2"},null,-1),gn=u("circle",{cx:"5",cy:"12",r:"2"},null,-1),yn=[pn,mn,gn];function bn(e,t){return a(),i("svg",fn,yn)}const kn=p(hn,[["render",bn]]),$n={class:"VPMenuLink"},Pn=g({__name:"VPMenuLink",props:{item:null},setup(e){const{page:t}=V();return(n,s)=>(a(),i("div",$n,[h(O,{class:M({active:c(K)(c(t).relativePath,e.item.activeMatch||e.item.link,!!e.item.activeMatch)}),href:e.item.link,target:e.item.target,rel:e.item.rel},{default:v(()=>[I(P(e.item.text),1)]),_:1},8,["class","href","target","rel"])]))}});const ie=p(Pn,[["__scopeId","data-v-d2c93bab"]]),Vn={class:"VPMenuGroup"},wn={key:0,class:"title"},xn=g({__name:"VPMenuGroup",props:{text:null,items:null},setup(e){return(t,n)=>(a(),i("div",Vn,[e.text?(a(),i("p",wn,P(e.text),1)):m("",!0),(a(!0),i(S,null,N(e.items,s=>(a(),i(S,null,["link"in s?(a(),y(ie,{key:0,item:s},null,8,["item"])):m("",!0)],64))),256))]))}});const Sn=p(xn,[["__scopeId","data-v-69e747b5"]]),Ln={class:"VPMenu"},Mn={key:0,class:"items"},Cn=g({__name:"VPMenu",props:{items:null},setup(e){return(t,n)=>(a(),i("div",Ln,[e.items?(a(),i("div",Mn,[(a(!0),i(S,null,N(e.items,s=>(a(),i(S,{key:s.text},["link"in s?(a(),y(ie,{key:0,item:s},null,8,["item"])):(a(),y(Sn,{key:1,text:s.text,items:s.items},null,8,["text","items"]))],64))),128))])):m("",!0),r(t.$slots,"default",{},void 0,!0)]))}});const Bn=p(Cn,[["__scopeId","data-v-e7ea1737"]]),In=["aria-expanded","aria-label"],Nn={key:0,class:"text"},Tn={class:"menu"},An=g({__name:"VPFlyout",props:{icon:null,button:null,label:null,items:null},setup(e){const t=x(!1),n=x();an({el:n,onBlur:s});function s(){t.value=!1}return(o,l)=>(a(),i("div",{class:"VPFlyout",ref_key:"el",ref:n,onMouseenter:l[1]||(l[1]=d=>t.value=!0),onMouseleave:l[2]||(l[2]=d=>t.value=!1)},[u("button",{type:"button",class:"button","aria-haspopup":"true","aria-expanded":t.value,"aria-label":e.label,onClick:l[0]||(l[0]=d=>t.value=!t.value)},[e.button||e.icon?(a(),i("span",Nn,[e.icon?(a(),y(U(e.icon),{key:0,class:"option-icon"})):m("",!0),I(" "+P(e.button)+" ",1),h(Ue,{class:"text-icon"})])):(a(),y(kn,{key:1,class:"icon"}))],8,In),u("div",Tn,[h(Bn,{items:e.items},{default:v(()=>[r(o.$slots,"default",{},void 0,!0)]),_:3},8,["items"])])],544))}});const $e=p(An,[["__scopeId","data-v-764effdf"]]),Hn=g({__name:"VPNavBarMenuGroup",props:{item:null},setup(e){const{page:t}=V();return(n,s)=>(a(),y($e,{class:M({VPNavBarMenuGroup:!0,active:c(K)(c(t).relativePath,e.item.activeMatch,!!e.item.activeMatch)}),button:e.item.text,items:e.item.items},null,8,["class","button","items"]))}}),En=e=>(H("data-v-7f418b0f"),e=e(),E(),e),zn={key:0,"aria-labelledby":"main-nav-aria-label",class:"VPNavBarMenu"},Dn=En(()=>u("span",{id:"main-nav-aria-label",class:"visually-hidden"},"Main Navigation",-1)),Fn=g({__name:"VPNavBarMenu",setup(e){const{theme:t}=V();return(n,s)=>c(t).nav?(a(),i("nav",zn,[Dn,(a(!0),i(S,null,N(c(t).nav,o=>(a(),i(S,{key:o.text},["link"in o?(a(),y(on,{key:0,item:o},null,8,["item"])):(a(),y(Hn,{key:1,item:o},null,8,["item"]))],64))),128))])):m("",!0)}});const On=p(Fn,[["__scopeId","data-v-7f418b0f"]]),Gn={},Rn={xmlns:"http://www.w3.org/2000/svg","aria-hidden":"true",focusable:"false",viewBox:"0 0 24 24"},Un=u("path",{d:"M0 0h24v24H0z",fill:"none"},null,-1),jn=u("path",{d:" M12.87 15.07l-2.54-2.51.03-.03c1.74-1.94 2.98-4.17 3.71-6.53H17V4h-7V2H8v2H1v1.99h11.17C11.5 7.92 10.44 9.75 9 11.35 8.07 10.32 7.3 9.19 6.69 8h-2c.73 1.63 1.73 3.17 2.98 4.56l-5.09 5.02L4 19l5-5 3.11 3.11.76-2.04zM18.5 10h-2L12 22h2l1.12-3h4.75L21 22h2l-4.5-12zm-2.62 7l1.62-4.33L19.12 17h-3.24z ",class:"css-c4d79v"},null,-1),qn=[Un,jn];function Kn(e,t){return a(),i("svg",Rn,qn)}const je=p(Gn,[["render",Kn]]),Wn={class:"items"},Yn={class:"title"},Xn=g({__name:"VPNavBarTranslations",setup(e){const{theme:t}=V(),{localeLinks:n,currentLang:s}=te({correspondingLink:!0});return(o,l)=>c(n).length&&c(s).label?(a(),y($e,{key:0,class:"VPNavBarTranslations",icon:je,label:c(t).langMenuLabel||"Change language"},{default:v(()=>[u("div",Wn,[u("p",Yn,P(c(s).label),1),(a(!0),i(S,null,N(c(n),d=>(a(),y(ie,{key:d.link,item:d},null,8,["item"]))),128))])]),_:1},8,["label"])):m("",!0)}});const Qn=p(Xn,[["__scopeId","data-v-74abcbb9"]]);const Jn={},Zn={class:"VPSwitch",type:"button",role:"switch"},es={class:"check"},ts={key:0,class:"icon"};function ns(e,t){return a(),i("button",Zn,[u("span",es,[e.$slots.default?(a(),i("span",ts,[r(e.$slots,"default",{},void 0,!0)])):m("",!0)])])}const ss=p(Jn,[["render",ns],["__scopeId","data-v-f3c41672"]]),os={},as={xmlns:"http://www.w3.org/2000/svg","aria-hidden":"true",focusable:"false",viewBox:"0 0 24 24"},cs=ct('',9),ls=[cs];function is(e,t){return a(),i("svg",as,ls)}const rs=p(os,[["render",is]]),us={},ds={xmlns:"http://www.w3.org/2000/svg","aria-hidden":"true",focusable:"false",viewBox:"0 0 24 24"},_s=u("path",{d:"M12.1,22c-0.3,0-0.6,0-0.9,0c-5.5-0.5-9.5-5.4-9-10.9c0.4-4.8,4.2-8.6,9-9c0.4,0,0.8,0.2,1,0.5c0.2,0.3,0.2,0.8-0.1,1.1c-2,2.7-1.4,6.4,1.3,8.4c2.1,1.6,5,1.6,7.1,0c0.3-0.2,0.7-0.3,1.1-0.1c0.3,0.2,0.5,0.6,0.5,1c-0.2,2.7-1.5,5.1-3.6,6.8C16.6,21.2,14.4,22,12.1,22zM9.3,4.4c-2.9,1-5,3.6-5.2,6.8c-0.4,4.4,2.8,8.3,7.2,8.7c2.1,0.2,4.2-0.4,5.8-1.8c1.1-0.9,1.9-2.1,2.4-3.4c-2.5,0.9-5.3,0.5-7.5-1.1C9.2,11.4,8.1,7.7,9.3,4.4z"},null,-1),vs=[_s];function hs(e,t){return a(),i("svg",ds,vs)}const fs=p(us,[["render",hs]]),ps={title:"toggle dark mode"},ms=g({__name:"VPSwitchAppearance",setup(e){const{site:t,isDark:n}=V(),s=x(!1),o=typeof localStorage<"u"?l():()=>{};F(()=>{s.value=document.documentElement.classList.contains("dark")});function l(){const d=window.matchMedia("(prefers-color-scheme: dark)"),f=document.documentElement.classList;let _=localStorage.getItem(Be),b=t.value.appearance==="dark"&&_==null||(_==="auto"||_==null?d.matches:_==="dark");d.onchange=w=>{_==="auto"&&C(b=w.matches)};function L(){C(b=!b),_=b?d.matches?"auto":"dark":d.matches?"light":"auto",localStorage.setItem(Be,_)}function C(w){const $=document.createElement("style");$.type="text/css",$.appendChild(document.createTextNode(`:not(.VPSwitchAppearance):not(.VPSwitchAppearance *) { - -webkit-transition: none !important; - -moz-transition: none !important; - -o-transition: none !important; - -ms-transition: none !important; - transition: none !important; -}`)),document.head.appendChild($),s.value=w,f[w?"add":"remove"]("dark"),window.getComputedStyle($).opacity,document.head.removeChild($)}return L}return q(s,d=>{n.value=d}),(d,f)=>(a(),i("label",ps,[h(ss,{class:"VPSwitchAppearance","aria-checked":s.value,onClick:c(o)},{default:v(()=>[h(rs,{class:"sun"}),h(fs,{class:"moon"})]),_:1},8,["aria-checked","onClick"])]))}});const Pe=p(ms,[["__scopeId","data-v-a9c8afb8"]]),gs={key:0,class:"VPNavBarAppearance"},ys=g({__name:"VPNavBarAppearance",setup(e){const{site:t}=V();return(n,s)=>c(t).appearance?(a(),i("div",gs,[h(Pe)])):m("",!0)}});const bs=p(ys,[["__scopeId","data-v-f6a63727"]]),ks={discord:'Discord',facebook:'Facebook',github:'GitHub',instagram:'Instagram',linkedin:'LinkedIn',mastodon:'Mastodon',slack:'Slack',twitter:'Twitter',youtube:'YouTube'},$s=["href","aria-label","innerHTML"],Ps=g({__name:"VPSocialLink",props:{icon:null,link:null},setup(e){const t=e,n=k(()=>typeof t.icon=="object"?t.icon.svg:ks[t.icon]);return(s,o)=>(a(),i("a",{class:"VPSocialLink",href:e.link,"aria-label":typeof e.icon=="string"?e.icon:"",target:"_blank",rel:"noopener",innerHTML:c(n)},null,8,$s))}});const Vs=p(Ps,[["__scopeId","data-v-c530cc0a"]]),ws={class:"VPSocialLinks"},xs=g({__name:"VPSocialLinks",props:{links:null},setup(e){return(t,n)=>(a(),i("div",ws,[(a(!0),i(S,null,N(e.links,({link:s,icon:o})=>(a(),y(Vs,{key:s,icon:o,link:s},null,8,["icon","link"]))),128))]))}});const Ve=p(xs,[["__scopeId","data-v-f6988cfb"]]),Ss=g({__name:"VPNavBarSocialLinks",setup(e){const{theme:t}=V();return(n,s)=>c(t).socialLinks?(a(),y(Ve,{key:0,class:"VPNavBarSocialLinks",links:c(t).socialLinks},null,8,["links"])):m("",!0)}});const Ls=p(Ss,[["__scopeId","data-v-0394ad82"]]),Ms={key:0,class:"group translations"},Cs={class:"trans-title"},Bs={key:1,class:"group"},Is={class:"item appearance"},Ns={class:"label"},Ts={class:"appearance-action"},As={key:2,class:"group"},Hs={class:"item social-links"},Es=g({__name:"VPNavBarExtra",setup(e){const{site:t,theme:n}=V(),{localeLinks:s,currentLang:o}=te({correspondingLink:!0}),l=k(()=>s.value.length&&o.value.label||t.value.appearance||n.value.socialLinks);return(d,f)=>c(l)?(a(),y($e,{key:0,class:"VPNavBarExtra",label:"extra navigation"},{default:v(()=>[c(s).length&&c(o).label?(a(),i("div",Ms,[u("p",Cs,P(c(o).label),1),(a(!0),i(S,null,N(c(s),_=>(a(),y(ie,{key:_.link,item:_},null,8,["item"]))),128))])):m("",!0),c(t).appearance?(a(),i("div",Bs,[u("div",Is,[u("p",Ns,P(c(n).darkModeSwitchLabel||"Appearance"),1),u("div",Ts,[h(Pe)])])])):m("",!0),c(n).socialLinks?(a(),i("div",As,[u("div",Hs,[h(Ve,{class:"social-links-list",links:c(n).socialLinks},null,8,["links"])])])):m("",!0)]),_:1})):m("",!0)}});const zs=p(Es,[["__scopeId","data-v-40855f84"]]),Ds=e=>(H("data-v-e5dd9c1c"),e=e(),E(),e),Fs=["aria-expanded"],Os=Ds(()=>u("span",{class:"container"},[u("span",{class:"top"}),u("span",{class:"middle"}),u("span",{class:"bottom"})],-1)),Gs=[Os],Rs=g({__name:"VPNavBarHamburger",props:{active:{type:Boolean}},emits:["click"],setup(e){return(t,n)=>(a(),i("button",{type:"button",class:M(["VPNavBarHamburger",{active:e.active}]),"aria-label":"mobile navigation","aria-expanded":e.active,"aria-controls":"VPNavScreen",onClick:n[0]||(n[0]=s=>t.$emit("click"))},Gs,10,Fs))}});const Us=p(Rs,[["__scopeId","data-v-e5dd9c1c"]]),js=e=>(H("data-v-94c81dcc"),e=e(),E(),e),qs={class:"container"},Ks={class:"title"},Ws={class:"content"},Ys=js(()=>u("div",{class:"curtain"},null,-1)),Xs={class:"content-body"},Qs=g({__name:"VPNavBar",props:{isScreenOpen:{type:Boolean}},emits:["toggle-screen"],setup(e){const{y:t}=$t(),{hasSidebar:n}=z(),s=k(()=>({"has-sidebar":n.value,fill:t.value>0}));return(o,l)=>(a(),i("div",{class:M(["VPNavBar",c(s)])},[u("div",qs,[u("div",Ks,[h(Ft,null,{"nav-bar-title-before":v(()=>[r(o.$slots,"nav-bar-title-before",{},void 0,!0)]),"nav-bar-title-after":v(()=>[r(o.$slots,"nav-bar-title-after",{},void 0,!0)]),_:3})]),u("div",Ws,[Ys,u("div",Xs,[r(o.$slots,"nav-bar-content-before",{},void 0,!0),h(Wt,{class:"search"}),h(On,{class:"menu"}),h(Qn,{class:"translations"}),h(bs,{class:"appearance"}),h(Ls,{class:"social-links"}),h(zs,{class:"extra"}),r(o.$slots,"nav-bar-content-after",{},void 0,!0),h(Us,{class:"hamburger",active:e.isScreenOpen,onClick:l[0]||(l[0]=d=>o.$emit("toggle-screen"))},null,8,["active"])])])])],2))}});const Js=p(Qs,[["__scopeId","data-v-94c81dcc"]]);function Zs(e){if(Array.isArray(e)){for(var t=0,n=Array(e.length);t1),j=[],ce=!1,xe=-1,Y=void 0,G=void 0,X=void 0,qe=function(t){return j.some(function(n){return!!(n.options.allowTouchMove&&n.options.allowTouchMove(t))})},le=function(t){var n=t||window.event;return qe(n.target)||n.touches.length>1?!0:(n.preventDefault&&n.preventDefault(),!1)},eo=function(t){if(X===void 0){var n=!!t&&t.reserveScrollBarGap===!0,s=window.innerWidth-document.documentElement.clientWidth;if(n&&s>0){var o=parseInt(window.getComputedStyle(document.body).getPropertyValue("padding-right"),10);X=document.body.style.paddingRight,document.body.style.paddingRight=o+s+"px"}}Y===void 0&&(Y=document.body.style.overflow,document.body.style.overflow="hidden")},to=function(){X!==void 0&&(document.body.style.paddingRight=X,X=void 0),Y!==void 0&&(document.body.style.overflow=Y,Y=void 0)},no=function(){return window.requestAnimationFrame(function(){if(G===void 0){G={position:document.body.style.position,top:document.body.style.top,left:document.body.style.left};var t=window,n=t.scrollY,s=t.scrollX,o=t.innerHeight;document.body.style.position="fixed",document.body.style.top=-n,document.body.style.left=-s,setTimeout(function(){return window.requestAnimationFrame(function(){var l=o-window.innerHeight;l&&n>=o&&(document.body.style.top=-(n+l))})},300)}})},so=function(){if(G!==void 0){var t=-parseInt(document.body.style.top,10),n=-parseInt(document.body.style.left,10);document.body.style.position=G.position,document.body.style.top=G.top,document.body.style.left=G.left,window.scrollTo(n,t),G=void 0}},oo=function(t){return t?t.scrollHeight-t.scrollTop<=t.clientHeight:!1},ao=function(t,n){var s=t.targetTouches[0].clientY-xe;return qe(t.target)?!1:n&&n.scrollTop===0&&s>0||oo(n)&&s<0?le(t):(t.stopPropagation(),!0)},Ke=function(t,n){if(!t){console.error("disableBodyScroll unsuccessful - targetElement must be provided when calling disableBodyScroll on IOS devices.");return}if(!j.some(function(o){return o.targetElement===t})){var s={targetElement:t,options:n||{}};j=[].concat(Zs(j),[s]),ae?no():eo(n),ae&&(t.ontouchstart=function(o){o.targetTouches.length===1&&(xe=o.targetTouches[0].clientY)},t.ontouchmove=function(o){o.targetTouches.length===1&&ao(o,t)},ce||(document.addEventListener("touchmove",le,we?{passive:!1}:void 0),ce=!0))}},We=function(){ae&&(j.forEach(function(t){t.targetElement.ontouchstart=null,t.targetElement.ontouchmove=null}),ce&&(document.removeEventListener("touchmove",le,we?{passive:!1}:void 0),ce=!1),xe=-1),ae?so():to(),j=[]};const co=g({__name:"VPNavScreenMenuLink",props:{text:null,link:null},setup(e){const t=me("close-screen");return(n,s)=>(a(),y(O,{class:"VPNavScreenMenuLink",href:e.link,onClick:c(t)},{default:v(()=>[I(P(e.text),1)]),_:1},8,["href","onClick"]))}});const lo=p(co,[["__scopeId","data-v-c328f34f"]]),io={},ro={xmlns:"http://www.w3.org/2000/svg","aria-hidden":"true",focusable:"false",viewBox:"0 0 24 24"},uo=u("path",{d:"M18.9,10.9h-6v-6c0-0.6-0.4-1-1-1s-1,0.4-1,1v6h-6c-0.6,0-1,0.4-1,1s0.4,1,1,1h6v6c0,0.6,0.4,1,1,1s1-0.4,1-1v-6h6c0.6,0,1-0.4,1-1S19.5,10.9,18.9,10.9z"},null,-1),_o=[uo];function vo(e,t){return a(),i("svg",ro,_o)}const ho=p(io,[["render",vo]]),fo=g({__name:"VPNavScreenMenuGroupLink",props:{text:null,link:null},setup(e){const t=me("close-screen");return(n,s)=>(a(),y(O,{class:"VPNavScreenMenuGroupLink",href:e.link,onClick:c(t)},{default:v(()=>[I(P(e.text),1)]),_:1},8,["href","onClick"]))}});const Ye=p(fo,[["__scopeId","data-v-3d20956d"]]),po={class:"VPNavScreenMenuGroupSection"},mo={key:0,class:"title"},go=g({__name:"VPNavScreenMenuGroupSection",props:{text:null,items:null},setup(e){return(t,n)=>(a(),i("div",po,[e.text?(a(),i("p",mo,P(e.text),1)):m("",!0),(a(!0),i(S,null,N(e.items,s=>(a(),y(Ye,{key:s.text,text:s.text,link:s.link},null,8,["text","link"]))),128))]))}});const yo=p(go,[["__scopeId","data-v-7478538b"]]),bo=["aria-controls","aria-expanded"],ko={class:"button-text"},$o=["id"],Po={key:1,class:"group"},Vo=g({__name:"VPNavScreenMenuGroup",props:{text:null,items:null},setup(e){const t=e,n=x(!1),s=k(()=>`NavScreenGroup-${t.text.replace(" ","-").toLowerCase()}`);function o(){n.value=!n.value}return(l,d)=>(a(),i("div",{class:M(["VPNavScreenMenuGroup",{open:n.value}])},[u("button",{class:"button","aria-controls":c(s),"aria-expanded":n.value,onClick:o},[u("span",ko,P(e.text),1),h(ho,{class:"button-icon"})],8,bo),u("div",{id:c(s),class:"items"},[(a(!0),i(S,null,N(e.items,f=>(a(),i(S,{key:f.text},["link"in f?(a(),i("div",{key:f.text,class:"item"},[h(Ye,{text:f.text,link:f.link},null,8,["text","link"])])):(a(),i("div",Po,[h(yo,{text:f.text,items:f.items},null,8,["text","items"])]))],64))),128))],8,$o)],2))}});const wo=p(Vo,[["__scopeId","data-v-a9a19324"]]),xo={key:0,class:"VPNavScreenMenu"},So=g({__name:"VPNavScreenMenu",setup(e){const{theme:t}=V();return(n,s)=>c(t).nav?(a(),i("nav",xo,[(a(!0),i(S,null,N(c(t).nav,o=>(a(),i(S,{key:o.text},["link"in o?(a(),y(lo,{key:0,text:o.text,link:o.link},null,8,["text","link"])):(a(),y(wo,{key:1,text:o.text||"",items:o.items},null,8,["text","items"]))],64))),128))])):m("",!0)}}),Lo={key:0,class:"VPNavScreenAppearance"},Mo={class:"text"},Co=g({__name:"VPNavScreenAppearance",setup(e){const{site:t,theme:n}=V();return(s,o)=>c(t).appearance?(a(),i("div",Lo,[u("p",Mo,P(c(n).darkModeSwitchLabel||"Appearance"),1),h(Pe)])):m("",!0)}});const Bo=p(Co,[["__scopeId","data-v-add8f686"]]),Io={class:"list"},No=g({__name:"VPNavScreenTranslations",setup(e){const{localeLinks:t,currentLang:n}=te({correspondingLink:!0}),s=x(!1);function o(){s.value=!s.value}return(l,d)=>c(t).length&&c(n).label?(a(),i("div",{key:0,class:M(["VPNavScreenTranslations",{open:s.value}])},[u("button",{class:"title",onClick:o},[h(je,{class:"icon lang"}),I(" "+P(c(n).label)+" ",1),h(Ue,{class:"icon chevron"})]),u("ul",Io,[(a(!0),i(S,null,N(c(t),f=>(a(),i("li",{key:f.link,class:"item"},[h(O,{class:"link",href:f.link},{default:v(()=>[I(P(f.text),1)]),_:2},1032,["href"])]))),128))])],2)):m("",!0)}});const To=p(No,[["__scopeId","data-v-d72aa483"]]),Ao=g({__name:"VPNavScreenSocialLinks",setup(e){const{theme:t}=V();return(n,s)=>c(t).socialLinks?(a(),y(Ve,{key:0,class:"VPNavScreenSocialLinks",links:c(t).socialLinks},null,8,["links"])):m("",!0)}}),Ho={class:"container"},Eo=g({__name:"VPNavScreen",props:{open:{type:Boolean}},setup(e){const t=x(null);function n(){Ke(t.value,{reserveScrollBarGap:!0})}function s(){We()}return(o,l)=>(a(),y(pe,{name:"fade",onEnter:n,onAfterLeave:s},{default:v(()=>[e.open?(a(),i("div",{key:0,class:"VPNavScreen",ref_key:"screen",ref:t},[u("div",Ho,[r(o.$slots,"nav-screen-content-before",{},void 0,!0),h(So,{class:"menu"}),h(To,{class:"translations"}),h(Bo,{class:"appearance"}),h(Ao,{class:"social-links"}),r(o.$slots,"nav-screen-content-after",{},void 0,!0)])],512)):m("",!0)]),_:3}))}});const zo=p(Eo,[["__scopeId","data-v-724636ae"]]),Do={class:"VPNav"},Fo=g({__name:"VPNav",setup(e){const{isScreenOpen:t,closeScreen:n,toggleScreen:s}=Nt();return oe("close-screen",n),(o,l)=>(a(),i("header",Do,[h(Js,{"is-screen-open":c(t),onToggleScreen:c(s)},{"nav-bar-title-before":v(()=>[r(o.$slots,"nav-bar-title-before",{},void 0,!0)]),"nav-bar-title-after":v(()=>[r(o.$slots,"nav-bar-title-after",{},void 0,!0)]),"nav-bar-content-before":v(()=>[r(o.$slots,"nav-bar-content-before",{},void 0,!0)]),"nav-bar-content-after":v(()=>[r(o.$slots,"nav-bar-content-after",{},void 0,!0)]),_:3},8,["is-screen-open","onToggleScreen"]),h(zo,{open:c(t)},{"nav-screen-content-before":v(()=>[r(o.$slots,"nav-screen-content-before",{},void 0,!0)]),"nav-screen-content-after":v(()=>[r(o.$slots,"nav-screen-content-after",{},void 0,!0)]),_:3},8,["open"])]))}});const Oo=p(Fo,[["__scopeId","data-v-7e5bc4a5"]]),Go={},Ro={xmlns:"http://www.w3.org/2000/svg","aria-hidden":"true",focusable:"false",viewBox:"0 0 24 24"},Uo=u("path",{d:"M17,11H3c-0.6,0-1-0.4-1-1s0.4-1,1-1h14c0.6,0,1,0.4,1,1S17.6,11,17,11z"},null,-1),jo=u("path",{d:"M21,7H3C2.4,7,2,6.6,2,6s0.4-1,1-1h18c0.6,0,1,0.4,1,1S21.6,7,21,7z"},null,-1),qo=u("path",{d:"M21,15H3c-0.6,0-1-0.4-1-1s0.4-1,1-1h18c0.6,0,1,0.4,1,1S21.6,15,21,15z"},null,-1),Ko=u("path",{d:"M17,19H3c-0.6,0-1-0.4-1-1s0.4-1,1-1h14c0.6,0,1,0.4,1,1S17.6,19,17,19z"},null,-1),Wo=[Uo,jo,qo,Ko];function Yo(e,t){return a(),i("svg",Ro,Wo)}const Xo=p(Go,[["render",Yo]]);function Qo(){const{hasSidebar:e}=z(),t=de("(min-width: 960px)"),n=de("(min-width: 1280px)");return{isAsideEnabled:k(()=>!n.value&&!t.value?!1:e.value?n.value:t.value)}}const Jo=71;function Se(e){return typeof e.outline=="object"&&!Array.isArray(e.outline)&&e.outline.label||e.outlineTitle||"On this page"}function Le(e){const t=[...document.querySelectorAll(".VPDoc h2,h3,h4,h5,h6")].filter(n=>n.id&&n.hasChildNodes()).map(n=>{const s=Number(n.tagName[1]);return{title:Zo(n),link:"#"+n.id,level:s}});return ea(t,e)}function Zo(e){let t="";for(const n of e.childNodes)if(n.nodeType===1){if(n.classList.contains("VPBadge")||n.classList.contains("header-anchor"))continue;t+=n.textContent}else n.nodeType===3&&(t+=n.textContent);return t.trim()}function ea(e,t){if(t===!1)return[];const n=(typeof t=="object"&&!Array.isArray(t)?t.level:t)||2,[s,o]=typeof n=="number"?[n,n]:n==="deep"?[2,6]:n;e=e.filter(d=>d.level>=s&&d.level<=o);const l=[];e:for(let d=0;d=0;_--){const b=e[_];if(b.level{requestAnimationFrame(l),window.addEventListener("scroll",s)}),lt(()=>{d(location.hash)}),fe(()=>{window.removeEventListener("scroll",s)});function l(){if(!n.value)return;const f=[].slice.call(e.value.querySelectorAll(".outline-link")),_=[].slice.call(document.querySelectorAll(".content .header-anchor")).filter($=>f.some(B=>B.hash===$.hash&&$.offsetParent!==null)),b=window.scrollY,L=window.innerHeight,C=document.body.offsetHeight,w=Math.abs(b+L-C)<1;if(_.length&&w){d(_[_.length-1].hash);return}for(let $=0;$<_.length;$++){const B=_[$],T=_[$+1],[A,D]=na($,B,T);if(A){d(D);return}}}function d(f){o&&o.classList.remove("active"),f!==null&&(o=e.value.querySelector(`a[href="${decodeURIComponent(f)}"]`));const _=o;_?(_.classList.add("active"),t.value.style.top=_.offsetTop+33+"px",t.value.style.opacity="1"):(t.value.style.top="33px",t.value.style.opacity="0")}}function Te(e){return e.parentElement.offsetTop-Jo}function na(e,t,n){const s=window.scrollY;return e===0&&s===0?[!0,null]:s{const o=R("VPDocOutlineItem",!0);return a(),i("ul",{class:M(e.root?"root":"nested")},[(a(!0),i(S,null,N(e.headers,({children:l,link:d,title:f})=>(a(),i("li",null,[u("a",{class:"outline-link",href:d,onClick:t,title:f},P(f),9,sa),l!=null&&l.length?(a(),y(o,{key:0,headers:l},null,8,["headers"])):m("",!0)]))),256))],2)}}});const Me=p(oa,[["__scopeId","data-v-9a431c33"]]),aa={},ca={xmlns:"http://www.w3.org/2000/svg","aria-hidden":"true",focusable:"false",viewBox:"0 0 24 24"},la=u("path",{d:"M9,19c-0.3,0-0.5-0.1-0.7-0.3c-0.4-0.4-0.4-1,0-1.4l5.3-5.3L8.3,6.7c-0.4-0.4-0.4-1,0-1.4s1-0.4,1.4,0l6,6c0.4,0.4,0.4,1,0,1.4l-6,6C9.5,18.9,9.3,19,9,19z"},null,-1),ia=[la];function ra(e,t){return a(),i("svg",ca,ia)}const Ce=p(aa,[["render",ra]]),ua=g({__name:"VPLocalNavOutlineDropdown",setup(e){const{frontmatter:t,theme:n}=V(),s=x(!1),o=x(0),l=x();J(()=>{s.value=!1});function d(){s.value=!s.value,o.value=window.innerHeight+Math.min(window.scrollY-64,0)}function f(L){L.target.classList.contains("outline-link")&&(l.value&&(l.value.style.transition="none"),it(()=>{s.value=!1}))}function _(){s.value=!1,window.scrollTo({top:0,left:0,behavior:"smooth"})}const b=ge([]);return J(()=>{b.value=Le(t.value.outline??n.value.outline)}),(L,C)=>(a(),i("div",{class:"VPLocalNavOutlineDropdown",style:He({"--vp-vh":o.value+"px"})},[c(b).length>0?(a(),i("button",{key:0,onClick:d,class:M({open:s.value})},[I(P(c(Se)(c(n)))+" ",1),h(Ce,{class:"icon"})],2)):(a(),i("button",{key:1,onClick:_},P(c(n).returnToTopLabel||"Return to top"),1)),h(pe,{name:"flyout"},{default:v(()=>[s.value?(a(),i("div",{key:0,ref_key:"items",ref:l,class:"items",onClick:f},[u("a",{class:"top-link",href:"#",onClick:_},P(c(n).returnToTopLabel||"Return to top"),1),h(Me,{headers:c(b)},null,8,["headers"])],512)):m("",!0)]),_:1})],4))}});const da=p(ua,[["__scopeId","data-v-079b16a8"]]),_a={key:0,class:"VPLocalNav"},va=["aria-expanded"],ha={class:"menu-text"},fa=g({__name:"VPLocalNav",props:{open:{type:Boolean}},emits:["open-menu"],setup(e){const{theme:t,frontmatter:n}=V(),{hasSidebar:s}=z();return(o,l)=>c(n).layout!=="home"?(a(),i("div",_a,[c(s)?(a(),i("button",{key:0,class:"menu","aria-expanded":e.open,"aria-controls":"VPSidebarNav",onClick:l[0]||(l[0]=d=>o.$emit("open-menu"))},[h(Xo,{class:"menu-icon"}),u("span",ha,P(c(t).sidebarMenuLabel||"Menu"),1)],8,va)):m("",!0),h(da)])):m("",!0)}});const pa=p(fa,[["__scopeId","data-v-392e1bf8"]]),ma=e=>(H("data-v-c4656e6d"),e=e(),E(),e),ga=["role","tabindex"],ya=ma(()=>u("div",{class:"indicator"},null,-1)),ba=["onKeydown"],ka={key:1,class:"items"},$a=g({__name:"VPSidebarItem",props:{item:null,depth:null},setup(e){const t=e,{collapsed:n,collapsible:s,isLink:o,isActiveLink:l,hasActiveLink:d,hasChildren:f,toggle:_}=St(k(()=>t.item)),b=k(()=>f.value?"section":"div"),L=k(()=>o.value?"a":"div"),C=k(()=>f.value?t.depth+2===7?"p":`h${t.depth+2}`:"p"),w=k(()=>o.value?void 0:"button"),$=k(()=>[[`level-${t.depth}`],{collapsible:s.value},{collapsed:n.value},{"is-link":o.value},{"is-active":l.value},{"has-active":d.value}]);function B(A){"key"in A&&A.key!=="Enter"||!t.item.link&&_()}function T(){t.item.link&&_()}return(A,D)=>{const ne=R("VPSidebarItem",!0);return a(),y(U(c(b)),{class:M(["VPSidebarItem",c($)])},{default:v(()=>[e.item.text?(a(),i("div",se({key:0,class:"item",role:c(w)},ut(e.item.items?{click:B,keydown:B}:{},!0),{tabindex:e.item.items&&0}),[ya,e.item.link?(a(),y(O,{key:0,tag:c(L),class:"link",href:e.item.link},{default:v(()=>[(a(),y(U(c(C)),{class:"text",innerHTML:e.item.text},null,8,["innerHTML"]))]),_:1},8,["tag","href"])):(a(),y(U(c(C)),{key:1,class:"text",innerHTML:e.item.text},null,8,["innerHTML"])),e.item.collapsed!=null?(a(),i("div",{key:2,class:"caret",role:"button","aria-label":"toggle section",onClick:T,onKeydown:rt(T,["enter"]),tabindex:"0"},[h(Ce,{class:"caret-icon"})],40,ba)):m("",!0)],16,ga)):m("",!0),e.item.items&&e.item.items.length?(a(),i("div",ka,[e.depth<5?(a(!0),i(S,{key:0},N(e.item.items,W=>(a(),y(ne,{key:W.text,item:W,depth:e.depth+1},null,8,["item","depth"]))),128)):m("",!0)])):m("",!0)]),_:1},8,["class"])}}});const Pa=p($a,[["__scopeId","data-v-c4656e6d"]]),Xe=e=>(H("data-v-af16598e"),e=e(),E(),e),Va=Xe(()=>u("div",{class:"curtain"},null,-1)),wa={class:"nav",id:"VPSidebarNav","aria-labelledby":"sidebar-aria-label",tabindex:"-1"},xa=Xe(()=>u("span",{class:"visually-hidden",id:"sidebar-aria-label"}," Sidebar Navigation ",-1)),Sa=g({__name:"VPSidebar",props:{open:{type:Boolean}},setup(e){const t=e,{sidebarGroups:n,hasSidebar:s}=z();let o=x(null);function l(){Ke(o.value,{reserveScrollBarGap:!0})}function d(){We()}return dt(async()=>{var f;t.open?(l(),(f=o.value)==null||f.focus()):d()}),(f,_)=>c(s)?(a(),i("aside",{key:0,class:M(["VPSidebar",{open:e.open}]),ref_key:"navEl",ref:o,onClick:_[0]||(_[0]=_t(()=>{},["stop"]))},[Va,u("nav",wa,[xa,r(f.$slots,"sidebar-nav-before",{},void 0,!0),(a(!0),i(S,null,N(c(n),b=>(a(),i("div",{key:b.text,class:"group"},[h(Pa,{item:b,depth:0},null,8,["item"])]))),128)),r(f.$slots,"sidebar-nav-after",{},void 0,!0)])],2)):m("",!0)}});const La=p(Sa,[["__scopeId","data-v-af16598e"]]),Ma={},Ca={class:"VPPage"};function Ba(e,t){const n=R("Content");return a(),i("div",Ca,[r(e.$slots,"page-top"),h(n),r(e.$slots,"page-bottom")])}const Ia=p(Ma,[["render",Ba]]),Na=g({__name:"VPButton",props:{tag:null,size:null,theme:null,text:null,href:null},setup(e){const t=e,n=k(()=>[t.size??"medium",t.theme??"brand"]),s=k(()=>t.href&&Ee.test(t.href)),o=k(()=>t.tag?t.tag:t.href?"a":"button");return(l,d)=>(a(),y(U(c(o)),{class:M(["VPButton",c(n)]),href:e.href?c(Z)(e.href):void 0,target:c(s)?"_blank":void 0,rel:c(s)?"noreferrer":void 0},{default:v(()=>[I(P(e.text),1)]),_:1},8,["class","href","target","rel"]))}});const Ta=p(Na,[["__scopeId","data-v-567ba664"]]),Aa=e=>(H("data-v-fd2650d5"),e=e(),E(),e),Ha={class:"container"},Ea={class:"main"},za={key:0,class:"name"},Da={class:"clip"},Fa={key:1,class:"text"},Oa={key:2,class:"tagline"},Ga={key:0,class:"actions"},Ra={key:0,class:"image"},Ua={class:"image-container"},ja=Aa(()=>u("div",{class:"image-bg"},null,-1)),qa=g({__name:"VPHero",props:{name:null,text:null,tagline:null,image:null,actions:null},setup(e){const t=me("hero-image-slot-exists");return(n,s)=>(a(),i("div",{class:M(["VPHero",{"has-image":e.image||c(t)}])},[u("div",Ha,[u("div",Ea,[r(n.$slots,"home-hero-info",{},()=>[e.name?(a(),i("h1",za,[u("span",Da,P(e.name),1)])):m("",!0),e.text?(a(),i("p",Fa,P(e.text),1)):m("",!0),e.tagline?(a(),i("p",Oa,P(e.tagline),1)):m("",!0)],!0),e.actions?(a(),i("div",Ga,[(a(!0),i(S,null,N(e.actions,o=>(a(),i("div",{key:o.link,class:"action"},[h(Ta,{tag:"a",size:"medium",theme:o.theme,text:o.text,href:o.link},null,8,["theme","text","href"])]))),128))])):m("",!0)]),e.image||c(t)?(a(),i("div",Ra,[u("div",Ua,[ja,r(n.$slots,"home-hero-image",{},()=>[e.image?(a(),y(be,{key:0,class:"image-src",image:e.image},null,8,["image"])):m("",!0)],!0)])])):m("",!0)])],2))}});const Ka=p(qa,[["__scopeId","data-v-fd2650d5"]]),Wa=g({__name:"VPHomeHero",setup(e){const{frontmatter:t}=V();return(n,s)=>c(t).hero?(a(),y(Ka,{key:0,class:"VPHomeHero",name:c(t).hero.name,text:c(t).hero.text,tagline:c(t).hero.tagline,image:c(t).hero.image,actions:c(t).hero.actions},{"home-hero-info":v(()=>[r(n.$slots,"home-hero-info")]),"home-hero-image":v(()=>[r(n.$slots,"home-hero-image")]),_:3},8,["name","text","tagline","image","actions"])):m("",!0)}}),Ya={},Xa={xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 24 24"},Qa=u("path",{d:"M19.9,12.4c0.1-0.2,0.1-0.5,0-0.8c-0.1-0.1-0.1-0.2-0.2-0.3l-7-7c-0.4-0.4-1-0.4-1.4,0s-0.4,1,0,1.4l5.3,5.3H5c-0.6,0-1,0.4-1,1s0.4,1,1,1h11.6l-5.3,5.3c-0.4,0.4-0.4,1,0,1.4c0.2,0.2,0.5,0.3,0.7,0.3s0.5-0.1,0.7-0.3l7-7C19.8,12.6,19.9,12.5,19.9,12.4z"},null,-1),Ja=[Qa];function Za(e,t){return a(),i("svg",Xa,Ja)}const ec=p(Ya,[["render",Za]]),tc={class:"box"},nc=["innerHTML"],sc=["innerHTML"],oc=["innerHTML"],ac={key:3,class:"link-text"},cc={class:"link-text-value"},lc=g({__name:"VPFeature",props:{icon:null,title:null,details:null,link:null,linkText:null},setup(e){return(t,n)=>(a(),y(O,{class:"VPFeature",href:e.link,"no-icon":!0},{default:v(()=>[u("article",tc,[typeof e.icon=="object"?(a(),y(be,{key:0,image:e.icon,alt:e.icon.alt,height:e.icon.height,width:e.icon.width},null,8,["image","alt","height","width"])):e.icon?(a(),i("div",{key:1,class:"icon",innerHTML:e.icon},null,8,nc)):m("",!0),u("h2",{class:"title",innerHTML:e.title},null,8,sc),e.details?(a(),i("p",{key:2,class:"details",innerHTML:e.details},null,8,oc)):m("",!0),e.linkText?(a(),i("div",ac,[u("p",cc,[I(P(e.linkText)+" ",1),h(ec,{class:"link-text-icon"})])])):m("",!0)])]),_:1},8,["href"]))}});const ic=p(lc,[["__scopeId","data-v-837f6cca"]]),rc={key:0,class:"VPFeatures"},uc={class:"container"},dc={class:"items"},_c=g({__name:"VPFeatures",props:{features:null},setup(e){const t=e,n=k(()=>{const s=t.features.length;if(s){if(s===2)return"grid-2";if(s===3)return"grid-3";if(s%3===0)return"grid-6";if(s%2===0)return"grid-4"}else return});return(s,o)=>e.features?(a(),i("div",rc,[u("div",uc,[u("div",dc,[(a(!0),i(S,null,N(e.features,l=>(a(),i("div",{key:l.title,class:M(["item",[c(n)]])},[h(ic,{icon:l.icon,title:l.title,details:l.details,link:l.link,"link-text":l.linkText},null,8,["icon","title","details","link","link-text"])],2))),128))])])])):m("",!0)}});const vc=p(_c,[["__scopeId","data-v-6816157f"]]),hc=g({__name:"VPHomeFeatures",setup(e){const{frontmatter:t}=V();return(n,s)=>c(t).features?(a(),y(vc,{key:0,class:"VPHomeFeatures",features:c(t).features},null,8,["features"])):m("",!0)}}),fc={class:"VPHome"},pc=g({__name:"VPHome",setup(e){return(t,n)=>{const s=R("Content");return a(),i("div",fc,[r(t.$slots,"home-hero-before",{},void 0,!0),h(Wa,null,{"home-hero-info":v(()=>[r(t.$slots,"home-hero-info",{},void 0,!0)]),"home-hero-image":v(()=>[r(t.$slots,"home-hero-image",{},void 0,!0)]),_:3}),r(t.$slots,"home-hero-after",{},void 0,!0),r(t.$slots,"home-features-before",{},void 0,!0),h(hc),r(t.$slots,"home-features-after",{},void 0,!0),h(s)])}}});const mc=p(pc,[["__scopeId","data-v-d82743a8"]]),gc=e=>(H("data-v-ff0f39c8"),e=e(),E(),e),yc={class:"content"},bc={class:"outline-title"},kc={"aria-labelledby":"doc-outline-aria-label"},$c=gc(()=>u("span",{class:"visually-hidden",id:"doc-outline-aria-label"}," Table of Contents for current page ",-1)),Pc=g({__name:"VPDocAsideOutline",setup(e){const{frontmatter:t,theme:n}=V(),s=ge([]);J(()=>{s.value=Le(t.value.outline??n.value.outline)});const o=x(),l=x();return ta(o,l),(d,f)=>(a(),i("div",{class:M(["VPDocAsideOutline",{"has-outline":c(s).length>0}]),ref_key:"container",ref:o},[u("div",yc,[u("div",{class:"outline-marker",ref_key:"marker",ref:l},null,512),u("div",bc,P(c(Se)(c(n))),1),u("nav",kc,[$c,h(Me,{headers:c(s),root:!0},null,8,["headers"])])])],2))}});const Vc=p(Pc,[["__scopeId","data-v-ff0f39c8"]]),wc={class:"VPDocAsideCarbonAds"},xc=g({__name:"VPDocAsideCarbonAds",props:{carbonAds:null},setup(e){const t=()=>null;return(n,s)=>(a(),i("div",wc,[h(c(t),{"carbon-ads":e.carbonAds},null,8,["carbon-ads"])]))}}),Sc=e=>(H("data-v-3f215769"),e=e(),E(),e),Lc={class:"VPDocAside"},Mc=Sc(()=>u("div",{class:"spacer"},null,-1)),Cc=g({__name:"VPDocAside",setup(e){const{theme:t}=V();return(n,s)=>(a(),i("div",Lc,[r(n.$slots,"aside-top",{},void 0,!0),r(n.$slots,"aside-outline-before",{},void 0,!0),h(Vc),r(n.$slots,"aside-outline-after",{},void 0,!0),Mc,r(n.$slots,"aside-ads-before",{},void 0,!0),c(t).carbonAds?(a(),y(xc,{key:0,"carbon-ads":c(t).carbonAds},null,8,["carbon-ads"])):m("",!0),r(n.$slots,"aside-ads-after",{},void 0,!0),r(n.$slots,"aside-bottom",{},void 0,!0)]))}});const Bc=p(Cc,[["__scopeId","data-v-3f215769"]]);function Ic(){const{theme:e,page:t}=V();return k(()=>{const{text:n="Edit this page",pattern:s=""}=e.value.editLink||{};let o;return typeof s=="function"?o=s(t.value):o=s.replace(/:path/g,t.value.filePath),{url:o,text:n}})}function Nc(){const{page:e,theme:t,frontmatter:n}=V();return k(()=>{var d,f,_,b;const s=Oe(t.value.sidebar,e.value.relativePath),o=wt(s),l=o.findIndex(L=>K(e.value.relativePath,L.link));return{prev:n.value.prev===!1?void 0:{text:(typeof n.value.prev=="string"?n.value.prev:typeof n.value.prev=="object"?n.value.prev.text:void 0)??((d=o[l-1])==null?void 0:d.text),link:(typeof n.value.prev=="object"?n.value.prev.link:void 0)??((f=o[l-1])==null?void 0:f.link)},next:n.value.next===!1?void 0:{text:(typeof n.value.next=="string"?n.value.next:typeof n.value.next=="object"?n.value.next.text:void 0)??((_=o[l+1])==null?void 0:_.text),link:(typeof n.value.next=="object"?n.value.next.link:void 0)??((b=o[l+1])==null?void 0:b.link)}}})}const Tc={},Ac={xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 24 24"},Hc=u("path",{d:"M18,23H4c-1.7,0-3-1.3-3-3V6c0-1.7,1.3-3,3-3h7c0.6,0,1,0.4,1,1s-0.4,1-1,1H4C3.4,5,3,5.4,3,6v14c0,0.6,0.4,1,1,1h14c0.6,0,1-0.4,1-1v-7c0-0.6,0.4-1,1-1s1,0.4,1,1v7C21,21.7,19.7,23,18,23z"},null,-1),Ec=u("path",{d:"M8,17c-0.3,0-0.5-0.1-0.7-0.3C7,16.5,6.9,16.1,7,15.8l1-4c0-0.2,0.1-0.3,0.3-0.5l9.5-9.5c1.2-1.2,3.2-1.2,4.4,0c1.2,1.2,1.2,3.2,0,4.4l-9.5,9.5c-0.1,0.1-0.3,0.2-0.5,0.3l-4,1C8.2,17,8.1,17,8,17zM9.9,12.5l-0.5,2.1l2.1-0.5l9.3-9.3c0.4-0.4,0.4-1.1,0-1.6c-0.4-0.4-1.2-0.4-1.6,0l0,0L9.9,12.5z M18.5,2.5L18.5,2.5L18.5,2.5z"},null,-1),zc=[Hc,Ec];function Dc(e,t){return a(),i("svg",Ac,zc)}const Fc=p(Tc,[["render",Dc]]),Oc={class:"VPLastUpdated"},Gc=["datetime"],Rc=g({__name:"VPDocFooterLastUpdated",setup(e){const{theme:t,page:n,lang:s}=V(),o=k(()=>new Date(n.value.lastUpdated)),l=k(()=>o.value.toISOString()),d=x("");return F(()=>{Q(()=>{d.value=o.value.toLocaleString(s.value)})}),(f,_)=>(a(),i("p",Oc,[I(P(c(t).lastUpdatedText||"Last updated")+": ",1),u("time",{datetime:c(l)},P(d.value),9,Gc)]))}});const Uc=p(Rc,[["__scopeId","data-v-7b3ebfe1"]]),jc={key:0,class:"VPDocFooter"},qc={key:0,class:"edit-info"},Kc={key:0,class:"edit-link"},Wc={key:1,class:"last-updated"},Yc={key:1,class:"prev-next"},Xc={class:"pager"},Qc=["href"],Jc=["innerHTML"],Zc=["innerHTML"],el=["href"],tl=["innerHTML"],nl=["innerHTML"],sl=g({__name:"VPDocFooter",setup(e){const{theme:t,page:n,frontmatter:s}=V(),o=Ic(),l=Nc(),d=k(()=>t.value.editLink&&s.value.editLink!==!1),f=k(()=>n.value.lastUpdated&&s.value.lastUpdated!==!1),_=k(()=>d.value||f.value||l.value.prev||l.value.next);return(b,L)=>{var C,w,$,B,T,A,D;return c(_)?(a(),i("footer",jc,[r(b.$slots,"doc-footer-before",{},void 0,!0),c(d)||c(f)?(a(),i("div",qc,[c(d)?(a(),i("div",Kc,[h(O,{class:"edit-link-button",href:c(o).url,"no-icon":!0},{default:v(()=>[h(Fc,{class:"edit-link-icon","aria-label":"edit icon"}),I(" "+P(c(o).text),1)]),_:1},8,["href"])])):m("",!0),c(f)?(a(),i("div",Wc,[h(Uc)])):m("",!0)])):m("",!0),(C=c(l).prev)!=null&&C.link||(w=c(l).next)!=null&&w.link?(a(),i("div",Yc,[u("div",Xc,[($=c(l).prev)!=null&&$.link?(a(),i("a",{key:0,class:"pager-link prev",href:c(Z)(c(l).prev.link)},[u("span",{class:"desc",innerHTML:((B=c(t).docFooter)==null?void 0:B.prev)||"Previous page"},null,8,Jc),u("span",{class:"title",innerHTML:c(l).prev.text},null,8,Zc)],8,Qc)):m("",!0)]),u("div",{class:M(["pager",{"has-prev":(T=c(l).prev)==null?void 0:T.link}])},[(A=c(l).next)!=null&&A.link?(a(),i("a",{key:0,class:"pager-link next",href:c(Z)(c(l).next.link)},[u("span",{class:"desc",innerHTML:((D=c(t).docFooter)==null?void 0:D.next)||"Next page"},null,8,tl),u("span",{class:"title",innerHTML:c(l).next.text},null,8,nl)],8,el)):m("",!0)],2)])):m("",!0)])):m("",!0)}}});const ol=p(sl,[["__scopeId","data-v-face870a"]]),al={key:0,class:"VPDocOutlineDropdown"},cl={key:0,class:"items"},ll=g({__name:"VPDocOutlineDropdown",setup(e){const{frontmatter:t,theme:n}=V(),s=x(!1);J(()=>{s.value=!1});const o=ge([]);return J(()=>{o.value=Le(t.value.outline??n.value.outline)}),(l,d)=>c(o).length>0?(a(),i("div",al,[u("button",{onClick:d[0]||(d[0]=f=>s.value=!s.value),class:M({open:s.value})},[I(P(c(Se)(c(n)))+" ",1),h(Ce,{class:"icon"})],2),s.value?(a(),i("div",cl,[h(Me,{headers:c(o)},null,8,["headers"])])):m("",!0)])):m("",!0)}});const il=p(ll,[["__scopeId","data-v-2edece88"]]),rl=e=>(H("data-v-c4b0d3cf"),e=e(),E(),e),ul={class:"container"},dl=rl(()=>u("div",{class:"aside-curtain"},null,-1)),_l={class:"aside-container"},vl={class:"aside-content"},hl={class:"content"},fl={class:"content-container"},pl={class:"main"},ml=g({__name:"VPDoc",setup(e){const t=ee(),{hasSidebar:n,hasAside:s,leftAside:o}=z(),l=k(()=>t.path.replace(/[./]+/g,"_").replace(/_html$/,""));return(d,f)=>{const _=R("Content");return a(),i("div",{class:M(["VPDoc",{"has-sidebar":c(n),"has-aside":c(s)}])},[r(d.$slots,"doc-top",{},void 0,!0),u("div",ul,[c(s)?(a(),i("div",{key:0,class:M(["aside",{"left-aside":c(o)}])},[dl,u("div",_l,[u("div",vl,[h(Bc,null,{"aside-top":v(()=>[r(d.$slots,"aside-top",{},void 0,!0)]),"aside-bottom":v(()=>[r(d.$slots,"aside-bottom",{},void 0,!0)]),"aside-outline-before":v(()=>[r(d.$slots,"aside-outline-before",{},void 0,!0)]),"aside-outline-after":v(()=>[r(d.$slots,"aside-outline-after",{},void 0,!0)]),"aside-ads-before":v(()=>[r(d.$slots,"aside-ads-before",{},void 0,!0)]),"aside-ads-after":v(()=>[r(d.$slots,"aside-ads-after",{},void 0,!0)]),_:3})])])],2)):m("",!0),u("div",hl,[u("div",fl,[r(d.$slots,"doc-before",{},void 0,!0),h(il),u("main",pl,[h(_,{class:M(["vp-doc",c(l)])},null,8,["class"])]),h(ol,null,{"doc-footer-before":v(()=>[r(d.$slots,"doc-footer-before",{},void 0,!0)]),_:3}),r(d.$slots,"doc-after",{},void 0,!0)])])]),r(d.$slots,"doc-bottom",{},void 0,!0)],2)}}});const gl=p(ml,[["__scopeId","data-v-c4b0d3cf"]]),re=e=>(H("data-v-c70503b8"),e=e(),E(),e),yl={class:"NotFound"},bl=re(()=>u("p",{class:"code"},"404",-1)),kl=re(()=>u("h1",{class:"title"},"PAGE NOT FOUND",-1)),$l=re(()=>u("div",{class:"divider"},null,-1)),Pl=re(()=>u("blockquote",{class:"quote"}," But if you don't change your direction, and if you keep looking, you may end up where you are heading. ",-1)),Vl={class:"action"},wl=["href"],xl=g({__name:"NotFound",setup(e){const{site:t}=V(),{localeLinks:n}=te({removeCurrent:!1}),s=x("/");return F(()=>{var l;const o=window.location.pathname.replace(t.value.base,"").replace(/(^.*?\/).*$/,"/$1");n.value.length&&(s.value=((l=n.value.find(({link:d})=>d.startsWith(o)))==null?void 0:l.link)||n.value[0].link)}),(o,l)=>(a(),i("div",yl,[bl,kl,$l,Pl,u("div",Vl,[u("a",{class:"link",href:c(he)(s.value),"aria-label":"go to home"}," Take me home ",8,wl)])]))}});const Sl=p(xl,[["__scopeId","data-v-c70503b8"]]),Ll=g({__name:"VPContent",setup(e){const{page:t,frontmatter:n}=V(),{hasSidebar:s}=z();return(o,l)=>(a(),i("div",{class:M(["VPContent",{"has-sidebar":c(s),"is-home":c(n).layout==="home"}]),id:"VPContent"},[c(t).isNotFound?r(o.$slots,"not-found",{key:0},()=>[h(Sl)],!0):c(n).layout==="page"?(a(),y(Ia,{key:1},{"page-top":v(()=>[r(o.$slots,"page-top",{},void 0,!0)]),"page-bottom":v(()=>[r(o.$slots,"page-bottom",{},void 0,!0)]),_:3})):c(n).layout==="home"?(a(),y(mc,{key:2},{"home-hero-before":v(()=>[r(o.$slots,"home-hero-before",{},void 0,!0)]),"home-hero-info":v(()=>[r(o.$slots,"home-hero-info",{},void 0,!0)]),"home-hero-image":v(()=>[r(o.$slots,"home-hero-image",{},void 0,!0)]),"home-hero-after":v(()=>[r(o.$slots,"home-hero-after",{},void 0,!0)]),"home-features-before":v(()=>[r(o.$slots,"home-features-before",{},void 0,!0)]),"home-features-after":v(()=>[r(o.$slots,"home-features-after",{},void 0,!0)]),_:3})):(a(),y(gl,{key:3},{"doc-top":v(()=>[r(o.$slots,"doc-top",{},void 0,!0)]),"doc-bottom":v(()=>[r(o.$slots,"doc-bottom",{},void 0,!0)]),"doc-footer-before":v(()=>[r(o.$slots,"doc-footer-before",{},void 0,!0)]),"doc-before":v(()=>[r(o.$slots,"doc-before",{},void 0,!0)]),"doc-after":v(()=>[r(o.$slots,"doc-after",{},void 0,!0)]),"aside-top":v(()=>[r(o.$slots,"aside-top",{},void 0,!0)]),"aside-outline-before":v(()=>[r(o.$slots,"aside-outline-before",{},void 0,!0)]),"aside-outline-after":v(()=>[r(o.$slots,"aside-outline-after",{},void 0,!0)]),"aside-ads-before":v(()=>[r(o.$slots,"aside-ads-before",{},void 0,!0)]),"aside-ads-after":v(()=>[r(o.$slots,"aside-ads-after",{},void 0,!0)]),"aside-bottom":v(()=>[r(o.$slots,"aside-bottom",{},void 0,!0)]),_:3}))],2))}});const Ml=p(Ll,[["__scopeId","data-v-a494bd1d"]]),Cl={class:"container"},Bl=["innerHTML"],Il=["innerHTML"],Nl=g({__name:"VPFooter",setup(e){const{theme:t}=V(),{hasSidebar:n}=z();return(s,o)=>c(t).footer?(a(),i("footer",{key:0,class:M(["VPFooter",{"has-sidebar":c(n)}])},[u("div",Cl,[c(t).footer.message?(a(),i("p",{key:0,class:"message",innerHTML:c(t).footer.message},null,8,Bl)):m("",!0),c(t).footer.copyright?(a(),i("p",{key:1,class:"copyright",innerHTML:c(t).footer.copyright},null,8,Il)):m("",!0)])],2)):m("",!0)}});const Tl=p(Nl,[["__scopeId","data-v-2f86ebd2"]]),Al={key:0,class:"Layout"},Hl=g({__name:"Layout",setup(e){const{isOpen:t,open:n,close:s}=z(),o=ee();q(()=>o.path,s),xt(t,s),oe("close-sidebar",s),oe("is-sidebar-open",t);const{frontmatter:l}=V(),d=vt(),f=k(()=>!!d["home-hero-image"]);return oe("hero-image-slot-exists",f),(_,b)=>{const L=R("Content");return c(l).layout!==!1?(a(),i("div",Al,[r(_.$slots,"layout-top",{},void 0,!0),h(Mt),h(It,{class:"backdrop",show:c(t),onClick:c(s)},null,8,["show","onClick"]),h(Oo,null,{"nav-bar-title-before":v(()=>[r(_.$slots,"nav-bar-title-before",{},void 0,!0)]),"nav-bar-title-after":v(()=>[r(_.$slots,"nav-bar-title-after",{},void 0,!0)]),"nav-bar-content-before":v(()=>[r(_.$slots,"nav-bar-content-before",{},void 0,!0)]),"nav-bar-content-after":v(()=>[r(_.$slots,"nav-bar-content-after",{},void 0,!0)]),"nav-screen-content-before":v(()=>[r(_.$slots,"nav-screen-content-before",{},void 0,!0)]),"nav-screen-content-after":v(()=>[r(_.$slots,"nav-screen-content-after",{},void 0,!0)]),_:3}),h(pa,{open:c(t),onOpenMenu:c(n)},null,8,["open","onOpenMenu"]),h(La,{open:c(t)},{"sidebar-nav-before":v(()=>[r(_.$slots,"sidebar-nav-before",{},void 0,!0)]),"sidebar-nav-after":v(()=>[r(_.$slots,"sidebar-nav-after",{},void 0,!0)]),_:3},8,["open"]),h(Ml,null,{"page-top":v(()=>[r(_.$slots,"page-top",{},void 0,!0)]),"page-bottom":v(()=>[r(_.$slots,"page-bottom",{},void 0,!0)]),"not-found":v(()=>[r(_.$slots,"not-found",{},void 0,!0)]),"home-hero-before":v(()=>[r(_.$slots,"home-hero-before",{},void 0,!0)]),"home-hero-info":v(()=>[r(_.$slots,"home-hero-info",{},void 0,!0)]),"home-hero-image":v(()=>[r(_.$slots,"home-hero-image",{},void 0,!0)]),"home-hero-after":v(()=>[r(_.$slots,"home-hero-after",{},void 0,!0)]),"home-features-before":v(()=>[r(_.$slots,"home-features-before",{},void 0,!0)]),"home-features-after":v(()=>[r(_.$slots,"home-features-after",{},void 0,!0)]),"doc-footer-before":v(()=>[r(_.$slots,"doc-footer-before",{},void 0,!0)]),"doc-before":v(()=>[r(_.$slots,"doc-before",{},void 0,!0)]),"doc-after":v(()=>[r(_.$slots,"doc-after",{},void 0,!0)]),"doc-top":v(()=>[r(_.$slots,"doc-top",{},void 0,!0)]),"doc-bottom":v(()=>[r(_.$slots,"doc-bottom",{},void 0,!0)]),"aside-top":v(()=>[r(_.$slots,"aside-top",{},void 0,!0)]),"aside-bottom":v(()=>[r(_.$slots,"aside-bottom",{},void 0,!0)]),"aside-outline-before":v(()=>[r(_.$slots,"aside-outline-before",{},void 0,!0)]),"aside-outline-after":v(()=>[r(_.$slots,"aside-outline-after",{},void 0,!0)]),"aside-ads-before":v(()=>[r(_.$slots,"aside-ads-before",{},void 0,!0)]),"aside-ads-after":v(()=>[r(_.$slots,"aside-ads-after",{},void 0,!0)]),_:3}),h(Tl),r(_.$slots,"layout-bottom",{},void 0,!0)])):(a(),y(L,{key:1}))}}});const El=p(Hl,[["__scopeId","data-v-b2cf3e0b"]]);const Dl={Layout:El,enhanceApp:({app:e})=>{e.component("Badge",ft)}};export{Dl as t}; diff --git a/assets/guide_app-profile.md.27f6816c.js b/assets/guide_app-profile.md.27f6816c.js new file mode 100644 index 000000000000..23a6492d8ecc --- /dev/null +++ b/assets/guide_app-profile.md.27f6816c.js @@ -0,0 +1,9 @@ +import{_ as e,o as s,c as o,Q as a}from"./chunks/framework.ec8f7e8e.js";const E=JSON.parse('{"title":"App Profile","description":"","frontmatter":{},"headers":[],"relativePath":"guide/app-profile.md","filePath":"guide/app-profile.md"}'),n={name:"guide/app-profile.md"},t=a(`

App Profile

The App Profile is a mechanism provided by KernelSU for customizing the configuration of various applications.

For applications granted root permissions (i.e., able to use su), the App Profile can also be referred to as the Root Profile. It allows customization of the uid, gid, groups, capabilities, and SELinux rules of the su command, thereby restricting the privileges of the root user. For example, it can grant network permissions only to firewall applications while denying file access permissions, or it can grant shell permissions instead of full root access for freeze applications: keeping the power confined with the principle of least privilege.

For ordinary applications without root permissions, the App Profile can control the behavior of the kernel and module system towards these applications. For instance, it can determine whether modifications resulting from modules should be addressed. The kernel and module system can make decisions based on this configuration, such as performing operations akin to "hiding"

Root Profile

UID, GID, and Groups

Linux systems have two concepts: users and groups. Each user has a user ID (UID), and a user can belong to multiple groups, each with its own group ID (GID). These IDs are used to identify users in the system and determine which system resources they can access.

Users with a UID of 0 are known as root users, and groups with a GID of 0 are known as root groups. The root user group typically holds the highest system privileges.

In the case of the Android system, each app is a separate user (excluding shared UID scenarios) with a unique UID. For example, 0 represents the root user, 1000 represents system, 2000 represents the ADB shell, and UIDs ranging from 10000 to 19999 represent ordinary apps.

INFO

Here, the UID mentioned is not the same as the concept of multiple users or work profiles in the Android system. Work profiles are actually implemented by partitioning the UID range. For example, 10000-19999 represents the main user, while 110000-119999 represents a work profile. Each ordinary app among them has its own unique UID.

Each app can have several groups, with the GID representing the primary group, which usually matches the UID. Other groups are known as supplementary groups. Certain permissions are controlled through groups, such as network access permissions or Bluetooth access.

For example, if we execute the id command in ADB shell, the output might look like this:

sh
oriole:/ $ id
+uid=2000(shell) gid=2000(shell) groups=2000(shell),1004(input),1007(log),1011(adb),1015(sdcard_rw),1028(sdcard_r),1078(ext_data_rw),1079(ext_obb_rw),3001(net_bt_admin),3002(net_bt),3003(inet),3006(net_bw_stats),3009(readproc),3011(uhid),3012(readtracefs) context=u:r:shell:s0
oriole:/ $ id
+uid=2000(shell) gid=2000(shell) groups=2000(shell),1004(input),1007(log),1011(adb),1015(sdcard_rw),1028(sdcard_r),1078(ext_data_rw),1079(ext_obb_rw),3001(net_bt_admin),3002(net_bt),3003(inet),3006(net_bw_stats),3009(readproc),3011(uhid),3012(readtracefs) context=u:r:shell:s0

Here, the UID is 2000, and the GID (primary group ID) is also 2000. Additionally, it belongs to several supplementary groups, such as inet (indicating the ability to create AF_INET and AF_INET6 sockets) and sdcard_rw (indicating read/write permissions for the SD card).

KernelSU's Root Profile allows customization of the UID, GID, and groups for the root process after executing su. For example, the Root Profile of a root app can set its UID to 2000, which means that when using su, the app's actual permissions are at the ADB shell level. The inet group can be removed, preventing the su command from accessing the network.

Note

The App Profile only controls the permissions of the root process after using su; it does not control the permissions of the app itself. If an app has requested network access permission, it can still access the network even without using su. Removing the inet group from su only prevents su from accessing the network.

Root Profile is enforced in the kernel and does not rely on the voluntary behavior of root applications, unlike switching users or groups through su, the granting of su permission is entirely up to the user rather than the developer.

Capabilities

Capabilities are a mechanism for privilege separation in Linux.

For the purpose of performing permission checks, traditional UNIX implementations distinguish two categories of processes: privileged processes (whose effective user ID is 0, referred to as superuser or root), and unprivileged processes (whose effective UID is nonzero). Privileged processes bypass all kernel permission checks, while unprivileged processes are subject to full permission checking based on the process's credentials (usually: effective UID, effective GID, and supplementary group list).

Starting with Linux 2.2, Linux divides the privileges traditionally associated with superuser into distinct units, known as capabilities, which can be independently enabled and disabled.

Each Capability represents one or more privileges. For example, CAP_DAC_READ_SEARCH represents the ability to bypass permission checks for file reading, as well as directory reading and execution permissions. If a user with an effective UID of 0 (root user) lacks CAP_DAC_READ_SEARCH or higher capabilities, this means that even though they are root, they cannot read files at will.

KernelSU's Root Profile allows customization of the Capabilities of the root process after executing su, thereby achieving partially granting "root permissions." Unlike the aforementioned UID and GID, certain root apps require a UID of 0 after using su. In such cases, limiting the Capabilities of this root user with UID 0 can restrict their allowed operations.

Strong Recommendation

Linux's Capability official documentation provides detailed explanations of the abilities represented by each Capability. If you intend to customize Capabilities, it is strongly recommended that you read this document first.

SELinux

SELinux is a powerful Mandatory Access Control (MAC) mechanism. It operates on the principle of default deny: any action not explicitly allowed is denied.

SELinux can run in two global modes:

  1. Permissive mode: Denial events are logged but not enforced.
  2. Enforcing mode: Denial events are logged and enforced.

Warning

Modern Android systems heavily rely on SELinux to ensure overall system security. It is highly recommended not to use any custom systems running in "permissive mode" since it provides no significant advantages over a completely open system.

Explaining the full concept of SELinux is complex and beyond the scope of this document. It is recommended to first understand its workings through the following resources:

  1. Wikipedia
  2. Red Hat: What Is SELinux?
  3. ArchLinux: SELinux

KernelSU's Root Profile allows customization of the SELinux context of the root process after executing su. Specific access control rules can be set for this context to enable fine-grained control over root permissions.

In typical scenarios, when an app executes su, it switches the process to a SELinux domain with unrestricted access, such as u:r:su:s0. Through the Root Profile, this domain can be switched to a custom domain, such as u:r:app1:s0, and a series of rules can be defined for this domain:

sh
type app1
+enforce app1
+typeattribute app1 mlstrustedsubject
+allow app1 * * *
type app1
+enforce app1
+typeattribute app1 mlstrustedsubject
+allow app1 * * *

Note that the allow app1 * * * rule is used for demonstration purposes only. In practice, this rule should not be used extensively since it doesn't differ much from permissive mode.

Escalation

If the configuration of the Root Profile is not set properly, an escalation scenario may occur: the restrictions imposed by the Root Profile can unintentionally fail.

For example, if you grant root permission to an ADB shell user (which is a common case), and then you grant root permission to a regular application but configure its root profile with UID 2000 (which is the UID of the ADB shell user), the application can obtain full root access by executing the su command twice:

  1. The first su execution is subject to the enforcement of the App Profile and will switch to UID 2000 (adb shell) instead of 0 (root).
  2. The second su execution, since the UID is 2000, and you have granted root access to the UID 2000 (adb shell) in the configuration, the application will gain full root privileges.

Note

This behavior is entirely expected and not a bug. Therefore, we recommend the following:

If you genuinely need to grant root permissions to ADB (e.g., as a developer), it is not advisable to change the UID to 2000 when configuring the Root Profile. Using 1000 (system) would be a better choice.

Non-Root Profile

Umount Modules

KernelSU provides a systemless mechanism for modifying system partitions, achieved through overlayfs mounting. However, some apps may be sensitive to such behavior. Thus, we can unload modules mounted on these apps by setting the "umount modules" option.

Additionally, the settings interface of the KernelSU manager provides a switch for "umount modules by default". By default, this switch is enabled, which means that KernelSU or some modules will unload modules for this app unless additional settings are applied. If you do not prefer this setting or if it affects certain apps, you have the following options:

  1. Keep the switch for "umount modules by default" and individually disable the "umount modules" option in the App Profile for apps requiring module loading (acting as a "whitelist").
  2. Disable the switch for "umount modules by default" and individually enable the "umount modules" option in the App Profile for apps requiring module unloading (acting as a "blacklist").

INFO

In devices using kernel version 5.10 and above, the kernel performs the unloading of modules. However, for devices running kernel versions below 5.10, this switch is merely a configuration option, and KernelSU itself does not take any action. Some modules, such as Zygisksu, may use this switch to determine whether module unloading is necessary.

`,46),l=[t];function p(r,i,c,d,u,h){return s(),o("div",null,l)}const m=e(n,[["render",p]]);export{E as __pageData,m as default}; diff --git a/assets/guide_app-profile.md.27f6816c.lean.js b/assets/guide_app-profile.md.27f6816c.lean.js new file mode 100644 index 000000000000..cb3e0aa76d23 --- /dev/null +++ b/assets/guide_app-profile.md.27f6816c.lean.js @@ -0,0 +1 @@ +import{_ as e,o as s,c as o,Q as a}from"./chunks/framework.ec8f7e8e.js";const E=JSON.parse('{"title":"App Profile","description":"","frontmatter":{},"headers":[],"relativePath":"guide/app-profile.md","filePath":"guide/app-profile.md"}'),n={name:"guide/app-profile.md"},t=a("",46),l=[t];function p(r,i,c,d,u,h){return s(),o("div",null,l)}const m=e(n,[["render",p]]);export{E as __pageData,m as default}; diff --git a/assets/guide_app-profile.md.9faae288.js b/assets/guide_app-profile.md.9faae288.js deleted file mode 100644 index 31f1b8ebcde4..000000000000 --- a/assets/guide_app-profile.md.9faae288.js +++ /dev/null @@ -1,5 +0,0 @@ -import{_ as e,o,c as s,O as t}from"./chunks/framework.43781440.js";const f=JSON.parse('{"title":"App Profile","description":"","frontmatter":{},"headers":[],"relativePath":"guide/app-profile.md","filePath":"guide/app-profile.md"}'),a={name:"guide/app-profile.md"},n=t(`

App Profile

The App Profile is a mechanism provided by KernelSU for customizing the configuration of various applications.

For applications granted root permissions (i.e., able to use su), the App Profile can also be referred to as the Root Profile. It allows customization of the uid, gid, groups, capabilities, and SELinux rules of the su command, thereby restricting the privileges of the root user. For example, it can grant network permissions only to firewall applications while denying file access permissions, or it can grant shell permissions instead of full root access for freeze applications: keeping the power confined with the principle of least privilege.

For ordinary applications without root permissions, the App Profile can control the behavior of the kernel and module system towards these applications. For instance, it can determine whether modifications resulting from modules should be addressed. The kernel and module system can make decisions based on this configuration, such as performing operations akin to "hiding"

Root Profile

UID, GID, and Groups

Linux systems have two concepts: users and groups. Each user has a user ID (UID), and a user can belong to multiple groups, each with its own group ID (GID). These IDs are used to identify users in the system and determine which system resources they can access.

Users with a UID of 0 are known as root users, and groups with a GID of 0 are known as root groups. The root user group typically holds the highest system privileges.

In the case of the Android system, each app is a separate user (excluding shared UID scenarios) with a unique UID. For example, 0 represents the root user, 1000 represents system, 2000 represents the ADB shell, and UIDs ranging from 10000 to 19999 represent ordinary apps.

INFO

Here, the UID mentioned is not the same as the concept of multiple users or work profiles in the Android system. Work profiles are actually implemented by partitioning the UID range. For example, 10000-19999 represents the main user, while 110000-119999 represents a work profile. Each ordinary app among them has its own unique UID.

Each app can have several groups, with the GID representing the primary group, which usually matches the UID. Other groups are known as supplementary groups. Certain permissions are controlled through groups, such as network access permissions or Bluetooth access.

For example, if we execute the id command in ADB shell, the output might look like this:

sh
oriole:/ $ id
-uid=2000(shell) gid=2000(shell) groups=2000(shell),1004(input),1007(log),1011(adb),1015(sdcard_rw),1028(sdcard_r),1078(ext_data_rw),1079(ext_obb_rw),3001(net_bt_admin),3002(net_bt),3003(inet),3006(net_bw_stats),3009(readproc),3011(uhid),3012(readtracefs) context=u:r:shell:s0

Here, the UID is 2000, and the GID (primary group ID) is also 2000. Additionally, it belongs to several supplementary groups, such as inet (indicating the ability to create AF_INET and AF_INET6 sockets) and sdcard_rw (indicating read/write permissions for the SD card).

KernelSU's Root Profile allows customization of the UID, GID, and groups for the root process after executing su. For example, the Root Profile of a root app can set its UID to 2000, which means that when using su, the app's actual permissions are at the ADB shell level. The inet group can be removed, preventing the su command from accessing the network.

Note

The App Profile only controls the permissions of the root process after using su; it does not control the permissions of the app itself. If an app has requested network access permission, it can still access the network even without using su. Removing the inet group from su only prevents su from accessing the network.

Root Profile is enforced in the kernel and does not rely on the voluntary behavior of root applications, unlike switching users or groups through su, the granting of su permission is entirely up to the user rather than the developer.

Capabilities

Capabilities are a mechanism for privilege separation in Linux.

For the purpose of performing permission checks, traditional UNIX implementations distinguish two categories of processes: privileged processes (whose effective user ID is 0, referred to as superuser or root), and unprivileged processes (whose effective UID is nonzero). Privileged processes bypass all kernel permission checks, while unprivileged processes are subject to full permission checking based on the process's credentials (usually: effective UID, effective GID, and supplementary group list).

Starting with Linux 2.2, Linux divides the privileges traditionally associated with superuser into distinct units, known as capabilities, which can be independently enabled and disabled.

Each Capability represents one or more privileges. For example, CAP_DAC_READ_SEARCH represents the ability to bypass permission checks for file reading, as well as directory reading and execution permissions. If a user with an effective UID of 0 (root user) lacks CAP_DAC_READ_SEARCH or higher capabilities, this means that even though they are root, they cannot read files at will.

KernelSU's Root Profile allows customization of the Capabilities of the root process after executing su, thereby achieving partially granting "root permissions." Unlike the aforementioned UID and GID, certain root apps require a UID of 0 after using su. In such cases, limiting the Capabilities of this root user with UID 0 can restrict their allowed operations.

Strong Recommendation

Linux's Capability official documentation provides detailed explanations of the abilities represented by each Capability. If you intend to customize Capabilities, it is strongly recommended that you read this document first.

SELinux

SELinux is a powerful Mandatory Access Control (MAC) mechanism. It operates on the principle of default deny: any action not explicitly allowed is denied.

SELinux can run in two global modes:

  1. Permissive mode: Denial events are logged but not enforced.
  2. Enforcing mode: Denial events are logged and enforced.

Warning

Modern Android systems heavily rely on SELinux to ensure overall system security. It is highly recommended not to use any custom systems running in "permissive mode" since it provides no significant advantages over a completely open system.

Explaining the full concept of SELinux is complex and beyond the scope of this document. It is recommended to first understand its workings through the following resources:

  1. Wikipedia
  2. Red Hat: What Is SELinux?
  3. ArchLinux: SELinux

KernelSU's Root Profile allows customization of the SELinux context of the root process after executing su. Specific access control rules can be set for this context to enable fine-grained control over root permissions.

In typical scenarios, when an app executes su, it switches the process to a SELinux domain with unrestricted access, such as u:r:su:s0. Through the Root Profile, this domain can be switched to a custom domain, such as u:r:app1:s0, and a series of rules can be defined for this domain:

sh
type app1
-enforce app1
-typeattribute app1 mlstrustedsubject
-allow app1 * * *

Note that the allow app1 * * * rule is used for demonstration purposes only. In practice, this rule should not be used extensively since it doesn't differ much from permissive mode.

Escalation

If the configuration of the Root Profile is not set properly, an escalation scenario may occur: the restrictions imposed by the Root Profile can unintentionally fail.

For example, if you grant root permission to an ADB shell user (which is a common case), and then you grant root permission to a regular application but configure its root profile with UID 2000 (which is the UID of the ADB shell user), the application can obtain full root access by executing the su command twice:

  1. The first su execution is subject to the enforcement of the App Profile and will switch to UID 2000 (adb shell) instead of 0 (root).
  2. The second su execution, since the UID is 2000, and you have granted root access to the UID 2000 (adb shell) in the configuration, the application will gain full root privileges.

Note

This behavior is entirely expected and not a bug. Therefore, we recommend the following:

If you genuinely need to grant root permissions to ADB (e.g., as a developer), it is not advisable to change the UID to 2000 when configuring the Root Profile. Using 1000 (system) would be a better choice.

Non-Root Profile

Umount Modules

KernelSU provides a systemless mechanism for modifying system partitions, achieved through overlayfs mounting. However, some apps may be sensitive to such behavior. Thus, we can unload modules mounted on these apps by setting the "umount modules" option.

Additionally, the settings interface of the KernelSU manager provides a switch for "umount modules by default". By default, this switch is enabled, which means that KernelSU or some modules will unload modules for this app unless additional settings are applied. If you do not prefer this setting or if it affects certain apps, you have the following options:

  1. Keep the switch for "umount modules by default" and individually disable the "umount modules" option in the App Profile for apps requiring module loading (acting as a "whitelist").
  2. Disable the switch for "umount modules by default" and individually enable the "umount modules" option in the App Profile for apps requiring module unloading (acting as a "blacklist").

INFO

In devices using kernel version 5.10 and above, the kernel performs the unloading of modules. However, for devices running kernel versions below 5.10, this switch is merely a configuration option, and KernelSU itself does not take any action. Some modules, such as Zygisksu, may use this switch to determine whether module unloading is necessary.

`,46),i=[n];function r(l,p,c,d,u,h){return o(),s("div",null,i)}const y=e(a,[["render",r]]);export{f as __pageData,y as default}; diff --git a/assets/guide_app-profile.md.9faae288.lean.js b/assets/guide_app-profile.md.9faae288.lean.js deleted file mode 100644 index f313821b8ca8..000000000000 --- a/assets/guide_app-profile.md.9faae288.lean.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as e,o,c as s,O as t}from"./chunks/framework.43781440.js";const f=JSON.parse('{"title":"App Profile","description":"","frontmatter":{},"headers":[],"relativePath":"guide/app-profile.md","filePath":"guide/app-profile.md"}'),a={name:"guide/app-profile.md"},n=t("",46),i=[n];function r(l,p,c,d,u,h){return o(),s("div",null,i)}const y=e(a,[["render",r]]);export{f as __pageData,y as default}; diff --git a/assets/guide_difference-with-magisk.md.88801b6b.js b/assets/guide_difference-with-magisk.md.6bc0c587.js similarity index 96% rename from assets/guide_difference-with-magisk.md.88801b6b.js rename to assets/guide_difference-with-magisk.md.6bc0c587.js index 4769fe4b123e..ea0b8588c9ab 100644 --- a/assets/guide_difference-with-magisk.md.88801b6b.js +++ b/assets/guide_difference-with-magisk.md.6bc0c587.js @@ -1 +1 @@ -import{_ as e,o as i,c as t,O as o}from"./chunks/framework.43781440.js";const p=JSON.parse('{"title":"Difference with Magisk","description":"","frontmatter":{},"headers":[],"relativePath":"guide/difference-with-magisk.md","filePath":"guide/difference-with-magisk.md"}'),a={name:"guide/difference-with-magisk.md"},s=o('

Difference with Magisk

Although there are many similarities between KernelSU modules and Magisk modules, there are inevitably some differences due to their completely different implementation mechanisms. If you want your module to run on both Magisk and KernelSU, you must understand these differences.

Similarities

  • Module file format: both use zip format to organize modules, and the format of modules is almost the same
  • Module installation directory: both located in /data/adb/modules
  • Systemless: both support modifying /system in a systemless way through modules
  • post-fs-data.sh: the execution time and semantics are exactly the same
  • service.sh: the execution time and semantics are exactly the same
  • system.prop: completely the same
  • sepolicy.rule: completely the same
  • BusyBox: scripts are run in BusyBox with "standalone mode" enabled in both cases

Differences

Before understanding the differences, you need to know how to differentiate whether your module is running in KernelSU or Magisk. You can use the environment variable KSU to differentiate it in all places where you can run module scripts (customize.sh, post-fs-data.sh, service.sh). In KernelSU, this environment variable will be set to true.

Here are some differences:

  • KernelSU modules cannot be installed in Recovery mode.
  • KernelSU modules do not have built-in support for Zygisk (but you can use Zygisk modules through ZygiskNext.
  • The method for replacing or deleting files in KernelSU modules is completely different from Magisk. KernelSU does not support the .replace method. Instead, you need to create a same-named file with mknod filename c 0 0 to delete the corresponding file.
  • The directories for BusyBox are different. The built-in BusyBox in KernelSU is located in /data/adb/ksu/bin/busybox, while in Magisk it is in /data/adb/magisk/busybox. Note that this is an internal behavior of KernelSU and may change in the future!
  • KernelSU does not support .replace files; however, KernelSU supports the REMOVE and REPLACE variable to remove or replace files and folders.
  • KernelSU adds boot-completed stage to run some scripts on boot completed.
  • KernelSU adds post-mount stage to run some scripts after mounting overlayfs
',8),r=[s];function n(l,d,c,h,m,u){return i(),t("div",null,r)}const g=e(a,[["render",n]]);export{p as __pageData,g as default}; +import{_ as e,o as i,c as t,Q as o}from"./chunks/framework.ec8f7e8e.js";const p=JSON.parse('{"title":"Difference with Magisk","description":"","frontmatter":{},"headers":[],"relativePath":"guide/difference-with-magisk.md","filePath":"guide/difference-with-magisk.md"}'),a={name:"guide/difference-with-magisk.md"},s=o('

Difference with Magisk

Although there are many similarities between KernelSU modules and Magisk modules, there are inevitably some differences due to their completely different implementation mechanisms. If you want your module to run on both Magisk and KernelSU, you must understand these differences.

Similarities

  • Module file format: both use zip format to organize modules, and the format of modules is almost the same
  • Module installation directory: both located in /data/adb/modules
  • Systemless: both support modifying /system in a systemless way through modules
  • post-fs-data.sh: the execution time and semantics are exactly the same
  • service.sh: the execution time and semantics are exactly the same
  • system.prop: completely the same
  • sepolicy.rule: completely the same
  • BusyBox: scripts are run in BusyBox with "standalone mode" enabled in both cases

Differences

Before understanding the differences, you need to know how to differentiate whether your module is running in KernelSU or Magisk. You can use the environment variable KSU to differentiate it in all places where you can run module scripts (customize.sh, post-fs-data.sh, service.sh). In KernelSU, this environment variable will be set to true.

Here are some differences:

  • KernelSU modules cannot be installed in Recovery mode.
  • KernelSU modules do not have built-in support for Zygisk (but you can use Zygisk modules through ZygiskNext.
  • The method for replacing or deleting files in KernelSU modules is completely different from Magisk. KernelSU does not support the .replace method. Instead, you need to create a same-named file with mknod filename c 0 0 to delete the corresponding file.
  • The directories for BusyBox are different. The built-in BusyBox in KernelSU is located in /data/adb/ksu/bin/busybox, while in Magisk it is in /data/adb/magisk/busybox. Note that this is an internal behavior of KernelSU and may change in the future!
  • KernelSU does not support .replace files; however, KernelSU supports the REMOVE and REPLACE variable to remove or replace files and folders.
  • KernelSU adds boot-completed stage to run some scripts on boot completed.
  • KernelSU adds post-mount stage to run some scripts after mounting overlayfs
',8),r=[s];function n(l,d,c,h,m,u){return i(),t("div",null,r)}const g=e(a,[["render",n]]);export{p as __pageData,g as default}; diff --git a/assets/guide_difference-with-magisk.md.88801b6b.lean.js b/assets/guide_difference-with-magisk.md.6bc0c587.lean.js similarity index 71% rename from assets/guide_difference-with-magisk.md.88801b6b.lean.js rename to assets/guide_difference-with-magisk.md.6bc0c587.lean.js index 78d6257d743a..4f7d9483c08c 100644 --- a/assets/guide_difference-with-magisk.md.88801b6b.lean.js +++ b/assets/guide_difference-with-magisk.md.6bc0c587.lean.js @@ -1 +1 @@ -import{_ as e,o as i,c as t,O as o}from"./chunks/framework.43781440.js";const p=JSON.parse('{"title":"Difference with Magisk","description":"","frontmatter":{},"headers":[],"relativePath":"guide/difference-with-magisk.md","filePath":"guide/difference-with-magisk.md"}'),a={name:"guide/difference-with-magisk.md"},s=o("",8),r=[s];function n(l,d,c,h,m,u){return i(),t("div",null,r)}const g=e(a,[["render",n]]);export{p as __pageData,g as default}; +import{_ as e,o as i,c as t,Q as o}from"./chunks/framework.ec8f7e8e.js";const p=JSON.parse('{"title":"Difference with Magisk","description":"","frontmatter":{},"headers":[],"relativePath":"guide/difference-with-magisk.md","filePath":"guide/difference-with-magisk.md"}'),a={name:"guide/difference-with-magisk.md"},s=o("",8),r=[s];function n(l,d,c,h,m,u){return i(),t("div",null,r)}const g=e(a,[["render",n]]);export{p as __pageData,g as default}; diff --git a/assets/guide_faq.md.234d8c0d.js b/assets/guide_faq.md.89521262.js similarity index 99% rename from assets/guide_faq.md.234d8c0d.js rename to assets/guide_faq.md.89521262.js index c175d2cf829d..a44751d0e016 100644 --- a/assets/guide_faq.md.234d8c0d.js +++ b/assets/guide_faq.md.89521262.js @@ -1 +1 @@ -import{_ as e,o,c as a,O as t}from"./chunks/framework.43781440.js";const m=JSON.parse('{"title":"FAQ","description":"","frontmatter":{},"headers":[],"relativePath":"guide/faq.md","filePath":"guide/faq.md"}'),r={name:"guide/faq.md"},n=t('

FAQ

Does KernelSU support my device?

First, your devices should be able to unlock the bootloader. If it can't, then it is unsupported.

Then install KernelSU manager App to your device and open it, if it shows Unsupported then your device cannot be supported out of box, but you can build kernel source and integrate KernelSU to make it work or using unofficially-support-devices.

Does KernelSU need to unlock Bootloader?

Certainly, yes.

Does KernelSU support modules?

Yes, But it is in early version, it may be buggy. Please wait for it to be stable 😃

Does KernelSU support Xposed?

Yes, Dreamland and TaiChi work now. For LSPosed, you can make it work by ZygiskNext

Does KernelSU support Zygisk?

KernelSU has no builtin Zygisk support, but you can use ZygiskNext instead.

Is KernelSU compatible with Magisk?

KernelSU's module system is conflict with Magisk's magic mount, if there is any module enabled in KernelSU, then the whole Magisk would not work.

But if you only use the su of KernelSU, then it will work well with Magisk: KernelSU modify the kernel and Magisk modify the ramdisk, they can work together.

Will KernelSU substitute Magisk?

We don't think so and it's not our goal. Magisk is good enough for userspace root solution and it will live long. KernelSU's goal is to provide a kernel interface to users, not substituting Magisk.

Can KernelSU support non GKI devices?

It is possible. But you should download the kernel source and intergrate KernelSU to the source tree and compile the kernel yourself.

Can KernelSU support devices below Android 12?

It is device's kernel that affect KernelSU's compatability and it has nothing to do with Android version.The only restriction is that devices launched with Android 12 must be kernel 5.10+(GKI devices). So:

  1. Devices launched with Android 12 must be supported.
  2. Devices with has an old kernel (Some Android 12 devices is also old kernel) is compatable (You should build kernel yourself)

Can KernelSU support old kernel?

It is possible, KernelSU is backported to kernel 4.14 now, for older kernel, you need to backport it manully and PRs welcome!

How to integrate KernelSU for old kernel?

Please refer guide

Why my Android version is 13, and the kernel shows "android12-5.10"?

The Kernel version has nothing to do with Android version, if you need to flash kernel, always use the kernel version, Android version is not so important.

Is there any --mount-master/global mount namespace in KernelSU?

There isn't now(maybe in the future), But there are many ways to switch to global mount namespace manully, such as:

  1. nsenter -t 1 -m sh to get a shell in global mount namespace.
  2. Add nsenter --mount=/proc/1/ns/mnt to the command you want to execute, then the command is executed in global mount namespace. KernelSU is also using this way

I am GKI1.0, can i use this?

GKI1 is completely different from GKI2, you must compile kernel by yourself.

',33),s=[n];function i(l,d,u,h,c,p){return o(),a("div",null,s)}const b=e(r,[["render",i]]);export{m as __pageData,b as default}; +import{_ as e,o,c as a,Q as t}from"./chunks/framework.ec8f7e8e.js";const m=JSON.parse('{"title":"FAQ","description":"","frontmatter":{},"headers":[],"relativePath":"guide/faq.md","filePath":"guide/faq.md"}'),r={name:"guide/faq.md"},n=t('

FAQ

Does KernelSU support my device?

First, your devices should be able to unlock the bootloader. If it can't, then it is unsupported.

Then install KernelSU manager App to your device and open it, if it shows Unsupported then your device cannot be supported out of box, but you can build kernel source and integrate KernelSU to make it work or using unofficially-support-devices.

Does KernelSU need to unlock Bootloader?

Certainly, yes.

Does KernelSU support modules?

Yes, But it is in early version, it may be buggy. Please wait for it to be stable 😃

Does KernelSU support Xposed?

Yes, Dreamland and TaiChi work now. For LSPosed, you can make it work by ZygiskNext

Does KernelSU support Zygisk?

KernelSU has no builtin Zygisk support, but you can use ZygiskNext instead.

Is KernelSU compatible with Magisk?

KernelSU's module system is conflict with Magisk's magic mount, if there is any module enabled in KernelSU, then the whole Magisk would not work.

But if you only use the su of KernelSU, then it will work well with Magisk: KernelSU modify the kernel and Magisk modify the ramdisk, they can work together.

Will KernelSU substitute Magisk?

We don't think so and it's not our goal. Magisk is good enough for userspace root solution and it will live long. KernelSU's goal is to provide a kernel interface to users, not substituting Magisk.

Can KernelSU support non GKI devices?

It is possible. But you should download the kernel source and intergrate KernelSU to the source tree and compile the kernel yourself.

Can KernelSU support devices below Android 12?

It is device's kernel that affect KernelSU's compatability and it has nothing to do with Android version.The only restriction is that devices launched with Android 12 must be kernel 5.10+(GKI devices). So:

  1. Devices launched with Android 12 must be supported.
  2. Devices with has an old kernel (Some Android 12 devices is also old kernel) is compatable (You should build kernel yourself)

Can KernelSU support old kernel?

It is possible, KernelSU is backported to kernel 4.14 now, for older kernel, you need to backport it manully and PRs welcome!

How to integrate KernelSU for old kernel?

Please refer guide

Why my Android version is 13, and the kernel shows "android12-5.10"?

The Kernel version has nothing to do with Android version, if you need to flash kernel, always use the kernel version, Android version is not so important.

Is there any --mount-master/global mount namespace in KernelSU?

There isn't now(maybe in the future), But there are many ways to switch to global mount namespace manully, such as:

  1. nsenter -t 1 -m sh to get a shell in global mount namespace.
  2. Add nsenter --mount=/proc/1/ns/mnt to the command you want to execute, then the command is executed in global mount namespace. KernelSU is also using this way

I am GKI1.0, can i use this?

GKI1 is completely different from GKI2, you must compile kernel by yourself.

',33),s=[n];function i(l,d,u,h,c,p){return o(),a("div",null,s)}const b=e(r,[["render",i]]);export{m as __pageData,b as default}; diff --git a/assets/guide_faq.md.234d8c0d.lean.js b/assets/guide_faq.md.89521262.lean.js similarity index 82% rename from assets/guide_faq.md.234d8c0d.lean.js rename to assets/guide_faq.md.89521262.lean.js index ab03a21a2348..f410be157972 100644 --- a/assets/guide_faq.md.234d8c0d.lean.js +++ b/assets/guide_faq.md.89521262.lean.js @@ -1 +1 @@ -import{_ as e,o,c as a,O as t}from"./chunks/framework.43781440.js";const m=JSON.parse('{"title":"FAQ","description":"","frontmatter":{},"headers":[],"relativePath":"guide/faq.md","filePath":"guide/faq.md"}'),r={name:"guide/faq.md"},n=t("",33),s=[n];function i(l,d,u,h,c,p){return o(),a("div",null,s)}const b=e(r,[["render",i]]);export{m as __pageData,b as default}; +import{_ as e,o,c as a,Q as t}from"./chunks/framework.ec8f7e8e.js";const m=JSON.parse('{"title":"FAQ","description":"","frontmatter":{},"headers":[],"relativePath":"guide/faq.md","filePath":"guide/faq.md"}'),r={name:"guide/faq.md"},n=t("",33),s=[n];function i(l,d,u,h,c,p){return o(),a("div",null,s)}const b=e(r,[["render",i]]);export{m as __pageData,b as default}; diff --git a/assets/guide_hidden-features.md.45e791e6.js b/assets/guide_hidden-features.md.469c34a7.js similarity index 85% rename from assets/guide_hidden-features.md.45e791e6.js rename to assets/guide_hidden-features.md.469c34a7.js index d40fcd6386e5..598eeb191bbd 100644 --- a/assets/guide_hidden-features.md.45e791e6.js +++ b/assets/guide_hidden-features.md.469c34a7.js @@ -1 +1 @@ -import{_ as e,o as a,c as t,O as d}from"./chunks/framework.43781440.js";const m=JSON.parse('{"title":"Hidden Features","description":"","frontmatter":{},"headers":[],"relativePath":"guide/hidden-features.md","filePath":"guide/hidden-features.md"}'),s={name:"guide/hidden-features.md"},r=d('

Hidden Features

.ksurc

By default, /system/bin/sh loads /system/etc/mkshrc.

You can make su load customized rc file by creating a /data/adb/ksu/.ksurc file.

',4),c=[r];function o(i,n,u,_,h,l){return a(),t("div",null,c)}const p=e(s,[["render",o]]);export{m as __pageData,p as default}; +import{_ as e,o as a,c as t,Q as d}from"./chunks/framework.ec8f7e8e.js";const m=JSON.parse('{"title":"Hidden Features","description":"","frontmatter":{},"headers":[],"relativePath":"guide/hidden-features.md","filePath":"guide/hidden-features.md"}'),s={name:"guide/hidden-features.md"},r=d('

Hidden Features

.ksurc

By default, /system/bin/sh loads /system/etc/mkshrc.

You can make su load customized rc file by creating a /data/adb/ksu/.ksurc file.

',4),c=[r];function o(i,n,u,_,h,l){return a(),t("div",null,c)}const p=e(s,[["render",o]]);export{m as __pageData,p as default}; diff --git a/assets/guide_hidden-features.md.45e791e6.lean.js b/assets/guide_hidden-features.md.469c34a7.lean.js similarity index 69% rename from assets/guide_hidden-features.md.45e791e6.lean.js rename to assets/guide_hidden-features.md.469c34a7.lean.js index 4dbf2feece6f..a32a7599cb15 100644 --- a/assets/guide_hidden-features.md.45e791e6.lean.js +++ b/assets/guide_hidden-features.md.469c34a7.lean.js @@ -1 +1 @@ -import{_ as e,o as a,c as t,O as d}from"./chunks/framework.43781440.js";const m=JSON.parse('{"title":"Hidden Features","description":"","frontmatter":{},"headers":[],"relativePath":"guide/hidden-features.md","filePath":"guide/hidden-features.md"}'),s={name:"guide/hidden-features.md"},r=d("",4),c=[r];function o(i,n,u,_,h,l){return a(),t("div",null,c)}const p=e(s,[["render",o]]);export{m as __pageData,p as default}; +import{_ as e,o as a,c as t,Q as d}from"./chunks/framework.ec8f7e8e.js";const m=JSON.parse('{"title":"Hidden Features","description":"","frontmatter":{},"headers":[],"relativePath":"guide/hidden-features.md","filePath":"guide/hidden-features.md"}'),s={name:"guide/hidden-features.md"},r=d("",4),c=[r];function o(i,n,u,_,h,l){return a(),t("div",null,c)}const p=e(s,[["render",o]]);export{m as __pageData,p as default}; diff --git a/assets/guide_how-to-build.md.83e45bdd.js b/assets/guide_how-to-build.md.83e45bdd.js new file mode 100644 index 000000000000..b458a04fec85 --- /dev/null +++ b/assets/guide_how-to-build.md.83e45bdd.js @@ -0,0 +1,7 @@ +import{_ as s,o as a,c as e,Q as l}from"./chunks/framework.ec8f7e8e.js";const E=JSON.parse('{"title":"How to build KernelSU?","description":"","frontmatter":{},"headers":[],"relativePath":"guide/how-to-build.md","filePath":"guide/how-to-build.md"}'),n={name:"guide/how-to-build.md"},o=l(`

How to build KernelSU?

First, you should read the Android Official docs for kernel build:

  1. Building Kernels
  2. GKI Release Builds

WARNING

This page is for GKI devices, if you use an old kernel, please refer how to integrate KernelSU for old kernel

Build Kernel

Sync the kernel source code

sh
repo init -u https://android.googlesource.com/kernel/manifest
+mv <kernel_manifest.xml> .repo/manifests
+repo init -m manifest.xml
+repo sync
repo init -u https://android.googlesource.com/kernel/manifest
+mv <kernel_manifest.xml> .repo/manifests
+repo init -m manifest.xml
+repo sync

The <kernel_manifest.xml> is a manifest file which can determine a build uniquely, you can use the manifest to do a re-preducable build. You should download the manifest file from Google GKI release builds

Build

Please check the official docs first.

For example, we need to build aarch64 kernel image:

sh
LTO=thin BUILD_CONFIG=common/build.config.gki.aarch64 build/build.sh
LTO=thin BUILD_CONFIG=common/build.config.gki.aarch64 build/build.sh

Don't forget to add the LTO=thin flag, otherwise the build may fail if your computer's memory is less then 24Gb.

Starting from Android 13, the kernel is built by bazel:

sh
tools/bazel build --config=fast //common:kernel_aarch64_dist
tools/bazel build --config=fast //common:kernel_aarch64_dist

Build Kernel with KernelSU

If you can build the kernel successfully, then build KernelSU is so easy, Select any one run in Kernel source root dir:

  • Latest tag(stable)
sh
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -
  • main branch(dev)
sh
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -s main
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -s main
  • Select tag(Such as v0.5.2)
sh
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -s v0.5.2
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -s v0.5.2

And then rebuild the kernel and you will get a kernel image with KernelSU!

`,24),p=[o];function t(r,c,i,d,y,u){return a(),e("div",null,p)}const F=s(n,[["render",t]]);export{E as __pageData,F as default}; diff --git a/assets/guide_how-to-build.md.83e45bdd.lean.js b/assets/guide_how-to-build.md.83e45bdd.lean.js new file mode 100644 index 000000000000..6695a335f82c --- /dev/null +++ b/assets/guide_how-to-build.md.83e45bdd.lean.js @@ -0,0 +1 @@ +import{_ as s,o as a,c as e,Q as l}from"./chunks/framework.ec8f7e8e.js";const E=JSON.parse('{"title":"How to build KernelSU?","description":"","frontmatter":{},"headers":[],"relativePath":"guide/how-to-build.md","filePath":"guide/how-to-build.md"}'),n={name:"guide/how-to-build.md"},o=l("",24),p=[o];function t(r,c,i,d,y,u){return a(),e("div",null,p)}const F=s(n,[["render",t]]);export{E as __pageData,F as default}; diff --git a/assets/guide_how-to-build.md.cf9f2c40.js b/assets/guide_how-to-build.md.cf9f2c40.js deleted file mode 100644 index ad0bfd07c260..000000000000 --- a/assets/guide_how-to-build.md.cf9f2c40.js +++ /dev/null @@ -1,4 +0,0 @@ -import{_ as s,o as e,c as a,O as l}from"./chunks/framework.43781440.js";const y=JSON.parse('{"title":"How to build KernelSU?","description":"","frontmatter":{},"headers":[],"relativePath":"guide/how-to-build.md","filePath":"guide/how-to-build.md"}'),n={name:"guide/how-to-build.md"},o=l(`

How to build KernelSU?

First, you should read the Android Official docs for kernel build:

  1. Building Kernels
  2. GKI Release Builds

WARNING

This page is for GKI devices, if you use an old kernel, please refer how to integrate KernelSU for old kernel

Build Kernel

Sync the kernel source code

sh
repo init -u https://android.googlesource.com/kernel/manifest
-mv <kernel_manifest.xml> .repo/manifests
-repo init -m manifest.xml
-repo sync

The <kernel_manifest.xml> is a manifest file which can determine a build uniquely, you can use the manifest to do a re-preducable build. You should download the manifest file from Google GKI release builds

Build

Please check the official docs first.

For example, we need to build aarch64 kernel image:

sh
LTO=thin BUILD_CONFIG=common/build.config.gki.aarch64 build/build.sh

Don't forget to add the LTO=thin flag, otherwise the build may fail if your computer's memory is less then 24Gb.

Starting from Android 13, the kernel is built by bazel:

sh
tools/bazel build --config=fast //common:kernel_aarch64_dist

Build Kernel with KernelSU

If you can build the kernel successfully, then build KernelSU is so easy, Select any one run in Kernel source root dir:

  • Latest tag(stable)
sh
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -
  • main branch(dev)
sh
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -s main
  • Select tag(Such as v0.5.2)
sh
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -s v0.5.2

And then rebuild the kernel and you will get a kernel image with KernelSU!

`,24),t=[o];function r(p,c,i,d,u,h){return e(),a("div",null,t)}const D=s(n,[["render",r]]);export{y as __pageData,D as default}; diff --git a/assets/guide_how-to-build.md.cf9f2c40.lean.js b/assets/guide_how-to-build.md.cf9f2c40.lean.js deleted file mode 100644 index b7e4dcff60dc..000000000000 --- a/assets/guide_how-to-build.md.cf9f2c40.lean.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as s,o as e,c as a,O as l}from"./chunks/framework.43781440.js";const y=JSON.parse('{"title":"How to build KernelSU?","description":"","frontmatter":{},"headers":[],"relativePath":"guide/how-to-build.md","filePath":"guide/how-to-build.md"}'),n={name:"guide/how-to-build.md"},o=l("",24),t=[o];function r(p,c,i,d,u,h){return e(),a("div",null,t)}const D=s(n,[["render",r]]);export{y as __pageData,D as default}; diff --git a/assets/guide_how-to-integrate-for-non-gki.md.35d629c6.js b/assets/guide_how-to-integrate-for-non-gki.md.35d629c6.js deleted file mode 100644 index 16f0f70f9a2c..000000000000 --- a/assets/guide_how-to-integrate-for-non-gki.md.35d629c6.js +++ /dev/null @@ -1,152 +0,0 @@ -import{_ as s,o as n,c as a,O as e}from"./chunks/framework.43781440.js";const A=JSON.parse('{"title":"How to integrate KernelSU for non GKI kernels?","description":"","frontmatter":{},"headers":[],"relativePath":"guide/how-to-integrate-for-non-gki.md","filePath":"guide/how-to-integrate-for-non-gki.md"}'),l={name:"guide/how-to-integrate-for-non-gki.md"},p=e(`

How to integrate KernelSU for non GKI kernels?

KernelSU can be integrated into non GKI kernels, and was backported to 4.14 and below.

Due to the fragmentization of non GKI kernels, we do not have a uniform way to build it, so we can not provide non GKI boot images. But you can build the kernel yourself with KernelSU integrated.

First, you should be able to build a bootable kernel from kernel source code. If the kernel is not open source, then it is difficult to run KernelSU for your device.

If you can build a bootable kernel, there are two ways to integrate KernelSU to the kernel source code:

  1. Automatically with kprobe
  2. Manually

Integrate with kprobe

KernelSU uses kprobe to do kernel hooks, if the kprobe runs well in your kernel, it is recommended to use this way.

First, add KernelSU to your kernel source tree:

sh
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -

Then, you should check if kprobe is enabled in your kernel config, if it is not, please add these configs to it:

CONFIG_KPROBES=y
-CONFIG_HAVE_KPROBES=y
-CONFIG_KPROBE_EVENTS=y

And build your kernel again, KernelSU should works well.

If you find that KPROBES is still not activated, you can try enabling CONFIG_MODULES. (If it still doesn't take effect, use make menuconfig to search for other dependencies of KPROBES)

But if you encounter a boot loop when integrated KernelSU, it is maybe kprobe is broken in your kernel, you should fix the kprobe bug or use the second way.

How to check if kprobe is broken?

comment out ksu_enable_sucompat() and ksu_enable_ksud() in KernelSU/kernel/ksu.c, if the device boots normally, then kprobe may be broken.

Manually modify the kernel source

If kprobe does not work in your kernel (may be an upstream or kernel bug below 4.8), then you can try this way:

First, add KernelSU to your kernel source tree:

  • Latest tag(stable)
sh
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -
  • main branch(dev)
sh
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -s main
  • Select tag(Such as v0.5.2)
sh
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -s v0.5.2

Then, add KernelSU calls to the kernel source, here is a patch to refer:

diff
diff --git a/fs/exec.c b/fs/exec.c
-index ac59664eaecf..bdd585e1d2cc 100644
---- a/fs/exec.c
-+++ b/fs/exec.c
-@@ -1890,11 +1890,14 @@ static int __do_execve_file(int fd, struct filename *filename,
- 	return retval;
- }
- 
-+extern bool ksu_execveat_hook __read_mostly;
-+extern int ksu_handle_execveat(int *fd, struct filename **filename_ptr, void *argv,
-+			void *envp, int *flags);
-+extern int ksu_handle_execveat_sucompat(int *fd, struct filename **filename_ptr,
-+				 void *argv, void *envp, int *flags);
- static int do_execveat_common(int fd, struct filename *filename,
- 			      struct user_arg_ptr argv,
- 			      struct user_arg_ptr envp,
- 			      int flags)
- {
-+	if (unlikely(ksu_execveat_hook))
-+		ksu_handle_execveat(&fd, &filename, &argv, &envp, &flags);
-+	else
-+		ksu_handle_execveat_sucompat(&fd, &filename, &argv, &envp, &flags);
- 	return __do_execve_file(fd, filename, argv, envp, flags, NULL);
- }
- 
-diff --git a/fs/open.c b/fs/open.c
-index 05036d819197..965b84d486b8 100644
---- a/fs/open.c
-+++ b/fs/open.c
-@@ -348,6 +348,8 @@ SYSCALL_DEFINE4(fallocate, int, fd, int, mode, loff_t, offset, loff_t, len)
- 	return ksys_fallocate(fd, mode, offset, len);
- }
- 
-+extern int ksu_handle_faccessat(int *dfd, const char __user **filename_user, int *mode,
-+			 int *flags);
- /*
-  * access() needs to use the real uid/gid, not the effective uid/gid.
-  * We do this by temporarily clearing all FS-related capabilities and
-@@ -355,6 +357,7 @@ SYSCALL_DEFINE4(fallocate, int, fd, int, mode, loff_t, offset, loff_t, len)
-  */
- long do_faccessat(int dfd, const char __user *filename, int mode)
- {
-+	ksu_handle_faccessat(&dfd, &filename, &mode, NULL);
- 	const struct cred *old_cred;
- 	struct cred *override_cred;
- 	struct path path;
-diff --git a/fs/read_write.c b/fs/read_write.c
-index 650fc7e0f3a6..55be193913b6 100644
---- a/fs/read_write.c
-+++ b/fs/read_write.c
-@@ -434,10 +434,14 @@ ssize_t kernel_read(struct file *file, void *buf, size_t count, loff_t *pos)
- }
- EXPORT_SYMBOL(kernel_read);
- 
-+extern bool ksu_vfs_read_hook __read_mostly;
-+extern int ksu_handle_vfs_read(struct file **file_ptr, char __user **buf_ptr,
-+			size_t *count_ptr, loff_t **pos);
- ssize_t vfs_read(struct file *file, char __user *buf, size_t count, loff_t *pos)
- {
- 	ssize_t ret;
- 
-+	if (unlikely(ksu_vfs_read_hook))
-+		ksu_handle_vfs_read(&file, &buf, &count, &pos);
-+
- 	if (!(file->f_mode & FMODE_READ))
- 		return -EBADF;
- 	if (!(file->f_mode & FMODE_CAN_READ))
-diff --git a/fs/stat.c b/fs/stat.c
-index 376543199b5a..82adcef03ecc 100644
---- a/fs/stat.c
-+++ b/fs/stat.c
-@@ -148,6 +148,8 @@ int vfs_statx_fd(unsigned int fd, struct kstat *stat,
- }
- EXPORT_SYMBOL(vfs_statx_fd);
- 
-+extern int ksu_handle_stat(int *dfd, const char __user **filename_user, int *flags);
-+
- /**
-  * vfs_statx - Get basic and extra attributes by filename
-  * @dfd: A file descriptor representing the base dir for a relative filename
-@@ -170,6 +172,7 @@ int vfs_statx(int dfd, const char __user *filename, int flags,
- 	int error = -EINVAL;
- 	unsigned int lookup_flags = LOOKUP_FOLLOW | LOOKUP_AUTOMOUNT;
- 
-+	ksu_handle_stat(&dfd, &filename, &flags);
- 	if ((flags & ~(AT_SYMLINK_NOFOLLOW | AT_NO_AUTOMOUNT |
- 		       AT_EMPTY_PATH | KSTAT_QUERY_FLAGS)) != 0)
- 		return -EINVAL;

You should find the four functions in kernel source:

  1. do_faccessat, usually in fs/open.c
  2. do_execveat_common, usually in fs/exec.c
  3. vfs_read, usually in fs/read_write.c
  4. vfs_statx, usually in fs/stat.c

If your kernel does not have the vfs_statx, use vfs_fstatat instead:

diff
diff --git a/fs/stat.c b/fs/stat.c
-index 068fdbcc9e26..5348b7bb9db2 100644
---- a/fs/stat.c
-+++ b/fs/stat.c
-@@ -87,6 +87,8 @@ int vfs_fstat(unsigned int fd, struct kstat *stat)
- }
- EXPORT_SYMBOL(vfs_fstat);
- 
-+extern int ksu_handle_stat(int *dfd, const char __user **filename_user, int *flags);
-+
- int vfs_fstatat(int dfd, const char __user *filename, struct kstat *stat,
- 		int flag)
- {
-@@ -94,6 +96,8 @@ int vfs_fstatat(int dfd, const char __user *filename, struct kstat *stat,
- 	int error = -EINVAL;
- 	unsigned int lookup_flags = 0;
- 
-+	ksu_handle_stat(&dfd, &filename, &flag);
-+
- 	if ((flag & ~(AT_SYMLINK_NOFOLLOW | AT_NO_AUTOMOUNT |
- 		      AT_EMPTY_PATH)) != 0)
- 		goto out;

For kernels eariler than 4.17, if you cannot find do_faccessat, just go to the definition of the faccessat syscall and place the call there:

diff
diff --git a/fs/open.c b/fs/open.c
-index 2ff887661237..e758d7db7663 100644
---- a/fs/open.c
-+++ b/fs/open.c
-@@ -355,6 +355,9 @@ SYSCALL_DEFINE4(fallocate, int, fd, int, mode, loff_t, offset, loff_t, len)
- 	return error;
- }
- 
-+extern int ksu_handle_faccessat(int *dfd, const char __user **filename_user, int *mode,
-+			        int *flags);
-+
- /*
-  * access() needs to use the real uid/gid, not the effective uid/gid.
-  * We do this by temporarily clearing all FS-related capabilities and
-@@ -370,6 +373,8 @@ SYSCALL_DEFINE3(faccessat, int, dfd, const char __user *, filename, int, mode)
- 	int res;
- 	unsigned int lookup_flags = LOOKUP_FOLLOW;
- 
-+	ksu_handle_faccessat(&dfd, &filename, &mode, NULL);
-+
- 	if (mode & ~S_IRWXO)	/* where's F_OK, X_OK, W_OK, R_OK? */
- 		return -EINVAL;

To enable KernelSU's builtin SafeMode, You should also modify input_handle_event in drivers/input/input.c:

TIP

It is strongly recommended to enable this feature, it is very helpful to prevent bootloops!

diff
diff --git a/drivers/input/input.c b/drivers/input/input.c
-index 45306f9ef247..815091ebfca4 100755
---- a/drivers/input/input.c
-+++ b/drivers/input/input.c
-@@ -367,10 +367,13 @@ static int input_get_disposition(struct input_dev *dev,
- 	return disposition;
- }
- 
-+extern bool ksu_input_hook __read_mostly;
-+extern int ksu_handle_input_handle_event(unsigned int *type, unsigned int *code, int *value);
-+
- static void input_handle_event(struct input_dev *dev,
- 			       unsigned int type, unsigned int code, int value)
- {
-	int disposition = input_get_disposition(dev, type, code, &value);
-+
-+	if (unlikely(ksu_input_hook))
-+		ksu_handle_input_handle_event(&type, &code, &value);
- 
- 	if (disposition != INPUT_IGNORE_EVENT && type != EV_SYN)
- 		add_input_randomness(type, code, value);

Finally, build your kernel again, KernelSU should work well.

`,37),o=[p];function t(c,r,i,C,y,D){return n(),a("div",null,o)}const f=s(l,[["render",t]]);export{A as __pageData,f as default}; diff --git a/assets/guide_how-to-integrate-for-non-gki.md.35d629c6.lean.js b/assets/guide_how-to-integrate-for-non-gki.md.35d629c6.lean.js deleted file mode 100644 index 20679b7c9597..000000000000 --- a/assets/guide_how-to-integrate-for-non-gki.md.35d629c6.lean.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as s,o as n,c as a,O as e}from"./chunks/framework.43781440.js";const A=JSON.parse('{"title":"How to integrate KernelSU for non GKI kernels?","description":"","frontmatter":{},"headers":[],"relativePath":"guide/how-to-integrate-for-non-gki.md","filePath":"guide/how-to-integrate-for-non-gki.md"}'),l={name:"guide/how-to-integrate-for-non-gki.md"},p=e("",37),o=[p];function t(c,r,i,C,y,D){return n(),a("div",null,o)}const f=s(l,[["render",t]]);export{A as __pageData,f as default}; diff --git a/assets/guide_how-to-integrate-for-non-gki.md.5ad8c015.js b/assets/guide_how-to-integrate-for-non-gki.md.5ad8c015.js new file mode 100644 index 000000000000..3f469ecc8270 --- /dev/null +++ b/assets/guide_how-to-integrate-for-non-gki.md.5ad8c015.js @@ -0,0 +1,303 @@ +import{_ as s,o as n,c as a,Q as e}from"./chunks/framework.ec8f7e8e.js";const u=JSON.parse('{"title":"How to integrate KernelSU for non GKI kernels?","description":"","frontmatter":{},"headers":[],"relativePath":"guide/how-to-integrate-for-non-gki.md","filePath":"guide/how-to-integrate-for-non-gki.md"}'),l={name:"guide/how-to-integrate-for-non-gki.md"},p=e(`

How to integrate KernelSU for non GKI kernels?

KernelSU can be integrated into non GKI kernels, and was backported to 4.14 and below.

Due to the fragmentization of non GKI kernels, we do not have a uniform way to build it, so we can not provide non GKI boot images. But you can build the kernel yourself with KernelSU integrated.

First, you should be able to build a bootable kernel from kernel source code. If the kernel is not open source, then it is difficult to run KernelSU for your device.

If you can build a bootable kernel, there are two ways to integrate KernelSU to the kernel source code:

  1. Automatically with kprobe
  2. Manually

Integrate with kprobe

KernelSU uses kprobe to do kernel hooks, if the kprobe runs well in your kernel, it is recommended to use this way.

First, add KernelSU to your kernel source tree:

sh
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -

Then, you should check if kprobe is enabled in your kernel config, if it is not, please add these configs to it:

CONFIG_KPROBES=y
+CONFIG_HAVE_KPROBES=y
+CONFIG_KPROBE_EVENTS=y
CONFIG_KPROBES=y
+CONFIG_HAVE_KPROBES=y
+CONFIG_KPROBE_EVENTS=y

And build your kernel again, KernelSU should works well.

If you find that KPROBES is still not activated, you can try enabling CONFIG_MODULES. (If it still doesn't take effect, use make menuconfig to search for other dependencies of KPROBES)

But if you encounter a boot loop when integrated KernelSU, it is maybe kprobe is broken in your kernel, you should fix the kprobe bug or use the second way.

How to check if kprobe is broken?

comment out ksu_enable_sucompat() and ksu_enable_ksud() in KernelSU/kernel/ksu.c, if the device boots normally, then kprobe may be broken.

Manually modify the kernel source

If kprobe does not work in your kernel (may be an upstream or kernel bug below 4.8), then you can try this way:

First, add KernelSU to your kernel source tree:

  • Latest tag(stable)
sh
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -
  • main branch(dev)
sh
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -s main
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -s main
  • Select tag(Such as v0.5.2)
sh
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -s v0.5.2
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -s v0.5.2

Then, add KernelSU calls to the kernel source, here is a patch to refer:

diff
diff --git a/fs/exec.c b/fs/exec.c
+index ac59664eaecf..bdd585e1d2cc 100644
+--- a/fs/exec.c
++++ b/fs/exec.c
+@@ -1890,11 +1890,14 @@ static int __do_execve_file(int fd, struct filename *filename,
+ 	return retval;
+ }
+ 
++extern bool ksu_execveat_hook __read_mostly;
++extern int ksu_handle_execveat(int *fd, struct filename **filename_ptr, void *argv,
++			void *envp, int *flags);
++extern int ksu_handle_execveat_sucompat(int *fd, struct filename **filename_ptr,
++				 void *argv, void *envp, int *flags);
+ static int do_execveat_common(int fd, struct filename *filename,
+ 			      struct user_arg_ptr argv,
+ 			      struct user_arg_ptr envp,
+ 			      int flags)
+ {
++	if (unlikely(ksu_execveat_hook))
++		ksu_handle_execveat(&fd, &filename, &argv, &envp, &flags);
++	else
++		ksu_handle_execveat_sucompat(&fd, &filename, &argv, &envp, &flags);
+ 	return __do_execve_file(fd, filename, argv, envp, flags, NULL);
+ }
+ 
+diff --git a/fs/open.c b/fs/open.c
+index 05036d819197..965b84d486b8 100644
+--- a/fs/open.c
++++ b/fs/open.c
+@@ -348,6 +348,8 @@ SYSCALL_DEFINE4(fallocate, int, fd, int, mode, loff_t, offset, loff_t, len)
+ 	return ksys_fallocate(fd, mode, offset, len);
+ }
+ 
++extern int ksu_handle_faccessat(int *dfd, const char __user **filename_user, int *mode,
++			 int *flags);
+ /*
+  * access() needs to use the real uid/gid, not the effective uid/gid.
+  * We do this by temporarily clearing all FS-related capabilities and
+@@ -355,6 +357,7 @@ SYSCALL_DEFINE4(fallocate, int, fd, int, mode, loff_t, offset, loff_t, len)
+  */
+ long do_faccessat(int dfd, const char __user *filename, int mode)
+ {
++	ksu_handle_faccessat(&dfd, &filename, &mode, NULL);
+ 	const struct cred *old_cred;
+ 	struct cred *override_cred;
+ 	struct path path;
+diff --git a/fs/read_write.c b/fs/read_write.c
+index 650fc7e0f3a6..55be193913b6 100644
+--- a/fs/read_write.c
++++ b/fs/read_write.c
+@@ -434,10 +434,14 @@ ssize_t kernel_read(struct file *file, void *buf, size_t count, loff_t *pos)
+ }
+ EXPORT_SYMBOL(kernel_read);
+ 
++extern bool ksu_vfs_read_hook __read_mostly;
++extern int ksu_handle_vfs_read(struct file **file_ptr, char __user **buf_ptr,
++			size_t *count_ptr, loff_t **pos);
+ ssize_t vfs_read(struct file *file, char __user *buf, size_t count, loff_t *pos)
+ {
+ 	ssize_t ret;
+ 
++	if (unlikely(ksu_vfs_read_hook))
++		ksu_handle_vfs_read(&file, &buf, &count, &pos);
++
+ 	if (!(file->f_mode & FMODE_READ))
+ 		return -EBADF;
+ 	if (!(file->f_mode & FMODE_CAN_READ))
+diff --git a/fs/stat.c b/fs/stat.c
+index 376543199b5a..82adcef03ecc 100644
+--- a/fs/stat.c
++++ b/fs/stat.c
+@@ -148,6 +148,8 @@ int vfs_statx_fd(unsigned int fd, struct kstat *stat,
+ }
+ EXPORT_SYMBOL(vfs_statx_fd);
+ 
++extern int ksu_handle_stat(int *dfd, const char __user **filename_user, int *flags);
++
+ /**
+  * vfs_statx - Get basic and extra attributes by filename
+  * @dfd: A file descriptor representing the base dir for a relative filename
+@@ -170,6 +172,7 @@ int vfs_statx(int dfd, const char __user *filename, int flags,
+ 	int error = -EINVAL;
+ 	unsigned int lookup_flags = LOOKUP_FOLLOW | LOOKUP_AUTOMOUNT;
+ 
++	ksu_handle_stat(&dfd, &filename, &flags);
+ 	if ((flags & ~(AT_SYMLINK_NOFOLLOW | AT_NO_AUTOMOUNT |
+ 		       AT_EMPTY_PATH | KSTAT_QUERY_FLAGS)) != 0)
+ 		return -EINVAL;
diff --git a/fs/exec.c b/fs/exec.c
+index ac59664eaecf..bdd585e1d2cc 100644
+--- a/fs/exec.c
++++ b/fs/exec.c
+@@ -1890,11 +1890,14 @@ static int __do_execve_file(int fd, struct filename *filename,
+ 	return retval;
+ }
+ 
++extern bool ksu_execveat_hook __read_mostly;
++extern int ksu_handle_execveat(int *fd, struct filename **filename_ptr, void *argv,
++			void *envp, int *flags);
++extern int ksu_handle_execveat_sucompat(int *fd, struct filename **filename_ptr,
++				 void *argv, void *envp, int *flags);
+ static int do_execveat_common(int fd, struct filename *filename,
+ 			      struct user_arg_ptr argv,
+ 			      struct user_arg_ptr envp,
+ 			      int flags)
+ {
++	if (unlikely(ksu_execveat_hook))
++		ksu_handle_execveat(&fd, &filename, &argv, &envp, &flags);
++	else
++		ksu_handle_execveat_sucompat(&fd, &filename, &argv, &envp, &flags);
+ 	return __do_execve_file(fd, filename, argv, envp, flags, NULL);
+ }
+ 
+diff --git a/fs/open.c b/fs/open.c
+index 05036d819197..965b84d486b8 100644
+--- a/fs/open.c
++++ b/fs/open.c
+@@ -348,6 +348,8 @@ SYSCALL_DEFINE4(fallocate, int, fd, int, mode, loff_t, offset, loff_t, len)
+ 	return ksys_fallocate(fd, mode, offset, len);
+ }
+ 
++extern int ksu_handle_faccessat(int *dfd, const char __user **filename_user, int *mode,
++			 int *flags);
+ /*
+  * access() needs to use the real uid/gid, not the effective uid/gid.
+  * We do this by temporarily clearing all FS-related capabilities and
+@@ -355,6 +357,7 @@ SYSCALL_DEFINE4(fallocate, int, fd, int, mode, loff_t, offset, loff_t, len)
+  */
+ long do_faccessat(int dfd, const char __user *filename, int mode)
+ {
++	ksu_handle_faccessat(&dfd, &filename, &mode, NULL);
+ 	const struct cred *old_cred;
+ 	struct cred *override_cred;
+ 	struct path path;
+diff --git a/fs/read_write.c b/fs/read_write.c
+index 650fc7e0f3a6..55be193913b6 100644
+--- a/fs/read_write.c
++++ b/fs/read_write.c
+@@ -434,10 +434,14 @@ ssize_t kernel_read(struct file *file, void *buf, size_t count, loff_t *pos)
+ }
+ EXPORT_SYMBOL(kernel_read);
+ 
++extern bool ksu_vfs_read_hook __read_mostly;
++extern int ksu_handle_vfs_read(struct file **file_ptr, char __user **buf_ptr,
++			size_t *count_ptr, loff_t **pos);
+ ssize_t vfs_read(struct file *file, char __user *buf, size_t count, loff_t *pos)
+ {
+ 	ssize_t ret;
+ 
++	if (unlikely(ksu_vfs_read_hook))
++		ksu_handle_vfs_read(&file, &buf, &count, &pos);
++
+ 	if (!(file->f_mode & FMODE_READ))
+ 		return -EBADF;
+ 	if (!(file->f_mode & FMODE_CAN_READ))
+diff --git a/fs/stat.c b/fs/stat.c
+index 376543199b5a..82adcef03ecc 100644
+--- a/fs/stat.c
++++ b/fs/stat.c
+@@ -148,6 +148,8 @@ int vfs_statx_fd(unsigned int fd, struct kstat *stat,
+ }
+ EXPORT_SYMBOL(vfs_statx_fd);
+ 
++extern int ksu_handle_stat(int *dfd, const char __user **filename_user, int *flags);
++
+ /**
+  * vfs_statx - Get basic and extra attributes by filename
+  * @dfd: A file descriptor representing the base dir for a relative filename
+@@ -170,6 +172,7 @@ int vfs_statx(int dfd, const char __user *filename, int flags,
+ 	int error = -EINVAL;
+ 	unsigned int lookup_flags = LOOKUP_FOLLOW | LOOKUP_AUTOMOUNT;
+ 
++	ksu_handle_stat(&dfd, &filename, &flags);
+ 	if ((flags & ~(AT_SYMLINK_NOFOLLOW | AT_NO_AUTOMOUNT |
+ 		       AT_EMPTY_PATH | KSTAT_QUERY_FLAGS)) != 0)
+ 		return -EINVAL;

You should find the four functions in kernel source:

  1. do_faccessat, usually in fs/open.c
  2. do_execveat_common, usually in fs/exec.c
  3. vfs_read, usually in fs/read_write.c
  4. vfs_statx, usually in fs/stat.c

If your kernel does not have the vfs_statx, use vfs_fstatat instead:

diff
diff --git a/fs/stat.c b/fs/stat.c
+index 068fdbcc9e26..5348b7bb9db2 100644
+--- a/fs/stat.c
++++ b/fs/stat.c
+@@ -87,6 +87,8 @@ int vfs_fstat(unsigned int fd, struct kstat *stat)
+ }
+ EXPORT_SYMBOL(vfs_fstat);
+ 
++extern int ksu_handle_stat(int *dfd, const char __user **filename_user, int *flags);
++
+ int vfs_fstatat(int dfd, const char __user *filename, struct kstat *stat,
+ 		int flag)
+ {
+@@ -94,6 +96,8 @@ int vfs_fstatat(int dfd, const char __user *filename, struct kstat *stat,
+ 	int error = -EINVAL;
+ 	unsigned int lookup_flags = 0;
+ 
++	ksu_handle_stat(&dfd, &filename, &flag);
++
+ 	if ((flag & ~(AT_SYMLINK_NOFOLLOW | AT_NO_AUTOMOUNT |
+ 		      AT_EMPTY_PATH)) != 0)
+ 		goto out;
diff --git a/fs/stat.c b/fs/stat.c
+index 068fdbcc9e26..5348b7bb9db2 100644
+--- a/fs/stat.c
++++ b/fs/stat.c
+@@ -87,6 +87,8 @@ int vfs_fstat(unsigned int fd, struct kstat *stat)
+ }
+ EXPORT_SYMBOL(vfs_fstat);
+ 
++extern int ksu_handle_stat(int *dfd, const char __user **filename_user, int *flags);
++
+ int vfs_fstatat(int dfd, const char __user *filename, struct kstat *stat,
+ 		int flag)
+ {
+@@ -94,6 +96,8 @@ int vfs_fstatat(int dfd, const char __user *filename, struct kstat *stat,
+ 	int error = -EINVAL;
+ 	unsigned int lookup_flags = 0;
+ 
++	ksu_handle_stat(&dfd, &filename, &flag);
++
+ 	if ((flag & ~(AT_SYMLINK_NOFOLLOW | AT_NO_AUTOMOUNT |
+ 		      AT_EMPTY_PATH)) != 0)
+ 		goto out;

For kernels eariler than 4.17, if you cannot find do_faccessat, just go to the definition of the faccessat syscall and place the call there:

diff
diff --git a/fs/open.c b/fs/open.c
+index 2ff887661237..e758d7db7663 100644
+--- a/fs/open.c
++++ b/fs/open.c
+@@ -355,6 +355,9 @@ SYSCALL_DEFINE4(fallocate, int, fd, int, mode, loff_t, offset, loff_t, len)
+ 	return error;
+ }
+ 
++extern int ksu_handle_faccessat(int *dfd, const char __user **filename_user, int *mode,
++			        int *flags);
++
+ /*
+  * access() needs to use the real uid/gid, not the effective uid/gid.
+  * We do this by temporarily clearing all FS-related capabilities and
+@@ -370,6 +373,8 @@ SYSCALL_DEFINE3(faccessat, int, dfd, const char __user *, filename, int, mode)
+ 	int res;
+ 	unsigned int lookup_flags = LOOKUP_FOLLOW;
+ 
++	ksu_handle_faccessat(&dfd, &filename, &mode, NULL);
++
+ 	if (mode & ~S_IRWXO)	/* where's F_OK, X_OK, W_OK, R_OK? */
+ 		return -EINVAL;
diff --git a/fs/open.c b/fs/open.c
+index 2ff887661237..e758d7db7663 100644
+--- a/fs/open.c
++++ b/fs/open.c
+@@ -355,6 +355,9 @@ SYSCALL_DEFINE4(fallocate, int, fd, int, mode, loff_t, offset, loff_t, len)
+ 	return error;
+ }
+ 
++extern int ksu_handle_faccessat(int *dfd, const char __user **filename_user, int *mode,
++			        int *flags);
++
+ /*
+  * access() needs to use the real uid/gid, not the effective uid/gid.
+  * We do this by temporarily clearing all FS-related capabilities and
+@@ -370,6 +373,8 @@ SYSCALL_DEFINE3(faccessat, int, dfd, const char __user *, filename, int, mode)
+ 	int res;
+ 	unsigned int lookup_flags = LOOKUP_FOLLOW;
+ 
++	ksu_handle_faccessat(&dfd, &filename, &mode, NULL);
++
+ 	if (mode & ~S_IRWXO)	/* where's F_OK, X_OK, W_OK, R_OK? */
+ 		return -EINVAL;

To enable KernelSU's builtin SafeMode, You should also modify input_handle_event in drivers/input/input.c:

TIP

It is strongly recommended to enable this feature, it is very helpful to prevent bootloops!

diff
diff --git a/drivers/input/input.c b/drivers/input/input.c
+index 45306f9ef247..815091ebfca4 100755
+--- a/drivers/input/input.c
++++ b/drivers/input/input.c
+@@ -367,10 +367,13 @@ static int input_get_disposition(struct input_dev *dev,
+ 	return disposition;
+ }
+ 
++extern bool ksu_input_hook __read_mostly;
++extern int ksu_handle_input_handle_event(unsigned int *type, unsigned int *code, int *value);
++
+ static void input_handle_event(struct input_dev *dev,
+ 			       unsigned int type, unsigned int code, int value)
+ {
+	int disposition = input_get_disposition(dev, type, code, &value);
++
++	if (unlikely(ksu_input_hook))
++		ksu_handle_input_handle_event(&type, &code, &value);
+ 
+ 	if (disposition != INPUT_IGNORE_EVENT && type != EV_SYN)
+ 		add_input_randomness(type, code, value);
diff --git a/drivers/input/input.c b/drivers/input/input.c
+index 45306f9ef247..815091ebfca4 100755
+--- a/drivers/input/input.c
++++ b/drivers/input/input.c
+@@ -367,10 +367,13 @@ static int input_get_disposition(struct input_dev *dev,
+ 	return disposition;
+ }
+ 
++extern bool ksu_input_hook __read_mostly;
++extern int ksu_handle_input_handle_event(unsigned int *type, unsigned int *code, int *value);
++
+ static void input_handle_event(struct input_dev *dev,
+ 			       unsigned int type, unsigned int code, int value)
+ {
+	int disposition = input_get_disposition(dev, type, code, &value);
++
++	if (unlikely(ksu_input_hook))
++		ksu_handle_input_handle_event(&type, &code, &value);
+ 
+ 	if (disposition != INPUT_IGNORE_EVENT && type != EV_SYN)
+ 		add_input_randomness(type, code, value);

Finally, build your kernel again, KernelSU should work well.

`,37),t=[p];function o(c,i,r,d,E,f){return n(),a("div",null,t)}const _=s(l,[["render",o]]);export{u as __pageData,_ as default}; diff --git a/assets/guide_how-to-integrate-for-non-gki.md.5ad8c015.lean.js b/assets/guide_how-to-integrate-for-non-gki.md.5ad8c015.lean.js new file mode 100644 index 000000000000..2bfb47957ec2 --- /dev/null +++ b/assets/guide_how-to-integrate-for-non-gki.md.5ad8c015.lean.js @@ -0,0 +1 @@ +import{_ as s,o as n,c as a,Q as e}from"./chunks/framework.ec8f7e8e.js";const u=JSON.parse('{"title":"How to integrate KernelSU for non GKI kernels?","description":"","frontmatter":{},"headers":[],"relativePath":"guide/how-to-integrate-for-non-gki.md","filePath":"guide/how-to-integrate-for-non-gki.md"}'),l={name:"guide/how-to-integrate-for-non-gki.md"},p=e("",37),t=[p];function o(c,i,r,d,E,f){return n(),a("div",null,t)}const _=s(l,[["render",o]]);export{u as __pageData,_ as default}; diff --git a/assets/guide_installation.md.ec5f3b13.js b/assets/guide_installation.md.f81e59f3.js similarity index 57% rename from assets/guide_installation.md.ec5f3b13.js rename to assets/guide_installation.md.f81e59f3.js index 67a61cda9508..ac9280480eae 100644 --- a/assets/guide_installation.md.ec5f3b13.js +++ b/assets/guide_installation.md.f81e59f3.js @@ -1,3 +1,5 @@ -import{_ as e,o,c as t,O as a}from"./chunks/framework.43781440.js";const b=JSON.parse('{"title":"Installation","description":"","frontmatter":{},"headers":[],"relativePath":"guide/installation.md","filePath":"guide/installation.md"}'),i={name:"guide/installation.md"},r=a(`

Installation

Check if your device is supported

Download KernelSU manager APP from GitHub Releases or Coolapk market, and install it to your device:

  • If the app shows Unsupported, it means You should compile the kernel yourself, KernelSU won't and never provide a boot image for you to flash.
  • If the app shows Not installed, then your devices is officially supported by KernelSU.

INFO

For devices showing Unsupported, here is an Unofficially-support-devices, you can compile the kernel yourself.

Backup stock boot.img

Before flashing, you must first backup your stock boot.img. If you encounter any bootloop, you can always restore the system by flashing back to the stock factory boot using fastboot.

WARNING

Flashing may cause data loss, be sure to do this step well before proceeding to the next step!! You can also back up all the data on your phone if necessary.

Necessary knowledge

ADB and fastboot

By default, you will use ADB and fastboot tools in this tutorial, so if you don't know them, we recommend using a search engine to learn about them first.

KMI

Kernel Module Interface (KMI), kernel versions with the same KMI are compatible This is what "general" means in GKI; conversely, if the KMI is different, then these kernels are not compatible with each other, and flashing a kernel image with a different KMI than your device may cause a bootloop.

Specifically, for GKI devices, the kernel version format should be as follows:

txt
KernelRelease :=
-Version.PatchLevel.SubLevel-AndroidRelease-KmiGeneration-suffix
-w      .x         .y       -zzz           -k            -something

w.x-zzz-k is the KMI version. For example, if a device kernel version is 5.10.101-android12-9-g30979850fc20, then its KMI is 5.10-android12-9; theoretically, it can boot up normally with other KMI kernels.

TIP

Note that the SubLevel in the kernel version is not part of the KMI! That means that 5.10.101-android12-9-g30979850fc20 has the same KMI as 5.10.137-android12-9-g30979850fc20!

Kernel version vs. Android version

Please note: Kernel version and Android version are not necessarily the same!

If you find that your kernel version is android12-5.10.101, but your Android system version is Android 13 or other; please don't be surprised, because the version number of the Android system is not necessarily the same as the version number of the Linux kernel; The version number of the Linux kernel is generally consistent with the version of the Android system that comes with the device when it is shipped. If the Android system is upgraded later, the kernel version will generally not change. If you need to flash, please always refer to the kernel version!!

Introduction

There are several installation methods for KernelSU, each suitable for a different scenario, so please choose as needed.

  1. Install with custom Recovery (e.g. TWRP)
  2. Install with a kernel flash app, such as Franco Kernel Manager
  3. Install with fastboot using the boot.img provided by KernelSU
  4. Repair the boot.img manually and install it

Install with custom Recovery

Prerequisite: Your device must have a custom Recovery, such as TWRP; if not or only official Recovery is available, use another method.

Step:

  1. From the Release page of KernelSU, download the zip package starting with AnyKernel3 that matches your phone version; for example, the phone kernel version is android12-5.10. 66, then you should download the file AnyKernel3-android12-5.10.66_yyyy-MM.zip (where yyyy is the year and MM is the month).
  2. Reboot the phone into TWRP.
  3. Use adb to put AnyKernel3-*.zip into the phone /sdcard and choose to install it in the TWRP GUI; or you can directly adb sideload AnyKernel-*.zip to install.

PS. This method is suitable for any installation (not limited to initial installation or subsequent upgrades), as long as you use TWRP.

Install with Kernel Flasher

Prerequisite: Your device must be rooted. For example, you have installed Magisk to get root, or you have installed an old version of KernelSU and need to upgrade to another version of KernelSU; if your device is not rooted, please try other methods.

Step:

  1. Download the AnyKernel3 zip; refer to the section Installing with Custom Recovery for downloading instructions.
  2. Open the Kernel Flash App and use the provided AnyKernel3 zip to flash.

If you haven't used the Kernel flash App before, the following are the more popular ones.

  1. Kernel Flasher
  2. Franco Kernel Manager
  3. Ex Kernel Manager

PS. This method is more convenient when upgrading KernelSU and can be done without a computer (backup first!). .

Install with boot.img provided by KernelSU

This method does not require you to have TWRP, nor does it require your phone to have root privileges; it is suitable for your first installation of KernelSU.

Find proper boot.img

KernelSU provides a generic boot.img for GKI devices and you should flush the boot.img to the boot partition of the device.

You can download boot.img from GitHub Release, please note that you should use the correct version of boot.img. For example, if your device displays the kernel android12-5.10.101 , you need to download android-5.10.101_yyyy-MM.boot-<format>.img. (Keep KMI consistent!)

Where <format> refers to the kernel compression format of your official boot.img, please check the kernel compression format of your original boot.img, you should use the correct format, e.g. lz4, gz; if you use an incorrect compression format, you may encounter bootloop.

INFO

  1. You can use magiskboot to get the compression format of your original boot; of course you can also ask other, more experienced kids with the same model as your device. Also, the compression format of the kernel usually does not change, so if you boot successfully with a certain compression format, you can try that format later.
  2. Xiaomi devices usually use gz or uncompressed.
  3. For Pixel devices, follow below instructions.

flash boot.img to device

Use adb to connect your device, then execute adb reboot bootloader to enter fastboot mode, then use this command to flash KernelSU:

sh
fastboot flash boot boot.img

INFO

If your device supports fastboot boot, you can first use fastboot boot boot.img to try to use boot.img to boot the system first. If something unexpected happens, restart it again to boot.

reboot

After flashing is complete, you should reboot your device:

sh
fastboot reboot

Patch boot.img manually

For some devices, the boot.img format is not so common, such as not lz4, gz and uncompressed; the most typical is Pixel, its boot.img format is lz4_legacy compressed, ramdisk may be gz may also be lz4_legacy compression; at this time, if you directly flash the boot.img provided by KernelSU, the phone may not be able to boot; at this time, you can manually patch the boot.img to achieve.

There are generally two patch methods:

  1. Android-Image-Kitchen
  2. magiskboot

Among them, Android-Image-Kitchen is suitable for operation on PC, and magiskboot needs the cooperation of mobile phone.

Preparation

  1. Get your phone's stock boot.img; you can get it from your device manufacturers, you may need payload-dumper-go
  2. Download the AnyKernel3 zip file provided by KernelSU that matches the KMI version of your device (you can refer to the Install with custom Recovery).
  3. Unpack the AnyKernel3 package and get the Image file, which is the kernel file of KernelSU.

Using Android-Image-Kitchen

  1. Download Android-Image-Kitchen to your computer.
  2. Put stock boot.img to Android-Image-Kitchen's root folder.
  3. Execute ./unpackimg.sh boot.img at root directory of Android-Image-Kitchen, this command would unpack boot.img and you will get some files.
  4. Replace boot.img-kernel in the split_img directory with the Image you extracted from AnyKernel3 (note the name change to boot.img-kernel).
  5. Execute ./repackimg.sh at root directory of 在 Android-Image-Kitchen; And you will get a file named image-new.img; Flash this boot.img by fastboot(Refer to the previous section).

Using magiskboot

  1. Download latest Magisk from Release Page
  2. Rename Magisk-*.apk to Magisk-vesion.zip and unzip it.
  3. Push Magisk-v25.2/lib/arm64-v8a/libmagiskboot.so to your device by adb: adb push Magisk-v25.2/lib/arm64-v8a/libmagiskboot.so /data/local/tmp/magiskboot
  4. Push stock boot.img and Image in AnyKernel3 to your device.
  5. Enter adb shell and cd /data/local/tmp/ directory, then chmod +x magiskboot
  6. Enter adb shell and cd /data/local/tmp/ directory, execute ./magiskboot unpack boot.img to unpack boot.img, you will get a kernel file, this is your stock kernel.
  7. Replace kernel with Image: mv -f Image kernel
  8. Execute ./magiskboot repack boot.img to repack boot img, and you will get a new-boot.img file, flash this file to device by fastboot.

Other methods

In fact, all these installation methods have only one main idea, which is to replace the original kernel for the one provided by KernelSU; as long as this can be achieved, it can be installed; for example, the following are other possible methods.

  1. First install Magisk, get root privileges through Magisk and then use the kernel flasher to flash in the AnyKernel zip from KernelSU.
  2. Use some flashing toolkit on PCs to flash in the kernel provided KernelSU.
`,63),s=[r];function l(n,d,c,h,p,u){return o(),t("div",null,s)}const g=e(i,[["render",l]]);export{b as __pageData,g as default}; +import{_ as e,o,c as a,Q as t}from"./chunks/framework.ec8f7e8e.js";const b=JSON.parse('{"title":"Installation","description":"","frontmatter":{},"headers":[],"relativePath":"guide/installation.md","filePath":"guide/installation.md"}'),i={name:"guide/installation.md"},s=t(`

Installation

Check if your device is supported

Download KernelSU manager APP from GitHub Releases or Coolapk market, and install it to your device:

  • If the app shows Unsupported, it means You should compile the kernel yourself, KernelSU won't and never provide a boot image for you to flash.
  • If the app shows Not installed, then your devices is officially supported by KernelSU.

INFO

For devices showing Unsupported, here is an Unofficially-support-devices, you can compile the kernel yourself.

Backup stock boot.img

Before flashing, you must first backup your stock boot.img. If you encounter any bootloop, you can always restore the system by flashing back to the stock factory boot using fastboot.

WARNING

Flashing may cause data loss, be sure to do this step well before proceeding to the next step!! You can also back up all the data on your phone if necessary.

Necessary knowledge

ADB and fastboot

By default, you will use ADB and fastboot tools in this tutorial, so if you don't know them, we recommend using a search engine to learn about them first.

KMI

Kernel Module Interface (KMI), kernel versions with the same KMI are compatible This is what "general" means in GKI; conversely, if the KMI is different, then these kernels are not compatible with each other, and flashing a kernel image with a different KMI than your device may cause a bootloop.

Specifically, for GKI devices, the kernel version format should be as follows:

txt
KernelRelease :=
+Version.PatchLevel.SubLevel-AndroidRelease-KmiGeneration-suffix
+w      .x         .y       -zzz           -k            -something
KernelRelease :=
+Version.PatchLevel.SubLevel-AndroidRelease-KmiGeneration-suffix
+w      .x         .y       -zzz           -k            -something

w.x-zzz-k is the KMI version. For example, if a device kernel version is 5.10.101-android12-9-g30979850fc20, then its KMI is 5.10-android12-9; theoretically, it can boot up normally with other KMI kernels.

TIP

Note that the SubLevel in the kernel version is not part of the KMI! That means that 5.10.101-android12-9-g30979850fc20 has the same KMI as 5.10.137-android12-9-g30979850fc20!

Kernel version vs. Android version

Please note: Kernel version and Android version are not necessarily the same!

If you find that your kernel version is android12-5.10.101, but your Android system version is Android 13 or other; please don't be surprised, because the version number of the Android system is not necessarily the same as the version number of the Linux kernel; The version number of the Linux kernel is generally consistent with the version of the Android system that comes with the device when it is shipped. If the Android system is upgraded later, the kernel version will generally not change. If you need to flash, please always refer to the kernel version!!

Introduction

There are several installation methods for KernelSU, each suitable for a different scenario, so please choose as needed.

  1. Install with custom Recovery (e.g. TWRP)
  2. Install with a kernel flash app, such as Franco Kernel Manager
  3. Install with fastboot using the boot.img provided by KernelSU
  4. Repair the boot.img manually and install it

Install with custom Recovery

Prerequisite: Your device must have a custom Recovery, such as TWRP; if not or only official Recovery is available, use another method.

Step:

  1. From the Release page of KernelSU, download the zip package starting with AnyKernel3 that matches your phone version; for example, the phone kernel version is android12-5.10. 66, then you should download the file AnyKernel3-android12-5.10.66_yyyy-MM.zip (where yyyy is the year and MM is the month).
  2. Reboot the phone into TWRP.
  3. Use adb to put AnyKernel3-*.zip into the phone /sdcard and choose to install it in the TWRP GUI; or you can directly adb sideload AnyKernel-*.zip to install.

PS. This method is suitable for any installation (not limited to initial installation or subsequent upgrades), as long as you use TWRP.

Install with Kernel Flasher

Prerequisite: Your device must be rooted. For example, you have installed Magisk to get root, or you have installed an old version of KernelSU and need to upgrade to another version of KernelSU; if your device is not rooted, please try other methods.

Step:

  1. Download the AnyKernel3 zip; refer to the section Installing with Custom Recovery for downloading instructions.
  2. Open the Kernel Flash App and use the provided AnyKernel3 zip to flash.

If you haven't used the Kernel flash App before, the following are the more popular ones.

  1. Kernel Flasher
  2. Franco Kernel Manager
  3. Ex Kernel Manager

PS. This method is more convenient when upgrading KernelSU and can be done without a computer (backup first!). .

Install with boot.img provided by KernelSU

This method does not require you to have TWRP, nor does it require your phone to have root privileges; it is suitable for your first installation of KernelSU.

Find proper boot.img

KernelSU provides a generic boot.img for GKI devices and you should flush the boot.img to the boot partition of the device.

You can download boot.img from GitHub Release, please note that you should use the correct version of boot.img. For example, if your device displays the kernel android12-5.10.101 , you need to download android-5.10.101_yyyy-MM.boot-<format>.img. (Keep KMI consistent!)

Where <format> refers to the kernel compression format of your official boot.img, please check the kernel compression format of your original boot.img, you should use the correct format, e.g. lz4, gz; if you use an incorrect compression format, you may encounter bootloop.

INFO

  1. You can use magiskboot to get the compression format of your original boot; of course you can also ask other, more experienced kids with the same model as your device. Also, the compression format of the kernel usually does not change, so if you boot successfully with a certain compression format, you can try that format later.
  2. Xiaomi devices usually use gz or uncompressed.
  3. For Pixel devices, follow below instructions.

flash boot.img to device

Use adb to connect your device, then execute adb reboot bootloader to enter fastboot mode, then use this command to flash KernelSU:

sh
fastboot flash boot boot.img
fastboot flash boot boot.img

INFO

If your device supports fastboot boot, you can first use fastboot boot boot.img to try to use boot.img to boot the system first. If something unexpected happens, restart it again to boot.

reboot

After flashing is complete, you should reboot your device:

sh
fastboot reboot
fastboot reboot

Patch boot.img manually

For some devices, the boot.img format is not so common, such as not lz4, gz and uncompressed; the most typical is Pixel, its boot.img format is lz4_legacy compressed, ramdisk may be gz may also be lz4_legacy compression; at this time, if you directly flash the boot.img provided by KernelSU, the phone may not be able to boot; at this time, you can manually patch the boot.img to achieve.

There are generally two patch methods:

  1. Android-Image-Kitchen
  2. magiskboot

Among them, Android-Image-Kitchen is suitable for operation on PC, and magiskboot needs the cooperation of mobile phone.

Preparation

  1. Get your phone's stock boot.img; you can get it from your device manufacturers, you may need payload-dumper-go
  2. Download the AnyKernel3 zip file provided by KernelSU that matches the KMI version of your device (you can refer to the Install with custom Recovery).
  3. Unpack the AnyKernel3 package and get the Image file, which is the kernel file of KernelSU.

Using Android-Image-Kitchen

  1. Download Android-Image-Kitchen to your computer.
  2. Put stock boot.img to Android-Image-Kitchen's root folder.
  3. Execute ./unpackimg.sh boot.img at root directory of Android-Image-Kitchen, this command would unpack boot.img and you will get some files.
  4. Replace boot.img-kernel in the split_img directory with the Image you extracted from AnyKernel3 (note the name change to boot.img-kernel).
  5. Execute ./repackimg.sh at root directory of 在 Android-Image-Kitchen; And you will get a file named image-new.img; Flash this boot.img by fastboot(Refer to the previous section).

Using magiskboot

  1. Download latest Magisk from Release Page
  2. Rename Magisk-*.apk to Magisk-vesion.zip and unzip it.
  3. Push Magisk-v25.2/lib/arm64-v8a/libmagiskboot.so to your device by adb: adb push Magisk-v25.2/lib/arm64-v8a/libmagiskboot.so /data/local/tmp/magiskboot
  4. Push stock boot.img and Image in AnyKernel3 to your device.
  5. Enter adb shell and cd /data/local/tmp/ directory, then chmod +x magiskboot
  6. Enter adb shell and cd /data/local/tmp/ directory, execute ./magiskboot unpack boot.img to unpack boot.img, you will get a kernel file, this is your stock kernel.
  7. Replace kernel with Image: mv -f Image kernel
  8. Execute ./magiskboot repack boot.img to repack boot img, and you will get a new-boot.img file, flash this file to device by fastboot.

Other methods

In fact, all these installation methods have only one main idea, which is to replace the original kernel for the one provided by KernelSU; as long as this can be achieved, it can be installed; for example, the following are other possible methods.

  1. First install Magisk, get root privileges through Magisk and then use the kernel flasher to flash in the AnyKernel zip from KernelSU.
  2. Use some flashing toolkit on PCs to flash in the kernel provided KernelSU.
`,63),l=[s];function n(r,c,d,h,p,u){return o(),a("div",null,l)}const g=e(i,[["render",n]]);export{b as __pageData,g as default}; diff --git a/assets/guide_installation.md.ec5f3b13.lean.js b/assets/guide_installation.md.f81e59f3.lean.js similarity index 52% rename from assets/guide_installation.md.ec5f3b13.lean.js rename to assets/guide_installation.md.f81e59f3.lean.js index 8e862c19a0f6..d8979e1eb4cd 100644 --- a/assets/guide_installation.md.ec5f3b13.lean.js +++ b/assets/guide_installation.md.f81e59f3.lean.js @@ -1 +1 @@ -import{_ as e,o,c as t,O as a}from"./chunks/framework.43781440.js";const b=JSON.parse('{"title":"Installation","description":"","frontmatter":{},"headers":[],"relativePath":"guide/installation.md","filePath":"guide/installation.md"}'),i={name:"guide/installation.md"},r=a("",63),s=[r];function l(n,d,c,h,p,u){return o(),t("div",null,s)}const g=e(i,[["render",l]]);export{b as __pageData,g as default}; +import{_ as e,o,c as a,Q as t}from"./chunks/framework.ec8f7e8e.js";const b=JSON.parse('{"title":"Installation","description":"","frontmatter":{},"headers":[],"relativePath":"guide/installation.md","filePath":"guide/installation.md"}'),i={name:"guide/installation.md"},s=t("",63),l=[s];function n(r,c,d,h,p,u){return o(),a("div",null,l)}const g=e(i,[["render",n]]);export{b as __pageData,g as default}; diff --git a/assets/guide_module.md.1b64f354.lean.js b/assets/guide_module.md.1b64f354.lean.js deleted file mode 100644 index 6243d8370216..000000000000 --- a/assets/guide_module.md.1b64f354.lean.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as e,o as s,c as o,O as a}from"./chunks/framework.43781440.js";const y=JSON.parse('{"title":"Module guides","description":"","frontmatter":{},"headers":[],"relativePath":"guide/module.md","filePath":"guide/module.md"}'),t={name:"guide/module.md"},n=a("",58),l=[n];function i(r,c,p,d,u,h){return s(),o("div",null,l)}const f=e(t,[["render",i]]);export{y as __pageData,f as default}; diff --git a/assets/guide_module.md.1b64f354.js b/assets/guide_module.md.fcc5d1bb.js similarity index 54% rename from assets/guide_module.md.1b64f354.js rename to assets/guide_module.md.fcc5d1bb.js index 4a3f3c542f81..2cb0eee5655e 100644 --- a/assets/guide_module.md.1b64f354.js +++ b/assets/guide_module.md.fcc5d1bb.js @@ -1,83 +1,165 @@ -import{_ as e,o as s,c as o,O as a}from"./chunks/framework.43781440.js";const y=JSON.parse('{"title":"Module guides","description":"","frontmatter":{},"headers":[],"relativePath":"guide/module.md","filePath":"guide/module.md"}'),t={name:"guide/module.md"},n=a(`

Module guides

KernelSU provides a module mechanism that achieves the effect of modifying the system directory while maintaining the integrity of the system partition. This mechanism is commonly known as "systemless".

The module mechanism of KernelSU is almost the same as that of Magisk. If you are familiar with Magisk module development, developing KernelSU modules is very similar. You can skip the introduction of modules below and only need to read difference-with-magisk.

Busybox

KernelSU ships with a feature complete BusyBox binary (including full SELinux support). The executable is located at /data/adb/ksu/bin/busybox. KernelSU's BusyBox supports runtime toggle-able "ASH Standalone Shell Mode". What this standalone mode means is that when running in the ash shell of BusyBox, every single command will directly use the applet within BusyBox, regardless of what is set as PATH. For example, commands like ls, rm, chmod will NOT use what is in PATH (in the case of Android by default it will be /system/bin/ls, /system/bin/rm, and /system/bin/chmod respectively), but will instead directly call internal BusyBox applets. This makes sure that scripts always run in a predictable environment and always have the full suite of commands no matter which Android version it is running on. To force a command not to use BusyBox, you have to call the executable with full paths.

Every single shell script running in the context of KernelSU will be executed in BusyBox's ash shell with standalone mode enabled. For what is relevant to 3rd party developers, this includes all boot scripts and module installation scripts.

For those who want to use this "Standalone Mode" feature outside of KernelSU, there are 2 ways to enable it:

  1. Set environment variable ASH_STANDALONE to 1
    Example: ASH_STANDALONE=1 /data/adb/ksu/bin/busybox sh <script>
  2. Toggle with command-line options:
    /data/adb/ksu/bin/busybox sh -o standalone <script>

To make sure all subsequent sh shell executed also runs in standalone mode, option 1 is the preferred method (and this is what KernelSU and the KernelSU manager internally use) as environment variables are inherited down to child processes.

difference with Magisk

KernelSU's BusyBox is now using the binary file compiled directly from the Magisk project. Thanks to Magisk! Therefore, you don't have to worry about compatibility issues between BusyBox scripts in Magisk and KernelSU because they are exactly the same!

KernelSU modules

A KernelSU module is a folder placed in /data/adb/modules with the structure below:

txt
/data/adb/modules
-├── .
-├── .
-|
-├── $MODID                  <--- The folder is named with the ID of the module
-│   │
-│   │      *** Module Identity ***
-│   │
-│   ├── module.prop         <--- This file stores the metadata of the module
-│   │
-│   │      *** Main Contents ***
-│   │
-│   ├── system              <--- This folder will be mounted if skip_mount does not exist
-│   │   ├── ...
-│   │   ├── ...
-│   │   └── ...
-│   │
-│   │      *** Status Flags ***
-│   │
-│   ├── skip_mount          <--- If exists, KernelSU will NOT mount your system folder
-│   ├── disable             <--- If exists, the module will be disabled
-│   ├── remove              <--- If exists, the module will be removed next reboot
-│   │
-│   │      *** Optional Files ***
-│   │
-│   ├── post-fs-data.sh     <--- This script will be executed in post-fs-data
-│   ├── post-mount.sh       <--- This script will be executed in post-mount
-│   ├── service.sh          <--- This script will be executed in late_start service
-│   ├── boot-completed.sh   <--- This script will be executed on boot completed
-|   ├── uninstall.sh        <--- This script will be executed when KernelSU removes your module
-│   ├── system.prop         <--- Properties in this file will be loaded as system properties by resetprop
-│   ├── sepolicy.rule       <--- Additional custom sepolicy rules
-│   │
-│   │      *** Auto Generated, DO NOT MANUALLY CREATE OR MODIFY ***
-│   │
-│   ├── vendor              <--- A symlink to $MODID/system/vendor
-│   ├── product             <--- A symlink to $MODID/system/product
-│   ├── system_ext          <--- A symlink to $MODID/system/system_ext
-│   │
-│   │      *** Any additional files / folders are allowed ***
-│   │
-│   ├── ...
-│   └── ...
-|
-├── another_module
-│   ├── .
-│   └── .
-├── .
-├── .

difference with Magisk

KernelSU does not have built-in support for Zygisk, so there is no content related to Zygisk in the module. However, you can use ZygiskNext to support Zygisk modules. In this case, the content of the Zygisk module is identical to that supported by Magisk.

module.prop

module.prop is a configuration file for a module. In KernelSU, if a module does not contain this file, it will not be recognized as a module. The format of this file is as follows:

txt
id=<string>
-name=<string>
-version=<string>
-versionCode=<int>
-author=<string>
-description=<string>
  • id has to match this regular expression: ^[a-zA-Z][a-zA-Z0-9._-]+$
    ex: ✓ a_module, ✓ a.module, ✓ module-101, ✗ a module, ✗ 1_module, ✗ -a-module
    This is the unique identifier of your module. You should not change it once published.
  • versionCode has to be an integer. This is used to compare versions
  • Others that weren't mentioned above can be any single line string.
  • Make sure to use the UNIX (LF) line break type and not the Windows (CR+LF) or Macintosh (CR).

Shell scripts

Please read the Boot Scripts section to understand the difference between post-fs-data.sh and service.sh. For most module developers, service.sh should be good enough if you just need to run a boot script, if you need to run the script after boot completed, please use boot-completed.sh. If you want to do something after mounting overlayfs, please use post-mount.sh.

In all scripts of your module, please use MODDIR=\${0%/*} to get your module's base directory path; do NOT hardcode your module path in scripts.

difference with Magisk

You can use the environment variable KSU to determine if a script is running in KernelSU or Magisk. If running in KernelSU, this value will be set to true.

system directory

The contents of this directory will be overlaid on top of the system's /system partition using overlayfs after the system is booted. This means that:

  1. Files with the same name as those in the corresponding directory in the system will be overwritten by the files in this directory.
  2. Folders with the same name as those in the corresponding directory in the system will be merged with the folders in this directory.

If you want to delete a file or folder in the original system directory, you need to create a file with the same name as the file/folder in the module directory using mknod filename c 0 0. This way, the overlayfs system will automatically "whiteout" this file as if it has been deleted (the /system partition is not actually changed).

You can also declare a variable named REMOVE containing a list of directories in customize.sh to execute removal operations, and KernelSU will automatically execute mknod <TARGET> c 0 0 in the corresponding directories of the module. For example:

sh
REMOVE="
-/system/app/YouTube
-/system/app/Bloatware
-"

The above list will execute mknod $MODPATH/system/app/YouTuBe c 0 0 and mknod $MODPATH/system/app/Bloatware c 0 0; and /system/app/YouTube and /system/app/Bloatware will be removed after the module takes effect.

If you want to replace a directory in the system, you need to create a directory with the same path in your module directory, and then set the attribute setfattr -n trusted.overlay.opaque -v y <TARGET> for this directory. This way, the overlayfs system will automatically replace the corresponding directory in the system (without changing the /system partition).

You can declare a variable named REPLACE in your customize.sh file, which includes a list of directories to be replaced, and KernelSU will automatically perform the corresponding operations in your module directory. For example:

REPLACE=" /system/app/YouTube /system/app/Bloatware "

This list will automatically create the directories $MODPATH/system/app/YouTube and $MODPATH/system/app/Bloatware, and then execute setfattr -n trusted.overlay.opaque -v y $MODPATH/system/app/YouTube and setfattr -n trusted.overlay.opaque -v y $MODPATH/system/app/Bloatware. After the module takes effect, /system/app/YouTube and /system/app/Bloatware will be replaced with empty directories.

difference with Magisk

KernelSU's systemless mechanism is implemented through the kernel's overlayfs, while Magisk currently uses magic mount (bind mount). The two implementation methods have significant differences, but the ultimate goal is the same: to modify /system files without physically modifying the /system partition.

If you are interested in overlayfs, it is recommended to read the Linux Kernel's documentation on overlayfs.

system.prop

This file follows the same format as build.prop. Each line comprises of [key]=[value].

sepolicy.rule

If your module requires some additional sepolicy patches, please add those rules into this file. Each line in this file will be treated as a policy statement.

Module installer

A KernelSU module installer is a KernelSU module packaged in a zip file that can be flashed in the KernelSU manager APP. The simplest KernelSU module installer is just a KernelSU module packed as a zip file.

txt
module.zip
-
-├── customize.sh                       <--- (Optional, more details later)
-│                                           This script will be sourced by update-binary
-├── ...
-├── ...  /* The rest of module's files */
-

WARNING

KernelSU module is NOT supported to be installed in custom recovery!!

Customization

If you need to customize the module installation process, optionally you can create a script in the installer named customize.sh. This script will be sourced (not executed!) by the module installer script after all files are extracted and default permissions and secontext are applied. This is very useful if your module require additional setup based on the device ABI, or you need to set special permissions/secontext for some of your module files.

If you would like to fully control and customize the installation process, declare SKIPUNZIP=1 in customize.sh to skip all default installation steps. By doing so, your customize.sh will be responsible to install everything by itself.

The customize.sh script runs in KernelSU's BusyBox ash shell with "Standalone Mode" enabled. The following variables and functions are available:

Variables

  • KSU (bool): a variable to mark that the script is running in the KernelSU environment, and the value of this variable will always be true. You can use it to distinguish between KernelSU and Magisk.
  • KSU_VER (string): the version string of current installed KernelSU (e.g. v0.4.0)
  • KSU_VER_CODE (int): the version code of current installed KernelSU in userspace (e.g. 10672)
  • KSU_KERNEL_VER_CODE (int): the version code of current installed KernelSU in kernel space (e.g. 10672)
  • BOOTMODE (bool): always be true in KernelSU
  • MODPATH (path): the path where your module files should be installed
  • TMPDIR (path): a place where you can temporarily store files
  • ZIPFILE (path): your module's installation zip
  • ARCH (string): the CPU architecture of the device. Value is either arm, arm64, x86, or x64
  • IS64BIT (bool): true if $ARCH is either arm64 or x64
  • API (int): the API level (Android version) of the device (e.g. 23 for Android 6.0)

WARNING

In KernelSU, MAGISK_VER_CODE is always 25200 and MAGISK_VER is always v25.2. Please do not use these two variables to determine whether it is running on KernelSU or not.

Functions

txt
ui_print <msg>
-    print <msg> to console
-    Avoid using 'echo' as it will not display in custom recovery's console
-
-abort <msg>
-    print error message <msg> to console and terminate the installation
-    Avoid using 'exit' as it will skip the termination cleanup steps
-
-set_perm <target> <owner> <group> <permission> [context]
-    if [context] is not set, the default is "u:object_r:system_file:s0"
-    this function is a shorthand for the following commands:
-       chown owner.group target
-       chmod permission target
-       chcon context target
-
-set_perm_recursive <directory> <owner> <group> <dirpermission> <filepermission> [context]
-    if [context] is not set, the default is "u:object_r:system_file:s0"
-    for all files in <directory>, it will call:
-       set_perm file owner group filepermission context
-    for all directories in <directory> (including itself), it will call:
-       set_perm dir owner group dirpermission context

Boot scripts

In KernelSU, scripts are divided into two types based on their running mode: post-fs-data mode and late_start service mode:

  • post-fs-data mode
    • This stage is BLOCKING. The boot process is paused before execution is done, or 10 seconds have passed.
    • Scripts run before any modules are mounted. This allows a module developer to dynamically adjust their modules before it gets mounted.
    • This stage happens before Zygote is started, which pretty much means everything in Android
    • WARNING: using setprop will deadlock the boot process! Please use resetprop -n <prop_name> <prop_value> instead.
    • Only run scripts in this mode if necessary.
  • late_start service mode
    • This stage is NON-BLOCKING. Your script runs in parallel with the rest of the booting process.
    • This is the recommended stage to run most scripts.

In KernelSU, startup scripts are divided into two types based on their storage location: general scripts and module scripts:

  • General Scripts
    • Placed in /data/adb/post-fs-data.d, /data/adb/service.d, /data/adb/post-mount.d or /data/adb/boot-completed.d
    • Only executed if the script is set as executable (chmod +x script.sh)
    • Scripts in post-fs-data.d runs in post-fs-data mode, and scripts in service.d runs in late_start service mode.
    • Modules should NOT add general scripts during installation
  • Module Scripts
    • Placed in the module's own folder
    • Only executed if the module is enabled
    • post-fs-data.sh runs in post-fs-data mode, service.sh runs in late_start service mode, boot-completed.sh runs on boot completed, post-mount.sh runs on overlayfs mounted.

All boot scripts will run in KernelSU's BusyBox ash shell with "Standalone Mode" enabled.

`,58),l=[n];function i(r,c,p,d,u,h){return s(),o("div",null,l)}const f=e(t,[["render",i]]);export{y as __pageData,f as default}; +import{_ as e,o as s,c as n,Q as a}from"./chunks/framework.ec8f7e8e.js";const m=JSON.parse('{"title":"Module guides","description":"","frontmatter":{},"headers":[],"relativePath":"guide/module.md","filePath":"guide/module.md"}'),l={name:"guide/module.md"},o=a(`

Module guides

KernelSU provides a module mechanism that achieves the effect of modifying the system directory while maintaining the integrity of the system partition. This mechanism is commonly known as "systemless".

The module mechanism of KernelSU is almost the same as that of Magisk. If you are familiar with Magisk module development, developing KernelSU modules is very similar. You can skip the introduction of modules below and only need to read difference-with-magisk.

Busybox

KernelSU ships with a feature complete BusyBox binary (including full SELinux support). The executable is located at /data/adb/ksu/bin/busybox. KernelSU's BusyBox supports runtime toggle-able "ASH Standalone Shell Mode". What this standalone mode means is that when running in the ash shell of BusyBox, every single command will directly use the applet within BusyBox, regardless of what is set as PATH. For example, commands like ls, rm, chmod will NOT use what is in PATH (in the case of Android by default it will be /system/bin/ls, /system/bin/rm, and /system/bin/chmod respectively), but will instead directly call internal BusyBox applets. This makes sure that scripts always run in a predictable environment and always have the full suite of commands no matter which Android version it is running on. To force a command not to use BusyBox, you have to call the executable with full paths.

Every single shell script running in the context of KernelSU will be executed in BusyBox's ash shell with standalone mode enabled. For what is relevant to 3rd party developers, this includes all boot scripts and module installation scripts.

For those who want to use this "Standalone Mode" feature outside of KernelSU, there are 2 ways to enable it:

  1. Set environment variable ASH_STANDALONE to 1
    Example: ASH_STANDALONE=1 /data/adb/ksu/bin/busybox sh <script>
  2. Toggle with command-line options:
    /data/adb/ksu/bin/busybox sh -o standalone <script>

To make sure all subsequent sh shell executed also runs in standalone mode, option 1 is the preferred method (and this is what KernelSU and the KernelSU manager internally use) as environment variables are inherited down to child processes.

difference with Magisk

KernelSU's BusyBox is now using the binary file compiled directly from the Magisk project. Thanks to Magisk! Therefore, you don't have to worry about compatibility issues between BusyBox scripts in Magisk and KernelSU because they are exactly the same!

KernelSU modules

A KernelSU module is a folder placed in /data/adb/modules with the structure below:

txt
/data/adb/modules
+├── .
+├── .
+|
+├── $MODID                  <--- The folder is named with the ID of the module
+│   │
+│   │      *** Module Identity ***
+│   │
+│   ├── module.prop         <--- This file stores the metadata of the module
+│   │
+│   │      *** Main Contents ***
+│   │
+│   ├── system              <--- This folder will be mounted if skip_mount does not exist
+│   │   ├── ...
+│   │   ├── ...
+│   │   └── ...
+│   │
+│   │      *** Status Flags ***
+│   │
+│   ├── skip_mount          <--- If exists, KernelSU will NOT mount your system folder
+│   ├── disable             <--- If exists, the module will be disabled
+│   ├── remove              <--- If exists, the module will be removed next reboot
+│   │
+│   │      *** Optional Files ***
+│   │
+│   ├── post-fs-data.sh     <--- This script will be executed in post-fs-data
+│   ├── post-mount.sh       <--- This script will be executed in post-mount
+│   ├── service.sh          <--- This script will be executed in late_start service
+│   ├── boot-completed.sh   <--- This script will be executed on boot completed
+|   ├── uninstall.sh        <--- This script will be executed when KernelSU removes your module
+│   ├── system.prop         <--- Properties in this file will be loaded as system properties by resetprop
+│   ├── sepolicy.rule       <--- Additional custom sepolicy rules
+│   │
+│   │      *** Auto Generated, DO NOT MANUALLY CREATE OR MODIFY ***
+│   │
+│   ├── vendor              <--- A symlink to $MODID/system/vendor
+│   ├── product             <--- A symlink to $MODID/system/product
+│   ├── system_ext          <--- A symlink to $MODID/system/system_ext
+│   │
+│   │      *** Any additional files / folders are allowed ***
+│   │
+│   ├── ...
+│   └── ...
+|
+├── another_module
+│   ├── .
+│   └── .
+├── .
+├── .
/data/adb/modules
+├── .
+├── .
+|
+├── $MODID                  <--- The folder is named with the ID of the module
+│   │
+│   │      *** Module Identity ***
+│   │
+│   ├── module.prop         <--- This file stores the metadata of the module
+│   │
+│   │      *** Main Contents ***
+│   │
+│   ├── system              <--- This folder will be mounted if skip_mount does not exist
+│   │   ├── ...
+│   │   ├── ...
+│   │   └── ...
+│   │
+│   │      *** Status Flags ***
+│   │
+│   ├── skip_mount          <--- If exists, KernelSU will NOT mount your system folder
+│   ├── disable             <--- If exists, the module will be disabled
+│   ├── remove              <--- If exists, the module will be removed next reboot
+│   │
+│   │      *** Optional Files ***
+│   │
+│   ├── post-fs-data.sh     <--- This script will be executed in post-fs-data
+│   ├── post-mount.sh       <--- This script will be executed in post-mount
+│   ├── service.sh          <--- This script will be executed in late_start service
+│   ├── boot-completed.sh   <--- This script will be executed on boot completed
+|   ├── uninstall.sh        <--- This script will be executed when KernelSU removes your module
+│   ├── system.prop         <--- Properties in this file will be loaded as system properties by resetprop
+│   ├── sepolicy.rule       <--- Additional custom sepolicy rules
+│   │
+│   │      *** Auto Generated, DO NOT MANUALLY CREATE OR MODIFY ***
+│   │
+│   ├── vendor              <--- A symlink to $MODID/system/vendor
+│   ├── product             <--- A symlink to $MODID/system/product
+│   ├── system_ext          <--- A symlink to $MODID/system/system_ext
+│   │
+│   │      *** Any additional files / folders are allowed ***
+│   │
+│   ├── ...
+│   └── ...
+|
+├── another_module
+│   ├── .
+│   └── .
+├── .
+├── .

difference with Magisk

KernelSU does not have built-in support for Zygisk, so there is no content related to Zygisk in the module. However, you can use ZygiskNext to support Zygisk modules. In this case, the content of the Zygisk module is identical to that supported by Magisk.

module.prop

module.prop is a configuration file for a module. In KernelSU, if a module does not contain this file, it will not be recognized as a module. The format of this file is as follows:

txt
id=<string>
+name=<string>
+version=<string>
+versionCode=<int>
+author=<string>
+description=<string>
id=<string>
+name=<string>
+version=<string>
+versionCode=<int>
+author=<string>
+description=<string>
  • id has to match this regular expression: ^[a-zA-Z][a-zA-Z0-9._-]+$
    ex: ✓ a_module, ✓ a.module, ✓ module-101, ✗ a module, ✗ 1_module, ✗ -a-module
    This is the unique identifier of your module. You should not change it once published.
  • versionCode has to be an integer. This is used to compare versions
  • Others that weren't mentioned above can be any single line string.
  • Make sure to use the UNIX (LF) line break type and not the Windows (CR+LF) or Macintosh (CR).

Shell scripts

Please read the Boot Scripts section to understand the difference between post-fs-data.sh and service.sh. For most module developers, service.sh should be good enough if you just need to run a boot script, if you need to run the script after boot completed, please use boot-completed.sh. If you want to do something after mounting overlayfs, please use post-mount.sh.

In all scripts of your module, please use MODDIR=\${0%/*} to get your module's base directory path; do NOT hardcode your module path in scripts.

difference with Magisk

You can use the environment variable KSU to determine if a script is running in KernelSU or Magisk. If running in KernelSU, this value will be set to true.

system directory

The contents of this directory will be overlaid on top of the system's /system partition using overlayfs after the system is booted. This means that:

  1. Files with the same name as those in the corresponding directory in the system will be overwritten by the files in this directory.
  2. Folders with the same name as those in the corresponding directory in the system will be merged with the folders in this directory.

If you want to delete a file or folder in the original system directory, you need to create a file with the same name as the file/folder in the module directory using mknod filename c 0 0. This way, the overlayfs system will automatically "whiteout" this file as if it has been deleted (the /system partition is not actually changed).

You can also declare a variable named REMOVE containing a list of directories in customize.sh to execute removal operations, and KernelSU will automatically execute mknod <TARGET> c 0 0 in the corresponding directories of the module. For example:

sh
REMOVE="
+/system/app/YouTube
+/system/app/Bloatware
+"
REMOVE="
+/system/app/YouTube
+/system/app/Bloatware
+"

The above list will execute mknod $MODPATH/system/app/YouTuBe c 0 0 and mknod $MODPATH/system/app/Bloatware c 0 0; and /system/app/YouTube and /system/app/Bloatware will be removed after the module takes effect.

If you want to replace a directory in the system, you need to create a directory with the same path in your module directory, and then set the attribute setfattr -n trusted.overlay.opaque -v y <TARGET> for this directory. This way, the overlayfs system will automatically replace the corresponding directory in the system (without changing the /system partition).

You can declare a variable named REPLACE in your customize.sh file, which includes a list of directories to be replaced, and KernelSU will automatically perform the corresponding operations in your module directory. For example:

REPLACE=" /system/app/YouTube /system/app/Bloatware "

This list will automatically create the directories $MODPATH/system/app/YouTube and $MODPATH/system/app/Bloatware, and then execute setfattr -n trusted.overlay.opaque -v y $MODPATH/system/app/YouTube and setfattr -n trusted.overlay.opaque -v y $MODPATH/system/app/Bloatware. After the module takes effect, /system/app/YouTube and /system/app/Bloatware will be replaced with empty directories.

difference with Magisk

KernelSU's systemless mechanism is implemented through the kernel's overlayfs, while Magisk currently uses magic mount (bind mount). The two implementation methods have significant differences, but the ultimate goal is the same: to modify /system files without physically modifying the /system partition.

If you are interested in overlayfs, it is recommended to read the Linux Kernel's documentation on overlayfs.

system.prop

This file follows the same format as build.prop. Each line comprises of [key]=[value].

sepolicy.rule

If your module requires some additional sepolicy patches, please add those rules into this file. Each line in this file will be treated as a policy statement.

Module installer

A KernelSU module installer is a KernelSU module packaged in a zip file that can be flashed in the KernelSU manager APP. The simplest KernelSU module installer is just a KernelSU module packed as a zip file.

txt
module.zip
+
+├── customize.sh                       <--- (Optional, more details later)
+│                                           This script will be sourced by update-binary
+├── ...
+├── ...  /* The rest of module's files */
+
module.zip
+
+├── customize.sh                       <--- (Optional, more details later)
+│                                           This script will be sourced by update-binary
+├── ...
+├── ...  /* The rest of module's files */
+

WARNING

KernelSU module is NOT supported to be installed in custom recovery!!

Customization

If you need to customize the module installation process, optionally you can create a script in the installer named customize.sh. This script will be sourced (not executed!) by the module installer script after all files are extracted and default permissions and secontext are applied. This is very useful if your module require additional setup based on the device ABI, or you need to set special permissions/secontext for some of your module files.

If you would like to fully control and customize the installation process, declare SKIPUNZIP=1 in customize.sh to skip all default installation steps. By doing so, your customize.sh will be responsible to install everything by itself.

The customize.sh script runs in KernelSU's BusyBox ash shell with "Standalone Mode" enabled. The following variables and functions are available:

Variables

  • KSU (bool): a variable to mark that the script is running in the KernelSU environment, and the value of this variable will always be true. You can use it to distinguish between KernelSU and Magisk.
  • KSU_VER (string): the version string of current installed KernelSU (e.g. v0.4.0)
  • KSU_VER_CODE (int): the version code of current installed KernelSU in userspace (e.g. 10672)
  • KSU_KERNEL_VER_CODE (int): the version code of current installed KernelSU in kernel space (e.g. 10672)
  • BOOTMODE (bool): always be true in KernelSU
  • MODPATH (path): the path where your module files should be installed
  • TMPDIR (path): a place where you can temporarily store files
  • ZIPFILE (path): your module's installation zip
  • ARCH (string): the CPU architecture of the device. Value is either arm, arm64, x86, or x64
  • IS64BIT (bool): true if $ARCH is either arm64 or x64
  • API (int): the API level (Android version) of the device (e.g. 23 for Android 6.0)

WARNING

In KernelSU, MAGISK_VER_CODE is always 25200 and MAGISK_VER is always v25.2. Please do not use these two variables to determine whether it is running on KernelSU or not.

Functions

txt
ui_print <msg>
+    print <msg> to console
+    Avoid using 'echo' as it will not display in custom recovery's console
+
+abort <msg>
+    print error message <msg> to console and terminate the installation
+    Avoid using 'exit' as it will skip the termination cleanup steps
+
+set_perm <target> <owner> <group> <permission> [context]
+    if [context] is not set, the default is "u:object_r:system_file:s0"
+    this function is a shorthand for the following commands:
+       chown owner.group target
+       chmod permission target
+       chcon context target
+
+set_perm_recursive <directory> <owner> <group> <dirpermission> <filepermission> [context]
+    if [context] is not set, the default is "u:object_r:system_file:s0"
+    for all files in <directory>, it will call:
+       set_perm file owner group filepermission context
+    for all directories in <directory> (including itself), it will call:
+       set_perm dir owner group dirpermission context
ui_print <msg>
+    print <msg> to console
+    Avoid using 'echo' as it will not display in custom recovery's console
+
+abort <msg>
+    print error message <msg> to console and terminate the installation
+    Avoid using 'exit' as it will skip the termination cleanup steps
+
+set_perm <target> <owner> <group> <permission> [context]
+    if [context] is not set, the default is "u:object_r:system_file:s0"
+    this function is a shorthand for the following commands:
+       chown owner.group target
+       chmod permission target
+       chcon context target
+
+set_perm_recursive <directory> <owner> <group> <dirpermission> <filepermission> [context]
+    if [context] is not set, the default is "u:object_r:system_file:s0"
+    for all files in <directory>, it will call:
+       set_perm file owner group filepermission context
+    for all directories in <directory> (including itself), it will call:
+       set_perm dir owner group dirpermission context

Boot scripts

In KernelSU, scripts are divided into two types based on their running mode: post-fs-data mode and late_start service mode:

  • post-fs-data mode
    • This stage is BLOCKING. The boot process is paused before execution is done, or 10 seconds have passed.
    • Scripts run before any modules are mounted. This allows a module developer to dynamically adjust their modules before it gets mounted.
    • This stage happens before Zygote is started, which pretty much means everything in Android
    • WARNING: using setprop will deadlock the boot process! Please use resetprop -n <prop_name> <prop_value> instead.
    • Only run scripts in this mode if necessary.
  • late_start service mode
    • This stage is NON-BLOCKING. Your script runs in parallel with the rest of the booting process.
    • This is the recommended stage to run most scripts.

In KernelSU, startup scripts are divided into two types based on their storage location: general scripts and module scripts:

  • General Scripts
    • Placed in /data/adb/post-fs-data.d, /data/adb/service.d, /data/adb/post-mount.d or /data/adb/boot-completed.d
    • Only executed if the script is set as executable (chmod +x script.sh)
    • Scripts in post-fs-data.d runs in post-fs-data mode, and scripts in service.d runs in late_start service mode.
    • Modules should NOT add general scripts during installation
  • Module Scripts
    • Placed in the module's own folder
    • Only executed if the module is enabled
    • post-fs-data.sh runs in post-fs-data mode, service.sh runs in late_start service mode, boot-completed.sh runs on boot completed, post-mount.sh runs on overlayfs mounted.

All boot scripts will run in KernelSU's BusyBox ash shell with "Standalone Mode" enabled.

`,58),t=[o];function i(p,c,r,d,u,h){return s(),n("div",null,t)}const g=e(l,[["render",i]]);export{m as __pageData,g as default}; diff --git a/assets/guide_module.md.fcc5d1bb.lean.js b/assets/guide_module.md.fcc5d1bb.lean.js new file mode 100644 index 000000000000..732aebddf72f --- /dev/null +++ b/assets/guide_module.md.fcc5d1bb.lean.js @@ -0,0 +1 @@ +import{_ as e,o as s,c as n,Q as a}from"./chunks/framework.ec8f7e8e.js";const m=JSON.parse('{"title":"Module guides","description":"","frontmatter":{},"headers":[],"relativePath":"guide/module.md","filePath":"guide/module.md"}'),l={name:"guide/module.md"},o=a("",58),t=[o];function i(p,c,r,d,u,h){return s(),n("div",null,t)}const g=e(l,[["render",i]]);export{m as __pageData,g as default}; diff --git a/assets/guide_rescue-from-bootloop.md.86b40f3b.js b/assets/guide_rescue-from-bootloop.md.656e5988.js similarity index 98% rename from assets/guide_rescue-from-bootloop.md.86b40f3b.js rename to assets/guide_rescue-from-bootloop.md.656e5988.js index c6c7070f18a8..8f135f6de2ad 100644 --- a/assets/guide_rescue-from-bootloop.md.86b40f3b.js +++ b/assets/guide_rescue-from-bootloop.md.656e5988.js @@ -1 +1 @@ -import{_ as e,o,c as t,O as a}from"./chunks/framework.43781440.js";const p=JSON.parse('{"title":"Rescue from bootloop","description":"","frontmatter":{},"headers":[],"relativePath":"guide/rescue-from-bootloop.md","filePath":"guide/rescue-from-bootloop.md"}'),s={name:"guide/rescue-from-bootloop.md"},i=a('

Rescue from bootloop

When flashing a device, we may encounter situations where the device becomes "bricked". In theory, if you only use fastboot to flash the boot partition or install unsuitable modules that cause the device to fail to boot, then this can be restored by appropriate operations. This document aims to provide some emergency methods to help you recover from a "bricked" device.

Brick by flashing boot partition

In KernelSU, the following situations may cause boot brick when flashing the boot partition:

  1. You flashed a boot image in the wrong format. For example, if your phone's boot format is gz, but you flashed an lz4 format image, then the phone will not be able to boot.
  2. Your phone needs to disable AVB verification in order to boot properly (usually requiring wiping all data on the phone).
  3. Your kernel has some bugs or is not suitable for your phone to flash.

No matter what the situation is, you can recover by flashing the stock boot image. Therefore, at the beginning of the installation tutorial, we strongly recommend that you back up your stock boot before flashing. If you haven't backed up, you can obtain the original factory boot from other users with the same device as you or from the official firmware.

Brick by modules

Installing modules can be a more common cause of bricking your device, but we must seriously warn you: Do not install modules from unknown sources! Since modules have root privileges, they can potentially cause irreversible damage to your device!

Normal modules

If you have flashed a module that has been proven to be safe but causes your device to fail to boot, then this situation is easily recoverable in KernelSU without any worries. KernelSU has built-in mechanisms to rescue your device, including the following:

  1. AB update
  2. Rescue by pressing Volume Down

AB update

KernelSU's module updates draw inspiration from the Android system's AB update mechanism used in OTA updates. If you install a new module or update an existing one, it will not directly modify the currently used module file. Instead, all modules will be built into another update image. After the system is restarted, it will attempt to start using this update image. If the Android system successfully boots up, the modules will then be truly updated.

Therefore, the simplest and most commonly used method to rescue your device is to force a reboot. If you are unable to start your system after flashing a module, you can press and hold the power button for more than 10 seconds, and the system will automatically reboot; after rebooting, it will roll back to the state before updating the module, and the previously updated modules will be automatically disabled.

Rescue by pressing Volume Down

If AB updates still cannot solve the problem, you can try using Safe Mode. In Safe Mode, all modules are disabled.

There are two ways to enter Safe Mode:

  1. The built-in Safe Mode of some systems; some systems have a built-in Safe Mode that can be accessed by long-pressing the volume down button, while others (such as MIUI) can enable Safe Mode in Recovery. When entering the system's Safe Mode, KernelSU will also enter Safe Mode and automatically disable modules.
  2. The built-in Safe Mode of KernelSU; the operation method is to press the volume down key continuously for more than three times after the first boot screen. Note that it is press-release, press-release, press-release, not press and hold.

After entering safe mode, all modules on the module page of the KernelSU Manager are disabled, but you can perform "uninstall" operations to uninstall any modules that may be causing issues.

The built-in safe mode is implemented in the kernel, so there is no possibility of missing key events due to interception. However, for non-GKI kernels, manual integration of the code may be required, and you can refer to the official documentation for guidance.

Malicious modules

If the above methods cannot rescue your device, it is highly likely that the module you installed has malicious operations or has damaged your device through other means. In this case, there are only two suggestions:

  1. Wipe the data and flash the official system.
  2. Consult the after-sales service.
',23),r=[i];function l(n,u,d,h,c,m){return o(),t("div",null,r)}const f=e(s,[["render",l]]);export{p as __pageData,f as default}; +import{_ as e,o,c as t,Q as a}from"./chunks/framework.ec8f7e8e.js";const p=JSON.parse('{"title":"Rescue from bootloop","description":"","frontmatter":{},"headers":[],"relativePath":"guide/rescue-from-bootloop.md","filePath":"guide/rescue-from-bootloop.md"}'),s={name:"guide/rescue-from-bootloop.md"},i=a('

Rescue from bootloop

When flashing a device, we may encounter situations where the device becomes "bricked". In theory, if you only use fastboot to flash the boot partition or install unsuitable modules that cause the device to fail to boot, then this can be restored by appropriate operations. This document aims to provide some emergency methods to help you recover from a "bricked" device.

Brick by flashing boot partition

In KernelSU, the following situations may cause boot brick when flashing the boot partition:

  1. You flashed a boot image in the wrong format. For example, if your phone's boot format is gz, but you flashed an lz4 format image, then the phone will not be able to boot.
  2. Your phone needs to disable AVB verification in order to boot properly (usually requiring wiping all data on the phone).
  3. Your kernel has some bugs or is not suitable for your phone to flash.

No matter what the situation is, you can recover by flashing the stock boot image. Therefore, at the beginning of the installation tutorial, we strongly recommend that you back up your stock boot before flashing. If you haven't backed up, you can obtain the original factory boot from other users with the same device as you or from the official firmware.

Brick by modules

Installing modules can be a more common cause of bricking your device, but we must seriously warn you: Do not install modules from unknown sources! Since modules have root privileges, they can potentially cause irreversible damage to your device!

Normal modules

If you have flashed a module that has been proven to be safe but causes your device to fail to boot, then this situation is easily recoverable in KernelSU without any worries. KernelSU has built-in mechanisms to rescue your device, including the following:

  1. AB update
  2. Rescue by pressing Volume Down

AB update

KernelSU's module updates draw inspiration from the Android system's AB update mechanism used in OTA updates. If you install a new module or update an existing one, it will not directly modify the currently used module file. Instead, all modules will be built into another update image. After the system is restarted, it will attempt to start using this update image. If the Android system successfully boots up, the modules will then be truly updated.

Therefore, the simplest and most commonly used method to rescue your device is to force a reboot. If you are unable to start your system after flashing a module, you can press and hold the power button for more than 10 seconds, and the system will automatically reboot; after rebooting, it will roll back to the state before updating the module, and the previously updated modules will be automatically disabled.

Rescue by pressing Volume Down

If AB updates still cannot solve the problem, you can try using Safe Mode. In Safe Mode, all modules are disabled.

There are two ways to enter Safe Mode:

  1. The built-in Safe Mode of some systems; some systems have a built-in Safe Mode that can be accessed by long-pressing the volume down button, while others (such as MIUI) can enable Safe Mode in Recovery. When entering the system's Safe Mode, KernelSU will also enter Safe Mode and automatically disable modules.
  2. The built-in Safe Mode of KernelSU; the operation method is to press the volume down key continuously for more than three times after the first boot screen. Note that it is press-release, press-release, press-release, not press and hold.

After entering safe mode, all modules on the module page of the KernelSU Manager are disabled, but you can perform "uninstall" operations to uninstall any modules that may be causing issues.

The built-in safe mode is implemented in the kernel, so there is no possibility of missing key events due to interception. However, for non-GKI kernels, manual integration of the code may be required, and you can refer to the official documentation for guidance.

Malicious modules

If the above methods cannot rescue your device, it is highly likely that the module you installed has malicious operations or has damaged your device through other means. In this case, there are only two suggestions:

  1. Wipe the data and flash the official system.
  2. Consult the after-sales service.
',23),r=[i];function l(n,u,d,h,c,m){return o(),t("div",null,r)}const f=e(s,[["render",l]]);export{p as __pageData,f as default}; diff --git a/assets/guide_rescue-from-bootloop.md.86b40f3b.lean.js b/assets/guide_rescue-from-bootloop.md.656e5988.lean.js similarity index 85% rename from assets/guide_rescue-from-bootloop.md.86b40f3b.lean.js rename to assets/guide_rescue-from-bootloop.md.656e5988.lean.js index 4743fa7ac81e..e2e5bd1d4466 100644 --- a/assets/guide_rescue-from-bootloop.md.86b40f3b.lean.js +++ b/assets/guide_rescue-from-bootloop.md.656e5988.lean.js @@ -1 +1 @@ -import{_ as e,o,c as t,O as a}from"./chunks/framework.43781440.js";const p=JSON.parse('{"title":"Rescue from bootloop","description":"","frontmatter":{},"headers":[],"relativePath":"guide/rescue-from-bootloop.md","filePath":"guide/rescue-from-bootloop.md"}'),s={name:"guide/rescue-from-bootloop.md"},i=a("",23),r=[i];function l(n,u,d,h,c,m){return o(),t("div",null,r)}const f=e(s,[["render",l]]);export{p as __pageData,f as default}; +import{_ as e,o,c as t,Q as a}from"./chunks/framework.ec8f7e8e.js";const p=JSON.parse('{"title":"Rescue from bootloop","description":"","frontmatter":{},"headers":[],"relativePath":"guide/rescue-from-bootloop.md","filePath":"guide/rescue-from-bootloop.md"}'),s={name:"guide/rescue-from-bootloop.md"},i=a("",23),r=[i];function l(n,u,d,h,c,m){return o(),t("div",null,r)}const f=e(s,[["render",l]]);export{p as __pageData,f as default}; diff --git a/assets/guide_unofficially-support-devices.md.d9173329.js b/assets/guide_unofficially-support-devices.md.692e1ce3.js similarity index 88% rename from assets/guide_unofficially-support-devices.md.d9173329.js rename to assets/guide_unofficially-support-devices.md.692e1ce3.js index 0242bfb1403f..4bb227a58702 100644 --- a/assets/guide_unofficially-support-devices.md.d9173329.js +++ b/assets/guide_unofficially-support-devices.md.692e1ce3.js @@ -1 +1 @@ -import{d as n}from"./chunks/repos.9a031e32.js";import{o as i,c as r,z as e,F as s,L as o,b as l,O as c,t as a}from"./chunks/framework.43781440.js";const d=c('

Unofficially Supported Devices

WARNING

In this page, there are kernels for non-GKI devices supporting KernelSU maintained by other developers.

WARNING

This page is only for you to find the source code corresponding to your device, it does NOT mean that the source code has been reviewed by KernelSU Developers. You should use it at your own risk.

',3),u=e("thead",null,[e("tr",null,[e("th",null,"Maintainer"),e("th",null,"Repository"),e("th",null,"Support devices")])],-1),p=["href"],_=["href"],k=JSON.parse('{"title":"Unofficially Supported Devices","description":"","frontmatter":{},"headers":[],"relativePath":"guide/unofficially-support-devices.md","filePath":"guide/unofficially-support-devices.md"}'),f={name:"guide/unofficially-support-devices.md"},b=Object.assign(f,{setup(h){return(m,v)=>(i(),r("div",null,[d,e("table",null,[u,e("tbody",null,[(i(!0),r(s,null,o(l(n),t=>(i(),r("tr",{key:t.devices},[e("td",null,[e("a",{href:t.maintainer_link,target:"_blank",rel:"noreferrer"},a(t.maintainer),9,p)]),e("td",null,[e("a",{href:t.kernel_link,target:"_blank",rel:"noreferrer"},a(t.kernel_name),9,_)]),e("td",null,a(t.devices),1)]))),128))])])]))}});export{k as __pageData,b as default}; +import{d as n}from"./chunks/repos.9a031e32.js";import{o as i,c as r,k as e,F as s,D as l,l as o,Q as c,t as a}from"./chunks/framework.ec8f7e8e.js";const d=c('

Unofficially Supported Devices

WARNING

In this page, there are kernels for non-GKI devices supporting KernelSU maintained by other developers.

WARNING

This page is only for you to find the source code corresponding to your device, it does NOT mean that the source code has been reviewed by KernelSU Developers. You should use it at your own risk.

',3),u=e("thead",null,[e("tr",null,[e("th",null,"Maintainer"),e("th",null,"Repository"),e("th",null,"Support devices")])],-1),p=["href"],_=["href"],k=JSON.parse('{"title":"Unofficially Supported Devices","description":"","frontmatter":{},"headers":[],"relativePath":"guide/unofficially-support-devices.md","filePath":"guide/unofficially-support-devices.md"}'),f={name:"guide/unofficially-support-devices.md"},b=Object.assign(f,{setup(h){return(m,v)=>(i(),r("div",null,[d,e("table",null,[u,e("tbody",null,[(i(!0),r(s,null,l(o(n),t=>(i(),r("tr",{key:t.devices},[e("td",null,[e("a",{href:t.maintainer_link,target:"_blank",rel:"noreferrer"},a(t.maintainer),9,p)]),e("td",null,[e("a",{href:t.kernel_link,target:"_blank",rel:"noreferrer"},a(t.kernel_name),9,_)]),e("td",null,a(t.devices),1)]))),128))])])]))}});export{k as __pageData,b as default}; diff --git a/assets/guide_unofficially-support-devices.md.d9173329.lean.js b/assets/guide_unofficially-support-devices.md.692e1ce3.lean.js similarity index 80% rename from assets/guide_unofficially-support-devices.md.d9173329.lean.js rename to assets/guide_unofficially-support-devices.md.692e1ce3.lean.js index 1913073d39d0..b26f468e0a36 100644 --- a/assets/guide_unofficially-support-devices.md.d9173329.lean.js +++ b/assets/guide_unofficially-support-devices.md.692e1ce3.lean.js @@ -1 +1 @@ -import{d as n}from"./chunks/repos.9a031e32.js";import{o as i,c as r,z as e,F as s,L as o,b as l,O as c,t as a}from"./chunks/framework.43781440.js";const d=c("",3),u=e("thead",null,[e("tr",null,[e("th",null,"Maintainer"),e("th",null,"Repository"),e("th",null,"Support devices")])],-1),p=["href"],_=["href"],k=JSON.parse('{"title":"Unofficially Supported Devices","description":"","frontmatter":{},"headers":[],"relativePath":"guide/unofficially-support-devices.md","filePath":"guide/unofficially-support-devices.md"}'),f={name:"guide/unofficially-support-devices.md"},b=Object.assign(f,{setup(h){return(m,v)=>(i(),r("div",null,[d,e("table",null,[u,e("tbody",null,[(i(!0),r(s,null,o(l(n),t=>(i(),r("tr",{key:t.devices},[e("td",null,[e("a",{href:t.maintainer_link,target:"_blank",rel:"noreferrer"},a(t.maintainer),9,p)]),e("td",null,[e("a",{href:t.kernel_link,target:"_blank",rel:"noreferrer"},a(t.kernel_name),9,_)]),e("td",null,a(t.devices),1)]))),128))])])]))}});export{k as __pageData,b as default}; +import{d as n}from"./chunks/repos.9a031e32.js";import{o as i,c as r,k as e,F as s,D as l,l as o,Q as c,t as a}from"./chunks/framework.ec8f7e8e.js";const d=c("",3),u=e("thead",null,[e("tr",null,[e("th",null,"Maintainer"),e("th",null,"Repository"),e("th",null,"Support devices")])],-1),p=["href"],_=["href"],k=JSON.parse('{"title":"Unofficially Supported Devices","description":"","frontmatter":{},"headers":[],"relativePath":"guide/unofficially-support-devices.md","filePath":"guide/unofficially-support-devices.md"}'),f={name:"guide/unofficially-support-devices.md"},b=Object.assign(f,{setup(h){return(m,v)=>(i(),r("div",null,[d,e("table",null,[u,e("tbody",null,[(i(!0),r(s,null,l(o(n),t=>(i(),r("tr",{key:t.devices},[e("td",null,[e("a",{href:t.maintainer_link,target:"_blank",rel:"noreferrer"},a(t.maintainer),9,p)]),e("td",null,[e("a",{href:t.kernel_link,target:"_blank",rel:"noreferrer"},a(t.kernel_name),9,_)]),e("td",null,a(t.devices),1)]))),128))])])]))}});export{k as __pageData,b as default}; diff --git a/assets/guide_what-is-kernelsu.md.340bbd63.js b/assets/guide_what-is-kernelsu.md.f089de90.js similarity index 94% rename from assets/guide_what-is-kernelsu.md.340bbd63.js rename to assets/guide_what-is-kernelsu.md.f089de90.js index 67111c911825..f4e233f49e80 100644 --- a/assets/guide_what-is-kernelsu.md.340bbd63.js +++ b/assets/guide_what-is-kernelsu.md.f089de90.js @@ -1 +1 @@ -import{_ as e,o as a,c as o,O as t}from"./chunks/framework.43781440.js";const f=JSON.parse('{"title":"What is KernelSU?","description":"","frontmatter":{},"headers":[],"relativePath":"guide/what-is-kernelsu.md","filePath":"guide/what-is-kernelsu.md"}'),r={name:"guide/what-is-kernelsu.md"},s=t('

What is KernelSU?

KernelSU is a root solution for Android GKI devices, it works in kernel mode and grant root permission to userspace application directly in kernel space.

Features

The main feature of KernelSU is it is Kernel-based. KernelSU works in kernel mode, so it can provide kernel interface we never had before. For example, we can add hardware breakpoint to any process in kernel mode; We can access physical memory of any process without anybody being aware of; We can intercept any syscall in kernel space; etc.

And also, KernelSU provides a module system via overlayfs, which allows you to load your custom plugin into system. It also provides a mechanism to modify files in /system partition.

How to use

Please refer: Installation

How to build

How to build

Discussion

',11),i=[s];function n(l,d,h,c,u,p){return a(),o("div",null,i)}const _=e(r,[["render",n]]);export{f as __pageData,_ as default}; +import{_ as e,o as a,c as o,Q as t}from"./chunks/framework.ec8f7e8e.js";const f=JSON.parse('{"title":"What is KernelSU?","description":"","frontmatter":{},"headers":[],"relativePath":"guide/what-is-kernelsu.md","filePath":"guide/what-is-kernelsu.md"}'),r={name:"guide/what-is-kernelsu.md"},s=t('

What is KernelSU?

KernelSU is a root solution for Android GKI devices, it works in kernel mode and grant root permission to userspace application directly in kernel space.

Features

The main feature of KernelSU is it is Kernel-based. KernelSU works in kernel mode, so it can provide kernel interface we never had before. For example, we can add hardware breakpoint to any process in kernel mode; We can access physical memory of any process without anybody being aware of; We can intercept any syscall in kernel space; etc.

And also, KernelSU provides a module system via overlayfs, which allows you to load your custom plugin into system. It also provides a mechanism to modify files in /system partition.

How to use

Please refer: Installation

How to build

How to build

Discussion

',11),i=[s];function n(l,d,h,c,u,p){return a(),o("div",null,i)}const _=e(r,[["render",n]]);export{f as __pageData,_ as default}; diff --git a/assets/guide_what-is-kernelsu.md.340bbd63.lean.js b/assets/guide_what-is-kernelsu.md.f089de90.lean.js similarity index 69% rename from assets/guide_what-is-kernelsu.md.340bbd63.lean.js rename to assets/guide_what-is-kernelsu.md.f089de90.lean.js index 8d78fe31e7d4..2207f27f2ab3 100644 --- a/assets/guide_what-is-kernelsu.md.340bbd63.lean.js +++ b/assets/guide_what-is-kernelsu.md.f089de90.lean.js @@ -1 +1 @@ -import{_ as e,o as a,c as o,O as t}from"./chunks/framework.43781440.js";const f=JSON.parse('{"title":"What is KernelSU?","description":"","frontmatter":{},"headers":[],"relativePath":"guide/what-is-kernelsu.md","filePath":"guide/what-is-kernelsu.md"}'),r={name:"guide/what-is-kernelsu.md"},s=t("",11),i=[s];function n(l,d,h,c,u,p){return a(),o("div",null,i)}const _=e(r,[["render",n]]);export{f as __pageData,_ as default}; +import{_ as e,o as a,c as o,Q as t}from"./chunks/framework.ec8f7e8e.js";const f=JSON.parse('{"title":"What is KernelSU?","description":"","frontmatter":{},"headers":[],"relativePath":"guide/what-is-kernelsu.md","filePath":"guide/what-is-kernelsu.md"}'),r={name:"guide/what-is-kernelsu.md"},s=t("",11),i=[s];function n(l,d,h,c,u,p){return a(),o("div",null,i)}const _=e(r,[["render",n]]);export{f as __pageData,_ as default}; diff --git a/assets/id_ID_guide_difference-with-magisk.md.abc25e03.js b/assets/id_ID_guide_difference-with-magisk.md.24526677.js similarity index 96% rename from assets/id_ID_guide_difference-with-magisk.md.abc25e03.js rename to assets/id_ID_guide_difference-with-magisk.md.24526677.js index 437daecb357c..9eb88b635a6e 100644 --- a/assets/id_ID_guide_difference-with-magisk.md.abc25e03.js +++ b/assets/id_ID_guide_difference-with-magisk.md.24526677.js @@ -1 +1 @@ -import{_ as a,o as e,c as n,O as i}from"./chunks/framework.43781440.js";const p=JSON.parse('{"title":"Perbedaan dengan Magisk","description":"","frontmatter":{},"headers":[],"relativePath":"id_ID/guide/difference-with-magisk.md","filePath":"id_ID/guide/difference-with-magisk.md"}'),d={name:"id_ID/guide/difference-with-magisk.md"},t=i('

Perbedaan dengan Magisk

Meskipun ada banyak kesamaan antara modul KernelSU dan modul Magisk, pasti ada beberapa perbedaan karena mekanisme implementasinya yang sangat berbeda. Jika Anda ingin modul Anda berjalan di Magisk dan KernelSU, Anda harus memahami perbedaan ini.

Kesamaan

  • Format file modul: keduanya menggunakan format zip untuk mengatur modul, dan format modulnya hampir sama
  • Direktori pemasangan modul: keduanya terletak di /data/adb/modules
  • Tanpa sistem: keduanya mendukung modifikasi / sistem dengan cara tanpa sistem melalui modul
  • post-fs-data.sh: waktu eksekusi dan semantiknya persis sama
  • service.sh: waktu eksekusi dan semantiknya persis sama
  • system.prop: sepenuhnya sama
  • sepolicy.rule: sama persis
  • BusyBox: skrip dijalankan di BusyBox dengan "mode mandiri" diaktifkan di kedua kasus

Perbedaan

Sebelum memahami perbedaannya, Anda perlu mengetahui cara membedakan apakah modul Anda berjalan di KernelSU atau Magisk. Anda dapat menggunakan variabel lingkungan KSU untuk membedakannya di semua tempat di mana Anda dapat menjalankan skrip modul (customize.sh, post-fs-data.sh, service.sh). Di KernelSU, variabel lingkungan ini akan disetel ke true.

Berikut beberapa perbedaannya:

  • Modul KernelSU tidak dapat diinstal dalam mode Pemulihan.
  • Modul KernelSU tidak memiliki dukungan bawaan untuk Zygisk (tetapi Anda dapat menggunakan modul Zygisk melalui ZygiskNext.
  • Metode untuk mengganti atau menghapus file dalam modul KernelSU sama sekali berbeda dari Magisk. KernelSU tidak mendukung metode .replace. Sebagai gantinya, Anda perlu membuat file dengan nama yang sama dengan mknod filename c 0 0 untuk menghapus file terkait.
  • Direktori untuk BusyBox berbeda. BusyBox bawaan di KernelSU terletak di /data/adb/ksu/bin/busybox, sedangkan di Magisk terletak di /data/adb/magisk/busybox. Perhatikan bahwa ini adalah perilaku internal KernelSU dan dapat berubah di masa mendatang!
  • KernelSU tidak mendukung file .replace; namun, KernelSU mendukung variabel REMOVE dan REPLACE untuk menghapus atau mengganti file dan folder.
',8),s=[t];function r(l,m,u,o,k,g){return e(),n("div",null,s)}const b=a(d,[["render",r]]);export{p as __pageData,b as default}; +import{_ as a,o as e,c as n,Q as i}from"./chunks/framework.ec8f7e8e.js";const p=JSON.parse('{"title":"Perbedaan dengan Magisk","description":"","frontmatter":{},"headers":[],"relativePath":"id_ID/guide/difference-with-magisk.md","filePath":"id_ID/guide/difference-with-magisk.md"}'),d={name:"id_ID/guide/difference-with-magisk.md"},t=i('

Perbedaan dengan Magisk

Meskipun ada banyak kesamaan antara modul KernelSU dan modul Magisk, pasti ada beberapa perbedaan karena mekanisme implementasinya yang sangat berbeda. Jika Anda ingin modul Anda berjalan di Magisk dan KernelSU, Anda harus memahami perbedaan ini.

Kesamaan

  • Format file modul: keduanya menggunakan format zip untuk mengatur modul, dan format modulnya hampir sama
  • Direktori pemasangan modul: keduanya terletak di /data/adb/modules
  • Tanpa sistem: keduanya mendukung modifikasi / sistem dengan cara tanpa sistem melalui modul
  • post-fs-data.sh: waktu eksekusi dan semantiknya persis sama
  • service.sh: waktu eksekusi dan semantiknya persis sama
  • system.prop: sepenuhnya sama
  • sepolicy.rule: sama persis
  • BusyBox: skrip dijalankan di BusyBox dengan "mode mandiri" diaktifkan di kedua kasus

Perbedaan

Sebelum memahami perbedaannya, Anda perlu mengetahui cara membedakan apakah modul Anda berjalan di KernelSU atau Magisk. Anda dapat menggunakan variabel lingkungan KSU untuk membedakannya di semua tempat di mana Anda dapat menjalankan skrip modul (customize.sh, post-fs-data.sh, service.sh). Di KernelSU, variabel lingkungan ini akan disetel ke true.

Berikut beberapa perbedaannya:

  • Modul KernelSU tidak dapat diinstal dalam mode Pemulihan.
  • Modul KernelSU tidak memiliki dukungan bawaan untuk Zygisk (tetapi Anda dapat menggunakan modul Zygisk melalui ZygiskNext.
  • Metode untuk mengganti atau menghapus file dalam modul KernelSU sama sekali berbeda dari Magisk. KernelSU tidak mendukung metode .replace. Sebagai gantinya, Anda perlu membuat file dengan nama yang sama dengan mknod filename c 0 0 untuk menghapus file terkait.
  • Direktori untuk BusyBox berbeda. BusyBox bawaan di KernelSU terletak di /data/adb/ksu/bin/busybox, sedangkan di Magisk terletak di /data/adb/magisk/busybox. Perhatikan bahwa ini adalah perilaku internal KernelSU dan dapat berubah di masa mendatang!
  • KernelSU tidak mendukung file .replace; namun, KernelSU mendukung variabel REMOVE dan REPLACE untuk menghapus atau mengganti file dan folder.
',8),s=[t];function r(l,m,u,o,k,g){return e(),n("div",null,s)}const b=a(d,[["render",r]]);export{p as __pageData,b as default}; diff --git a/assets/id_ID_guide_difference-with-magisk.md.abc25e03.lean.js b/assets/id_ID_guide_difference-with-magisk.md.24526677.lean.js similarity index 72% rename from assets/id_ID_guide_difference-with-magisk.md.abc25e03.lean.js rename to assets/id_ID_guide_difference-with-magisk.md.24526677.lean.js index a60ceb5f9a34..71855b1e9fd3 100644 --- a/assets/id_ID_guide_difference-with-magisk.md.abc25e03.lean.js +++ b/assets/id_ID_guide_difference-with-magisk.md.24526677.lean.js @@ -1 +1 @@ -import{_ as a,o as e,c as n,O as i}from"./chunks/framework.43781440.js";const p=JSON.parse('{"title":"Perbedaan dengan Magisk","description":"","frontmatter":{},"headers":[],"relativePath":"id_ID/guide/difference-with-magisk.md","filePath":"id_ID/guide/difference-with-magisk.md"}'),d={name:"id_ID/guide/difference-with-magisk.md"},t=i("",8),s=[t];function r(l,m,u,o,k,g){return e(),n("div",null,s)}const b=a(d,[["render",r]]);export{p as __pageData,b as default}; +import{_ as a,o as e,c as n,Q as i}from"./chunks/framework.ec8f7e8e.js";const p=JSON.parse('{"title":"Perbedaan dengan Magisk","description":"","frontmatter":{},"headers":[],"relativePath":"id_ID/guide/difference-with-magisk.md","filePath":"id_ID/guide/difference-with-magisk.md"}'),d={name:"id_ID/guide/difference-with-magisk.md"},t=i("",8),s=[t];function r(l,m,u,o,k,g){return e(),n("div",null,s)}const b=a(d,[["render",r]]);export{p as __pageData,b as default}; diff --git a/assets/id_ID_guide_faq.md.8fe1170a.js b/assets/id_ID_guide_faq.md.a19560c6.js similarity index 97% rename from assets/id_ID_guide_faq.md.8fe1170a.js rename to assets/id_ID_guide_faq.md.a19560c6.js index 20d9aaf8be2f..7863d1418815 100644 --- a/assets/id_ID_guide_faq.md.8fe1170a.js +++ b/assets/id_ID_guide_faq.md.a19560c6.js @@ -1 +1 @@ -import{_ as a,o as e,c as n,O as k}from"./chunks/framework.43781440.js";const p=JSON.parse('{"title":"FAQ","description":"","frontmatter":{},"headers":[],"relativePath":"id_ID/guide/faq.md","filePath":"id_ID/guide/faq.md"}'),r={name:"id_ID/guide/faq.md"},i=k('

FAQ

Apakah KernelSU mendukung perangkat saya?

Pertama, perangkatmu harus bisa dibuka bootloadernya. Jika tiddak bisa, berarti tidak memungkinkan untuk bekerja.

Lalu instal aplikasi KernelSU manager di dalam perangkatmu dan buka, jika terlihat Unsupported berarti perangkatmu tidak didukung dan tidak akan didukung di kemudian hari.

Apakah KernelSU membutuhkan buka-bootloader?

Ya, seharusnya.

Apakah KernelSU mendukung modul?

Ya, Tetapi masih dalam versi awal, bisa jadi ngebug. Mohon tunggu sampai semuanya stabil 😃

Apakah KernelSU mendukung Xposed?

Ya, Dreamland dan TaiChi sekarang bekerja sebagian, Dan kita sedang mencoba untuk membuat Xposed Framework lainnya bekerja.

Apakah KernelSU kompatibel dengan Magisk?

Sistem modul KernelSU bertentangan dengan magic mount Magisk, jika ada modul yang diaktifkan di KernelSU, maka seluruh Magisk tidak akan bekerja.

Tetapi jika Anda hanya menggunakan su dari KernelSU, maka KernelSU akan bekerja dengan baik dengan Magisk: KernelSU memodifikasi kernel dan Magisk memodifikasi ramdisk, keduanya dapat bekerja bersama.

Akankah KernelSU menggantikan Magisk?

Kami rasa tidak dan itu bukan tujuan kami. Magisk sudah cukup baik untuk solusi root userspace dan akan bertahan lama. Tujuan KernelSU adalah untuk menyediakan antarmuka kernel kepada pengguna, bukan untuk menggantikan Magisk.

Dapatkah KernelSU mendukung perangkat non GKI?

Hal ini dimungkinkan. Tetapi Anda harus mengunduh sumber kernel dan mengintegrasikan KernelSU ke source tree dan mengkompilasi kernel sendiri.

Dapatkah KernelSU mendukung perangkat di bawah Android 12?

Kernel perangkatlah yang mempengaruhi kompatibilitas KernelSU dan tidak ada hubungannya dengan versi Android, satu-satunya batasan adalah bahwa perangkat yang diluncurkan dengan Android 12 harus menggunakan kernel 5.10+ (perangkat GKI). Jadi:

  1. Perangkat yang diluncurkan dengan Android 12 harus didukung.
  2. Perangkat dengan kernel lama (Beberapa perangkat Android 12 juga memiliki kernel lama) dapat dikompilasi (Anda harus membuat kernel sendiri)

Dapatkah KernelSU mendukung kernel lama?

Ada kemungkinan, KernelSU sudah di-backport ke kernel 4.14 sekarang, untuk kernel yang lebih lama, Anda harus melakukan backport secara manual dan menyambut baik PR darimu!

Cara mengintegrasikan KernelSU untuk kernel lama?

Silakan merujuk ke guide

',24),u=[i];function d(t,l,m,o,s,h){return e(),n("div",null,u)}const b=a(r,[["render",d]]);export{p as __pageData,b as default}; +import{_ as a,o as e,c as n,Q as k}from"./chunks/framework.ec8f7e8e.js";const p=JSON.parse('{"title":"FAQ","description":"","frontmatter":{},"headers":[],"relativePath":"id_ID/guide/faq.md","filePath":"id_ID/guide/faq.md"}'),r={name:"id_ID/guide/faq.md"},i=k('

FAQ

Apakah KernelSU mendukung perangkat saya?

Pertama, perangkatmu harus bisa dibuka bootloadernya. Jika tiddak bisa, berarti tidak memungkinkan untuk bekerja.

Lalu instal aplikasi KernelSU manager di dalam perangkatmu dan buka, jika terlihat Unsupported berarti perangkatmu tidak didukung dan tidak akan didukung di kemudian hari.

Apakah KernelSU membutuhkan buka-bootloader?

Ya, seharusnya.

Apakah KernelSU mendukung modul?

Ya, Tetapi masih dalam versi awal, bisa jadi ngebug. Mohon tunggu sampai semuanya stabil 😃

Apakah KernelSU mendukung Xposed?

Ya, Dreamland dan TaiChi sekarang bekerja sebagian, Dan kita sedang mencoba untuk membuat Xposed Framework lainnya bekerja.

Apakah KernelSU kompatibel dengan Magisk?

Sistem modul KernelSU bertentangan dengan magic mount Magisk, jika ada modul yang diaktifkan di KernelSU, maka seluruh Magisk tidak akan bekerja.

Tetapi jika Anda hanya menggunakan su dari KernelSU, maka KernelSU akan bekerja dengan baik dengan Magisk: KernelSU memodifikasi kernel dan Magisk memodifikasi ramdisk, keduanya dapat bekerja bersama.

Akankah KernelSU menggantikan Magisk?

Kami rasa tidak dan itu bukan tujuan kami. Magisk sudah cukup baik untuk solusi root userspace dan akan bertahan lama. Tujuan KernelSU adalah untuk menyediakan antarmuka kernel kepada pengguna, bukan untuk menggantikan Magisk.

Dapatkah KernelSU mendukung perangkat non GKI?

Hal ini dimungkinkan. Tetapi Anda harus mengunduh sumber kernel dan mengintegrasikan KernelSU ke source tree dan mengkompilasi kernel sendiri.

Dapatkah KernelSU mendukung perangkat di bawah Android 12?

Kernel perangkatlah yang mempengaruhi kompatibilitas KernelSU dan tidak ada hubungannya dengan versi Android, satu-satunya batasan adalah bahwa perangkat yang diluncurkan dengan Android 12 harus menggunakan kernel 5.10+ (perangkat GKI). Jadi:

  1. Perangkat yang diluncurkan dengan Android 12 harus didukung.
  2. Perangkat dengan kernel lama (Beberapa perangkat Android 12 juga memiliki kernel lama) dapat dikompilasi (Anda harus membuat kernel sendiri)

Dapatkah KernelSU mendukung kernel lama?

Ada kemungkinan, KernelSU sudah di-backport ke kernel 4.14 sekarang, untuk kernel yang lebih lama, Anda harus melakukan backport secara manual dan menyambut baik PR darimu!

Cara mengintegrasikan KernelSU untuk kernel lama?

Silakan merujuk ke guide

',24),u=[i];function d(t,l,m,o,s,h){return e(),n("div",null,u)}const b=a(r,[["render",d]]);export{p as __pageData,b as default}; diff --git a/assets/id_ID_guide_faq.md.8fe1170a.lean.js b/assets/id_ID_guide_faq.md.a19560c6.lean.js similarity index 67% rename from assets/id_ID_guide_faq.md.8fe1170a.lean.js rename to assets/id_ID_guide_faq.md.a19560c6.lean.js index d909e7618237..6fd4481398cd 100644 --- a/assets/id_ID_guide_faq.md.8fe1170a.lean.js +++ b/assets/id_ID_guide_faq.md.a19560c6.lean.js @@ -1 +1 @@ -import{_ as a,o as e,c as n,O as k}from"./chunks/framework.43781440.js";const p=JSON.parse('{"title":"FAQ","description":"","frontmatter":{},"headers":[],"relativePath":"id_ID/guide/faq.md","filePath":"id_ID/guide/faq.md"}'),r={name:"id_ID/guide/faq.md"},i=k("",24),u=[i];function d(t,l,m,o,s,h){return e(),n("div",null,u)}const b=a(r,[["render",d]]);export{p as __pageData,b as default}; +import{_ as a,o as e,c as n,Q as k}from"./chunks/framework.ec8f7e8e.js";const p=JSON.parse('{"title":"FAQ","description":"","frontmatter":{},"headers":[],"relativePath":"id_ID/guide/faq.md","filePath":"id_ID/guide/faq.md"}'),r={name:"id_ID/guide/faq.md"},i=k("",24),u=[i];function d(t,l,m,o,s,h){return e(),n("div",null,u)}const b=a(r,[["render",d]]);export{p as __pageData,b as default}; diff --git a/assets/id_ID_guide_how-to-build.md.1ecedb69.js b/assets/id_ID_guide_how-to-build.md.1ecedb69.js deleted file mode 100644 index 0b058ce26d19..000000000000 --- a/assets/id_ID_guide_how-to-build.md.1ecedb69.js +++ /dev/null @@ -1,4 +0,0 @@ -import{_ as a,o as s,c as n,O as e}from"./chunks/framework.43781440.js";const y=JSON.parse('{"title":"Bagaimana caranya untuk build KernelSU?","description":"","frontmatter":{},"headers":[],"relativePath":"id_ID/guide/how-to-build.md","filePath":"id_ID/guide/how-to-build.md"}'),l={name:"id_ID/guide/how-to-build.md"},o=e(`

Bagaimana caranya untuk build KernelSU?

Pertama, Anda harus membaca dokumen resmi Android untuk membangun kernel:

  1. Building Kernels
  2. GKI Release Builds

Halaman ini untuk perangkat GKI, jika Anda menggunakan kernel lama, silakan lihat cara mengintegrasikan KernelSU untuk kernel lama

Build Kernel

Menyinkronkan source code kernel

sh
repo init -u https://android.googlesource.com/kernel/manifest
-mv <kernel_manifest.xml> .repo/manifests
-repo init -m manifest.xml
-repo sync

<kernel_manifest.xml> adalah berkas manifes yang dapat menentukan build secara unik, Anda dapat menggunakan manifes tersebut untuk melakukan build yang dapat diprediksikan ulang. Anda harus mengunduh berkas manifes dari Google GKI release builds

Build

Silakan periksa official docs terlebih dahulu.

Sebagai contoh, kita perlu build image kernel aarch64:

sh
LTO=thin BUILD_CONFIG=common/build.config.gki.aarch64 build/build.sh

Jangan lupa untuk menambahkan flag LTO=thin, jika tidak, maka build akan gagal jika memori komputer Anda kurang dari 24GB.

Mulai dari Android 13, kernel dibuild oleh bazel:

sh
tools/bazel build --config=fast //common:kernel_aarch64_dist

Build Kernel dengan KernelSU

Jika Anda dapat build kernel dengan sukses, maka build KernelSU sangatlah mudah, jalankan perintah ini di root dir kernel source:

  • Latest tag(stable)
sh
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -
  • main branch(dev)
sh
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -s main
  • Select tag(Such as v0.5.2)
sh
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -s v0.5.2

Dan kemudian build ulang kernel dan Anda akan mendapatkan image kernel dengan KernelSU!

`,24),t=[o];function r(p,i,c,d,u,C){return s(),n("div",null,t)}const k=a(l,[["render",r]]);export{y as __pageData,k as default}; diff --git a/assets/id_ID_guide_how-to-build.md.1ecedb69.lean.js b/assets/id_ID_guide_how-to-build.md.1ecedb69.lean.js deleted file mode 100644 index ea3ae7f79955..000000000000 --- a/assets/id_ID_guide_how-to-build.md.1ecedb69.lean.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as a,o as s,c as n,O as e}from"./chunks/framework.43781440.js";const y=JSON.parse('{"title":"Bagaimana caranya untuk build KernelSU?","description":"","frontmatter":{},"headers":[],"relativePath":"id_ID/guide/how-to-build.md","filePath":"id_ID/guide/how-to-build.md"}'),l={name:"id_ID/guide/how-to-build.md"},o=e("",24),t=[o];function r(p,i,c,d,u,C){return s(),n("div",null,t)}const k=a(l,[["render",r]]);export{y as __pageData,k as default}; diff --git a/assets/id_ID_guide_how-to-build.md.f09b7a1a.js b/assets/id_ID_guide_how-to-build.md.f09b7a1a.js new file mode 100644 index 000000000000..05af2969d7e0 --- /dev/null +++ b/assets/id_ID_guide_how-to-build.md.f09b7a1a.js @@ -0,0 +1,7 @@ +import{_ as s,o as a,c as n,Q as l}from"./chunks/framework.ec8f7e8e.js";const E=JSON.parse('{"title":"Bagaimana caranya untuk build KernelSU?","description":"","frontmatter":{},"headers":[],"relativePath":"id_ID/guide/how-to-build.md","filePath":"id_ID/guide/how-to-build.md"}'),e={name:"id_ID/guide/how-to-build.md"},o=l(`

Bagaimana caranya untuk build KernelSU?

Pertama, Anda harus membaca dokumen resmi Android untuk membangun kernel:

  1. Building Kernels
  2. GKI Release Builds

Halaman ini untuk perangkat GKI, jika Anda menggunakan kernel lama, silakan lihat cara mengintegrasikan KernelSU untuk kernel lama

Build Kernel

Menyinkronkan source code kernel

sh
repo init -u https://android.googlesource.com/kernel/manifest
+mv <kernel_manifest.xml> .repo/manifests
+repo init -m manifest.xml
+repo sync
repo init -u https://android.googlesource.com/kernel/manifest
+mv <kernel_manifest.xml> .repo/manifests
+repo init -m manifest.xml
+repo sync

<kernel_manifest.xml> adalah berkas manifes yang dapat menentukan build secara unik, Anda dapat menggunakan manifes tersebut untuk melakukan build yang dapat diprediksikan ulang. Anda harus mengunduh berkas manifes dari Google GKI release builds

Build

Silakan periksa official docs terlebih dahulu.

Sebagai contoh, kita perlu build image kernel aarch64:

sh
LTO=thin BUILD_CONFIG=common/build.config.gki.aarch64 build/build.sh
LTO=thin BUILD_CONFIG=common/build.config.gki.aarch64 build/build.sh

Jangan lupa untuk menambahkan flag LTO=thin, jika tidak, maka build akan gagal jika memori komputer Anda kurang dari 24GB.

Mulai dari Android 13, kernel dibuild oleh bazel:

sh
tools/bazel build --config=fast //common:kernel_aarch64_dist
tools/bazel build --config=fast //common:kernel_aarch64_dist

Build Kernel dengan KernelSU

Jika Anda dapat build kernel dengan sukses, maka build KernelSU sangatlah mudah, jalankan perintah ini di root dir kernel source:

  • Latest tag(stable)
sh
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -
  • main branch(dev)
sh
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -s main
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -s main
  • Select tag(Such as v0.5.2)
sh
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -s v0.5.2
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -s v0.5.2

Dan kemudian build ulang kernel dan Anda akan mendapatkan image kernel dengan KernelSU!

`,24),p=[o];function t(r,c,i,d,u,y){return a(),n("div",null,p)}const k=s(e,[["render",t]]);export{E as __pageData,k as default}; diff --git a/assets/id_ID_guide_how-to-build.md.f09b7a1a.lean.js b/assets/id_ID_guide_how-to-build.md.f09b7a1a.lean.js new file mode 100644 index 000000000000..8510c7aaad7d --- /dev/null +++ b/assets/id_ID_guide_how-to-build.md.f09b7a1a.lean.js @@ -0,0 +1 @@ +import{_ as s,o as a,c as n,Q as l}from"./chunks/framework.ec8f7e8e.js";const E=JSON.parse('{"title":"Bagaimana caranya untuk build KernelSU?","description":"","frontmatter":{},"headers":[],"relativePath":"id_ID/guide/how-to-build.md","filePath":"id_ID/guide/how-to-build.md"}'),e={name:"id_ID/guide/how-to-build.md"},o=l("",24),p=[o];function t(r,c,i,d,u,y){return a(),n("div",null,p)}const k=s(e,[["render",t]]);export{E as __pageData,k as default}; diff --git a/assets/id_ID_guide_how-to-integrate-for-non-gki.md.16b9216a.js b/assets/id_ID_guide_how-to-integrate-for-non-gki.md.16b9216a.js deleted file mode 100644 index 9c50484f8670..000000000000 --- a/assets/id_ID_guide_how-to-integrate-for-non-gki.md.16b9216a.js +++ /dev/null @@ -1,152 +0,0 @@ -import{_ as s,o as a,c as n,O as e}from"./chunks/framework.43781440.js";const y=JSON.parse('{"title":"Bagaimana Caranya untuk mengintegrasikan KernelSU ke kernel non GKI?","description":"","frontmatter":{},"headers":[],"relativePath":"id_ID/guide/how-to-integrate-for-non-gki.md","filePath":"id_ID/guide/how-to-integrate-for-non-gki.md"}'),l={name:"id_ID/guide/how-to-integrate-for-non-gki.md"},p=e(`

Bagaimana Caranya untuk mengintegrasikan KernelSU ke kernel non GKI?

KernelSU dapat diintegrasikan ke kernel non GKI, dan saat ini sudah di-backport ke 4.14, dan juga dapat dijalankan pada kernel di bawah 4.14.

Karena fragmentasi kernel non GKI, kami tidak memiliki cara yang seragam untuk membangunnya, sehingga kami tidak dapat menyediakan gambar boot non GKI. Tetapi Anda dapat membangun kernel sendiri dengan KernelSU yang terintegrasi.

Pertama, Anda harus dapat membangun kernel yang dapat di-boot dari kode sumber kernel, jika kernel tersebut tidak open source, maka akan sulit untuk menjalankan KernelSU untuk perangkat Anda.

Jika Anda dapat membuat kernel yang dapat di-booting, ada dua cara untuk mengintegrasikan KernelSU ke kode sumber kernel:

  1. Secara otomatis dengan kprobe
  2. Secara manual

Integrasikan dengan kprobe

KernelSU menggunakan kprobe untuk melakukan hook kernel, jika kprobe berjalan dengan baik pada kernel Anda, maka disarankan untuk menggunakan cara ini.

Pertama, tambahkan KernelSU ke dalam berkas kernel source tree:

  • Latest tag(stable)
sh
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -
  • main branch(dev)
sh
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -s main
  • Select tag(Such as v0.5.2)
sh
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -s v0.5.2

Kemudian, Anda harus memeriksa apakah kprobe diaktifkan dalam konfigurasi kernel Anda, jika tidak, tambahkan konfigurasi ini ke dalamnya:

CONFIG_KPROBES=y
-CONFIG_HAVE_KPROBES=y
-CONFIG_KPROBE_EVENTS=y

Dan build kernel Anda lagi, KernelSU seharusnya bekerja dengan baik.

Jika Anda menemukan bahwa KPROBES masih belum diaktifkan, Anda dapat mencoba mengaktifkan CONFIG_MODULES. (Jika masih belum berlaku, gunakan make menuconfig untuk mencari ketergantungan KPROBES yang lain)

etapi jika Anda mengalami boot loop saat mengintegrasikan KernelSU, itu mungkin kprobe rusak di kernel Anda, Anda harus memperbaiki bug kprobe atau menggunakan cara kedua.

Memodifikasi sumber kernel secara manual

Jika kprobe tidak dapat bekerja pada kernel Anda (mungkin karena bug di upstream atau kernel di bawah 4.8), maka Anda dapat mencoba cara ini:

Pertama, tambahkan KernelSU ke dalam direktori kernel source tree:

sh
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -

Kemudian, tambahkan panggilan KernelSU ke source kernel, berikut ini adalah patch yang dapat dirujuk:

diff
diff --git a/fs/exec.c b/fs/exec.c
-index ac59664eaecf..bdd585e1d2cc 100644
---- a/fs/exec.c
-+++ b/fs/exec.c
-@@ -1890,11 +1890,14 @@ static int __do_execve_file(int fd, struct filename *filename,
- 	return retval;
- }
- 
-+extern bool ksu_execveat_hook __read_mostly;
-+extern int ksu_handle_execveat(int *fd, struct filename **filename_ptr, void *argv,
-+			void *envp, int *flags);
-+extern int ksu_handle_execveat_sucompat(int *fd, struct filename **filename_ptr,
-+				 void *argv, void *envp, int *flags);
- static int do_execveat_common(int fd, struct filename *filename,
- 			      struct user_arg_ptr argv,
- 			      struct user_arg_ptr envp,
- 			      int flags)
- {
-+	if (unlikely(ksu_execveat_hook))
-+		ksu_handle_execveat(&fd, &filename, &argv, &envp, &flags);
-+	else
-+		ksu_handle_execveat_sucompat(&fd, &filename, &argv, &envp, &flags);
- 	return __do_execve_file(fd, filename, argv, envp, flags, NULL);
- }
- 
-diff --git a/fs/open.c b/fs/open.c
-index 05036d819197..965b84d486b8 100644
---- a/fs/open.c
-+++ b/fs/open.c
-@@ -348,6 +348,8 @@ SYSCALL_DEFINE4(fallocate, int, fd, int, mode, loff_t, offset, loff_t, len)
- 	return ksys_fallocate(fd, mode, offset, len);
- }
- 
-+extern int ksu_handle_faccessat(int *dfd, const char __user **filename_user, int *mode,
-+			 int *flags);
- /*
-  * access() needs to use the real uid/gid, not the effective uid/gid.
-  * We do this by temporarily clearing all FS-related capabilities and
-@@ -355,6 +357,7 @@ SYSCALL_DEFINE4(fallocate, int, fd, int, mode, loff_t, offset, loff_t, len)
-  */
- long do_faccessat(int dfd, const char __user *filename, int mode)
- {
-+	ksu_handle_faccessat(&dfd, &filename, &mode, NULL);
- 	const struct cred *old_cred;
- 	struct cred *override_cred;
- 	struct path path;
-diff --git a/fs/read_write.c b/fs/read_write.c
-index 650fc7e0f3a6..55be193913b6 100644
---- a/fs/read_write.c
-+++ b/fs/read_write.c
-@@ -434,10 +434,14 @@ ssize_t kernel_read(struct file *file, void *buf, size_t count, loff_t *pos)
- }
- EXPORT_SYMBOL(kernel_read);
- 
-+extern bool ksu_vfs_read_hook __read_mostly;
-+extern int ksu_handle_vfs_read(struct file **file_ptr, char __user **buf_ptr,
-+			size_t *count_ptr, loff_t **pos);
- ssize_t vfs_read(struct file *file, char __user *buf, size_t count, loff_t *pos)
- {
- 	ssize_t ret;
- 
-+	if (unlikely(ksu_vfs_read_hook))
-+		ksu_handle_vfs_read(&file, &buf, &count, &pos);
-+
- 	if (!(file->f_mode & FMODE_READ))
- 		return -EBADF;
- 	if (!(file->f_mode & FMODE_CAN_READ))
-diff --git a/fs/stat.c b/fs/stat.c
-index 376543199b5a..82adcef03ecc 100644
---- a/fs/stat.c
-+++ b/fs/stat.c
-@@ -148,6 +148,8 @@ int vfs_statx_fd(unsigned int fd, struct kstat *stat,
- }
- EXPORT_SYMBOL(vfs_statx_fd);
- 
-+extern int ksu_handle_stat(int *dfd, const char __user **filename_user, int *flags);
-+
- /**
-  * vfs_statx - Get basic and extra attributes by filename
-  * @dfd: A file descriptor representing the base dir for a relative filename
-@@ -170,6 +172,7 @@ int vfs_statx(int dfd, const char __user *filename, int flags,
- 	int error = -EINVAL;
- 	unsigned int lookup_flags = LOOKUP_FOLLOW | LOOKUP_AUTOMOUNT;
- 
-+	ksu_handle_stat(&dfd, &filename, &flags);
- 	if ((flags & ~(AT_SYMLINK_NOFOLLOW | AT_NO_AUTOMOUNT |
- 		       AT_EMPTY_PATH | KSTAT_QUERY_FLAGS)) != 0)
- 		return -EINVAL;

Anda harus menemukan empat fungsi dalam kernel source:

  1. do_faccessat, usually in fs/open.c
  2. do_execveat_common, usually in fs/exec.c
  3. vfs_read, usually in fs/read_write.c
  4. vfs_statx, usually in fs/stat.c

Jika kernel anda tidak memiliki vfs_statx, maka gunakan vfs_fstatat alih-alih:

diff
diff --git a/fs/stat.c b/fs/stat.c
-index 068fdbcc9e26..5348b7bb9db2 100644
---- a/fs/stat.c
-+++ b/fs/stat.c
-@@ -87,6 +87,8 @@ int vfs_fstat(unsigned int fd, struct kstat *stat)
- }
- EXPORT_SYMBOL(vfs_fstat);
- 
-+extern int ksu_handle_stat(int *dfd, const char __user **filename_user, int *flags);
-+
- int vfs_fstatat(int dfd, const char __user *filename, struct kstat *stat,
- 		int flag)
- {
-@@ -94,6 +96,8 @@ int vfs_fstatat(int dfd, const char __user *filename, struct kstat *stat,
- 	int error = -EINVAL;
- 	unsigned int lookup_flags = 0;
- 
-+	ksu_handle_stat(&dfd, &filename, &flag);
-+
- 	if ((flag & ~(AT_SYMLINK_NOFOLLOW | AT_NO_AUTOMOUNT |
- 		      AT_EMPTY_PATH)) != 0)
- 		goto out;

Untuk kernel lebih awal dari 4.17, jika anda menemukan do_faccessat, hanya pergi ke definisi yang sama faccessat syscall dan tempatkan pemanggil di sini:

diff
diff --git a/fs/open.c b/fs/open.c
-index 2ff887661237..e758d7db7663 100644
---- a/fs/open.c
-+++ b/fs/open.c
-@@ -355,6 +355,9 @@ SYSCALL_DEFINE4(fallocate, int, fd, int, mode, loff_t, offset, loff_t, len)
- 	return error;
- }
- 
-+extern int ksu_handle_faccessat(int *dfd, const char __user **filename_user, int *mode,
-+			        int *flags);
-+
- /*
-  * access() needs to use the real uid/gid, not the effective uid/gid.
-  * We do this by temporarily clearing all FS-related capabilities and
-@@ -370,6 +373,8 @@ SYSCALL_DEFINE3(faccessat, int, dfd, const char __user *, filename, int, mode)
- 	int res;
- 	unsigned int lookup_flags = LOOKUP_FOLLOW;
- 
-+	ksu_handle_faccessat(&dfd, &filename, &mode, NULL);
-+
- 	if (mode & ~S_IRWXO)	/* where's F_OK, X_OK, W_OK, R_OK? */
- 		return -EINVAL;

To enable KernelSU's builtin SafeMode, You should also modify input_handle_event in drivers/input/input.c:

TIP

Fitur ini sangat direkomendasikan, serta sangat membantu untuk memulihkan pada saat bootloop!

diff
diff --git a/drivers/input/input.c b/drivers/input/input.c
-index 45306f9ef247..815091ebfca4 100755
---- a/drivers/input/input.c
-+++ b/drivers/input/input.c
-@@ -367,10 +367,13 @@ static int input_get_disposition(struct input_dev *dev,
- 	return disposition;
- }
- 
-+extern bool ksu_input_hook __read_mostly;
-+extern int ksu_handle_input_handle_event(unsigned int *type, unsigned int *code, int *value);
-+
- static void input_handle_event(struct input_dev *dev,
- 			       unsigned int type, unsigned int code, int value)
- {
-	int disposition = input_get_disposition(dev, type, code, &value);
-+
-+	if (unlikely(ksu_input_hook))
-+		ksu_handle_input_handle_event(&type, &code, &value);
- 
- 	if (disposition != INPUT_IGNORE_EVENT && type != EV_SYN)
- 		add_input_randomness(type, code, value);

Terakhir, edit KernelSU/kernel/ksu.c dan beri komentar pada enable_sucompat() lalu build kernel Anda lagi, KernelSU akan bekerja dengan baik.

`,36),t=[p];function o(c,i,r,d,C,D){return a(),n("div",null,t)}const u=s(l,[["render",o]]);export{y as __pageData,u as default}; diff --git a/assets/id_ID_guide_how-to-integrate-for-non-gki.md.16b9216a.lean.js b/assets/id_ID_guide_how-to-integrate-for-non-gki.md.16b9216a.lean.js deleted file mode 100644 index b1d1dc6f5843..000000000000 --- a/assets/id_ID_guide_how-to-integrate-for-non-gki.md.16b9216a.lean.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as s,o as a,c as n,O as e}from"./chunks/framework.43781440.js";const y=JSON.parse('{"title":"Bagaimana Caranya untuk mengintegrasikan KernelSU ke kernel non GKI?","description":"","frontmatter":{},"headers":[],"relativePath":"id_ID/guide/how-to-integrate-for-non-gki.md","filePath":"id_ID/guide/how-to-integrate-for-non-gki.md"}'),l={name:"id_ID/guide/how-to-integrate-for-non-gki.md"},p=e("",36),t=[p];function o(c,i,r,d,C,D){return a(),n("div",null,t)}const u=s(l,[["render",o]]);export{y as __pageData,u as default}; diff --git a/assets/id_ID_guide_how-to-integrate-for-non-gki.md.600d4255.js b/assets/id_ID_guide_how-to-integrate-for-non-gki.md.600d4255.js new file mode 100644 index 000000000000..abae8935e5ea --- /dev/null +++ b/assets/id_ID_guide_how-to-integrate-for-non-gki.md.600d4255.js @@ -0,0 +1,303 @@ +import{_ as s,o as n,c as a,Q as e}from"./chunks/framework.ec8f7e8e.js";const u=JSON.parse('{"title":"Bagaimana Caranya untuk mengintegrasikan KernelSU ke kernel non GKI?","description":"","frontmatter":{},"headers":[],"relativePath":"id_ID/guide/how-to-integrate-for-non-gki.md","filePath":"id_ID/guide/how-to-integrate-for-non-gki.md"}'),l={name:"id_ID/guide/how-to-integrate-for-non-gki.md"},p=e(`

Bagaimana Caranya untuk mengintegrasikan KernelSU ke kernel non GKI?

KernelSU dapat diintegrasikan ke kernel non GKI, dan saat ini sudah di-backport ke 4.14, dan juga dapat dijalankan pada kernel di bawah 4.14.

Karena fragmentasi kernel non GKI, kami tidak memiliki cara yang seragam untuk membangunnya, sehingga kami tidak dapat menyediakan gambar boot non GKI. Tetapi Anda dapat membangun kernel sendiri dengan KernelSU yang terintegrasi.

Pertama, Anda harus dapat membangun kernel yang dapat di-boot dari kode sumber kernel, jika kernel tersebut tidak open source, maka akan sulit untuk menjalankan KernelSU untuk perangkat Anda.

Jika Anda dapat membuat kernel yang dapat di-booting, ada dua cara untuk mengintegrasikan KernelSU ke kode sumber kernel:

  1. Secara otomatis dengan kprobe
  2. Secara manual

Integrasikan dengan kprobe

KernelSU menggunakan kprobe untuk melakukan hook kernel, jika kprobe berjalan dengan baik pada kernel Anda, maka disarankan untuk menggunakan cara ini.

Pertama, tambahkan KernelSU ke dalam berkas kernel source tree:

  • Latest tag(stable)
sh
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -
  • main branch(dev)
sh
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -s main
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -s main
  • Select tag(Such as v0.5.2)
sh
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -s v0.5.2
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -s v0.5.2

Kemudian, Anda harus memeriksa apakah kprobe diaktifkan dalam konfigurasi kernel Anda, jika tidak, tambahkan konfigurasi ini ke dalamnya:

CONFIG_KPROBES=y
+CONFIG_HAVE_KPROBES=y
+CONFIG_KPROBE_EVENTS=y
CONFIG_KPROBES=y
+CONFIG_HAVE_KPROBES=y
+CONFIG_KPROBE_EVENTS=y

Dan build kernel Anda lagi, KernelSU seharusnya bekerja dengan baik.

Jika Anda menemukan bahwa KPROBES masih belum diaktifkan, Anda dapat mencoba mengaktifkan CONFIG_MODULES. (Jika masih belum berlaku, gunakan make menuconfig untuk mencari ketergantungan KPROBES yang lain)

etapi jika Anda mengalami boot loop saat mengintegrasikan KernelSU, itu mungkin kprobe rusak di kernel Anda, Anda harus memperbaiki bug kprobe atau menggunakan cara kedua.

Memodifikasi sumber kernel secara manual

Jika kprobe tidak dapat bekerja pada kernel Anda (mungkin karena bug di upstream atau kernel di bawah 4.8), maka Anda dapat mencoba cara ini:

Pertama, tambahkan KernelSU ke dalam direktori kernel source tree:

sh
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -

Kemudian, tambahkan panggilan KernelSU ke source kernel, berikut ini adalah patch yang dapat dirujuk:

diff
diff --git a/fs/exec.c b/fs/exec.c
+index ac59664eaecf..bdd585e1d2cc 100644
+--- a/fs/exec.c
++++ b/fs/exec.c
+@@ -1890,11 +1890,14 @@ static int __do_execve_file(int fd, struct filename *filename,
+ 	return retval;
+ }
+ 
++extern bool ksu_execveat_hook __read_mostly;
++extern int ksu_handle_execveat(int *fd, struct filename **filename_ptr, void *argv,
++			void *envp, int *flags);
++extern int ksu_handle_execveat_sucompat(int *fd, struct filename **filename_ptr,
++				 void *argv, void *envp, int *flags);
+ static int do_execveat_common(int fd, struct filename *filename,
+ 			      struct user_arg_ptr argv,
+ 			      struct user_arg_ptr envp,
+ 			      int flags)
+ {
++	if (unlikely(ksu_execveat_hook))
++		ksu_handle_execveat(&fd, &filename, &argv, &envp, &flags);
++	else
++		ksu_handle_execveat_sucompat(&fd, &filename, &argv, &envp, &flags);
+ 	return __do_execve_file(fd, filename, argv, envp, flags, NULL);
+ }
+ 
+diff --git a/fs/open.c b/fs/open.c
+index 05036d819197..965b84d486b8 100644
+--- a/fs/open.c
++++ b/fs/open.c
+@@ -348,6 +348,8 @@ SYSCALL_DEFINE4(fallocate, int, fd, int, mode, loff_t, offset, loff_t, len)
+ 	return ksys_fallocate(fd, mode, offset, len);
+ }
+ 
++extern int ksu_handle_faccessat(int *dfd, const char __user **filename_user, int *mode,
++			 int *flags);
+ /*
+  * access() needs to use the real uid/gid, not the effective uid/gid.
+  * We do this by temporarily clearing all FS-related capabilities and
+@@ -355,6 +357,7 @@ SYSCALL_DEFINE4(fallocate, int, fd, int, mode, loff_t, offset, loff_t, len)
+  */
+ long do_faccessat(int dfd, const char __user *filename, int mode)
+ {
++	ksu_handle_faccessat(&dfd, &filename, &mode, NULL);
+ 	const struct cred *old_cred;
+ 	struct cred *override_cred;
+ 	struct path path;
+diff --git a/fs/read_write.c b/fs/read_write.c
+index 650fc7e0f3a6..55be193913b6 100644
+--- a/fs/read_write.c
++++ b/fs/read_write.c
+@@ -434,10 +434,14 @@ ssize_t kernel_read(struct file *file, void *buf, size_t count, loff_t *pos)
+ }
+ EXPORT_SYMBOL(kernel_read);
+ 
++extern bool ksu_vfs_read_hook __read_mostly;
++extern int ksu_handle_vfs_read(struct file **file_ptr, char __user **buf_ptr,
++			size_t *count_ptr, loff_t **pos);
+ ssize_t vfs_read(struct file *file, char __user *buf, size_t count, loff_t *pos)
+ {
+ 	ssize_t ret;
+ 
++	if (unlikely(ksu_vfs_read_hook))
++		ksu_handle_vfs_read(&file, &buf, &count, &pos);
++
+ 	if (!(file->f_mode & FMODE_READ))
+ 		return -EBADF;
+ 	if (!(file->f_mode & FMODE_CAN_READ))
+diff --git a/fs/stat.c b/fs/stat.c
+index 376543199b5a..82adcef03ecc 100644
+--- a/fs/stat.c
++++ b/fs/stat.c
+@@ -148,6 +148,8 @@ int vfs_statx_fd(unsigned int fd, struct kstat *stat,
+ }
+ EXPORT_SYMBOL(vfs_statx_fd);
+ 
++extern int ksu_handle_stat(int *dfd, const char __user **filename_user, int *flags);
++
+ /**
+  * vfs_statx - Get basic and extra attributes by filename
+  * @dfd: A file descriptor representing the base dir for a relative filename
+@@ -170,6 +172,7 @@ int vfs_statx(int dfd, const char __user *filename, int flags,
+ 	int error = -EINVAL;
+ 	unsigned int lookup_flags = LOOKUP_FOLLOW | LOOKUP_AUTOMOUNT;
+ 
++	ksu_handle_stat(&dfd, &filename, &flags);
+ 	if ((flags & ~(AT_SYMLINK_NOFOLLOW | AT_NO_AUTOMOUNT |
+ 		       AT_EMPTY_PATH | KSTAT_QUERY_FLAGS)) != 0)
+ 		return -EINVAL;
diff --git a/fs/exec.c b/fs/exec.c
+index ac59664eaecf..bdd585e1d2cc 100644
+--- a/fs/exec.c
++++ b/fs/exec.c
+@@ -1890,11 +1890,14 @@ static int __do_execve_file(int fd, struct filename *filename,
+ 	return retval;
+ }
+ 
++extern bool ksu_execveat_hook __read_mostly;
++extern int ksu_handle_execveat(int *fd, struct filename **filename_ptr, void *argv,
++			void *envp, int *flags);
++extern int ksu_handle_execveat_sucompat(int *fd, struct filename **filename_ptr,
++				 void *argv, void *envp, int *flags);
+ static int do_execveat_common(int fd, struct filename *filename,
+ 			      struct user_arg_ptr argv,
+ 			      struct user_arg_ptr envp,
+ 			      int flags)
+ {
++	if (unlikely(ksu_execveat_hook))
++		ksu_handle_execveat(&fd, &filename, &argv, &envp, &flags);
++	else
++		ksu_handle_execveat_sucompat(&fd, &filename, &argv, &envp, &flags);
+ 	return __do_execve_file(fd, filename, argv, envp, flags, NULL);
+ }
+ 
+diff --git a/fs/open.c b/fs/open.c
+index 05036d819197..965b84d486b8 100644
+--- a/fs/open.c
++++ b/fs/open.c
+@@ -348,6 +348,8 @@ SYSCALL_DEFINE4(fallocate, int, fd, int, mode, loff_t, offset, loff_t, len)
+ 	return ksys_fallocate(fd, mode, offset, len);
+ }
+ 
++extern int ksu_handle_faccessat(int *dfd, const char __user **filename_user, int *mode,
++			 int *flags);
+ /*
+  * access() needs to use the real uid/gid, not the effective uid/gid.
+  * We do this by temporarily clearing all FS-related capabilities and
+@@ -355,6 +357,7 @@ SYSCALL_DEFINE4(fallocate, int, fd, int, mode, loff_t, offset, loff_t, len)
+  */
+ long do_faccessat(int dfd, const char __user *filename, int mode)
+ {
++	ksu_handle_faccessat(&dfd, &filename, &mode, NULL);
+ 	const struct cred *old_cred;
+ 	struct cred *override_cred;
+ 	struct path path;
+diff --git a/fs/read_write.c b/fs/read_write.c
+index 650fc7e0f3a6..55be193913b6 100644
+--- a/fs/read_write.c
++++ b/fs/read_write.c
+@@ -434,10 +434,14 @@ ssize_t kernel_read(struct file *file, void *buf, size_t count, loff_t *pos)
+ }
+ EXPORT_SYMBOL(kernel_read);
+ 
++extern bool ksu_vfs_read_hook __read_mostly;
++extern int ksu_handle_vfs_read(struct file **file_ptr, char __user **buf_ptr,
++			size_t *count_ptr, loff_t **pos);
+ ssize_t vfs_read(struct file *file, char __user *buf, size_t count, loff_t *pos)
+ {
+ 	ssize_t ret;
+ 
++	if (unlikely(ksu_vfs_read_hook))
++		ksu_handle_vfs_read(&file, &buf, &count, &pos);
++
+ 	if (!(file->f_mode & FMODE_READ))
+ 		return -EBADF;
+ 	if (!(file->f_mode & FMODE_CAN_READ))
+diff --git a/fs/stat.c b/fs/stat.c
+index 376543199b5a..82adcef03ecc 100644
+--- a/fs/stat.c
++++ b/fs/stat.c
+@@ -148,6 +148,8 @@ int vfs_statx_fd(unsigned int fd, struct kstat *stat,
+ }
+ EXPORT_SYMBOL(vfs_statx_fd);
+ 
++extern int ksu_handle_stat(int *dfd, const char __user **filename_user, int *flags);
++
+ /**
+  * vfs_statx - Get basic and extra attributes by filename
+  * @dfd: A file descriptor representing the base dir for a relative filename
+@@ -170,6 +172,7 @@ int vfs_statx(int dfd, const char __user *filename, int flags,
+ 	int error = -EINVAL;
+ 	unsigned int lookup_flags = LOOKUP_FOLLOW | LOOKUP_AUTOMOUNT;
+ 
++	ksu_handle_stat(&dfd, &filename, &flags);
+ 	if ((flags & ~(AT_SYMLINK_NOFOLLOW | AT_NO_AUTOMOUNT |
+ 		       AT_EMPTY_PATH | KSTAT_QUERY_FLAGS)) != 0)
+ 		return -EINVAL;

Anda harus menemukan empat fungsi dalam kernel source:

  1. do_faccessat, usually in fs/open.c
  2. do_execveat_common, usually in fs/exec.c
  3. vfs_read, usually in fs/read_write.c
  4. vfs_statx, usually in fs/stat.c

Jika kernel anda tidak memiliki vfs_statx, maka gunakan vfs_fstatat alih-alih:

diff
diff --git a/fs/stat.c b/fs/stat.c
+index 068fdbcc9e26..5348b7bb9db2 100644
+--- a/fs/stat.c
++++ b/fs/stat.c
+@@ -87,6 +87,8 @@ int vfs_fstat(unsigned int fd, struct kstat *stat)
+ }
+ EXPORT_SYMBOL(vfs_fstat);
+ 
++extern int ksu_handle_stat(int *dfd, const char __user **filename_user, int *flags);
++
+ int vfs_fstatat(int dfd, const char __user *filename, struct kstat *stat,
+ 		int flag)
+ {
+@@ -94,6 +96,8 @@ int vfs_fstatat(int dfd, const char __user *filename, struct kstat *stat,
+ 	int error = -EINVAL;
+ 	unsigned int lookup_flags = 0;
+ 
++	ksu_handle_stat(&dfd, &filename, &flag);
++
+ 	if ((flag & ~(AT_SYMLINK_NOFOLLOW | AT_NO_AUTOMOUNT |
+ 		      AT_EMPTY_PATH)) != 0)
+ 		goto out;
diff --git a/fs/stat.c b/fs/stat.c
+index 068fdbcc9e26..5348b7bb9db2 100644
+--- a/fs/stat.c
++++ b/fs/stat.c
+@@ -87,6 +87,8 @@ int vfs_fstat(unsigned int fd, struct kstat *stat)
+ }
+ EXPORT_SYMBOL(vfs_fstat);
+ 
++extern int ksu_handle_stat(int *dfd, const char __user **filename_user, int *flags);
++
+ int vfs_fstatat(int dfd, const char __user *filename, struct kstat *stat,
+ 		int flag)
+ {
+@@ -94,6 +96,8 @@ int vfs_fstatat(int dfd, const char __user *filename, struct kstat *stat,
+ 	int error = -EINVAL;
+ 	unsigned int lookup_flags = 0;
+ 
++	ksu_handle_stat(&dfd, &filename, &flag);
++
+ 	if ((flag & ~(AT_SYMLINK_NOFOLLOW | AT_NO_AUTOMOUNT |
+ 		      AT_EMPTY_PATH)) != 0)
+ 		goto out;

Untuk kernel lebih awal dari 4.17, jika anda menemukan do_faccessat, hanya pergi ke definisi yang sama faccessat syscall dan tempatkan pemanggil di sini:

diff
diff --git a/fs/open.c b/fs/open.c
+index 2ff887661237..e758d7db7663 100644
+--- a/fs/open.c
++++ b/fs/open.c
+@@ -355,6 +355,9 @@ SYSCALL_DEFINE4(fallocate, int, fd, int, mode, loff_t, offset, loff_t, len)
+ 	return error;
+ }
+ 
++extern int ksu_handle_faccessat(int *dfd, const char __user **filename_user, int *mode,
++			        int *flags);
++
+ /*
+  * access() needs to use the real uid/gid, not the effective uid/gid.
+  * We do this by temporarily clearing all FS-related capabilities and
+@@ -370,6 +373,8 @@ SYSCALL_DEFINE3(faccessat, int, dfd, const char __user *, filename, int, mode)
+ 	int res;
+ 	unsigned int lookup_flags = LOOKUP_FOLLOW;
+ 
++	ksu_handle_faccessat(&dfd, &filename, &mode, NULL);
++
+ 	if (mode & ~S_IRWXO)	/* where's F_OK, X_OK, W_OK, R_OK? */
+ 		return -EINVAL;
diff --git a/fs/open.c b/fs/open.c
+index 2ff887661237..e758d7db7663 100644
+--- a/fs/open.c
++++ b/fs/open.c
+@@ -355,6 +355,9 @@ SYSCALL_DEFINE4(fallocate, int, fd, int, mode, loff_t, offset, loff_t, len)
+ 	return error;
+ }
+ 
++extern int ksu_handle_faccessat(int *dfd, const char __user **filename_user, int *mode,
++			        int *flags);
++
+ /*
+  * access() needs to use the real uid/gid, not the effective uid/gid.
+  * We do this by temporarily clearing all FS-related capabilities and
+@@ -370,6 +373,8 @@ SYSCALL_DEFINE3(faccessat, int, dfd, const char __user *, filename, int, mode)
+ 	int res;
+ 	unsigned int lookup_flags = LOOKUP_FOLLOW;
+ 
++	ksu_handle_faccessat(&dfd, &filename, &mode, NULL);
++
+ 	if (mode & ~S_IRWXO)	/* where's F_OK, X_OK, W_OK, R_OK? */
+ 		return -EINVAL;

To enable KernelSU's builtin SafeMode, You should also modify input_handle_event in drivers/input/input.c:

TIP

Fitur ini sangat direkomendasikan, serta sangat membantu untuk memulihkan pada saat bootloop!

diff
diff --git a/drivers/input/input.c b/drivers/input/input.c
+index 45306f9ef247..815091ebfca4 100755
+--- a/drivers/input/input.c
++++ b/drivers/input/input.c
+@@ -367,10 +367,13 @@ static int input_get_disposition(struct input_dev *dev,
+ 	return disposition;
+ }
+ 
++extern bool ksu_input_hook __read_mostly;
++extern int ksu_handle_input_handle_event(unsigned int *type, unsigned int *code, int *value);
++
+ static void input_handle_event(struct input_dev *dev,
+ 			       unsigned int type, unsigned int code, int value)
+ {
+	int disposition = input_get_disposition(dev, type, code, &value);
++
++	if (unlikely(ksu_input_hook))
++		ksu_handle_input_handle_event(&type, &code, &value);
+ 
+ 	if (disposition != INPUT_IGNORE_EVENT && type != EV_SYN)
+ 		add_input_randomness(type, code, value);
diff --git a/drivers/input/input.c b/drivers/input/input.c
+index 45306f9ef247..815091ebfca4 100755
+--- a/drivers/input/input.c
++++ b/drivers/input/input.c
+@@ -367,10 +367,13 @@ static int input_get_disposition(struct input_dev *dev,
+ 	return disposition;
+ }
+ 
++extern bool ksu_input_hook __read_mostly;
++extern int ksu_handle_input_handle_event(unsigned int *type, unsigned int *code, int *value);
++
+ static void input_handle_event(struct input_dev *dev,
+ 			       unsigned int type, unsigned int code, int value)
+ {
+	int disposition = input_get_disposition(dev, type, code, &value);
++
++	if (unlikely(ksu_input_hook))
++		ksu_handle_input_handle_event(&type, &code, &value);
+ 
+ 	if (disposition != INPUT_IGNORE_EVENT && type != EV_SYN)
+ 		add_input_randomness(type, code, value);

Terakhir, edit KernelSU/kernel/ksu.c dan beri komentar pada enable_sucompat() lalu build kernel Anda lagi, KernelSU akan bekerja dengan baik.

`,36),t=[p];function o(c,i,r,d,E,y){return n(),a("div",null,t)}const _=s(l,[["render",o]]);export{u as __pageData,_ as default}; diff --git a/assets/id_ID_guide_how-to-integrate-for-non-gki.md.600d4255.lean.js b/assets/id_ID_guide_how-to-integrate-for-non-gki.md.600d4255.lean.js new file mode 100644 index 000000000000..b79b3808e98c --- /dev/null +++ b/assets/id_ID_guide_how-to-integrate-for-non-gki.md.600d4255.lean.js @@ -0,0 +1 @@ +import{_ as s,o as n,c as a,Q as e}from"./chunks/framework.ec8f7e8e.js";const u=JSON.parse('{"title":"Bagaimana Caranya untuk mengintegrasikan KernelSU ke kernel non GKI?","description":"","frontmatter":{},"headers":[],"relativePath":"id_ID/guide/how-to-integrate-for-non-gki.md","filePath":"id_ID/guide/how-to-integrate-for-non-gki.md"}'),l={name:"id_ID/guide/how-to-integrate-for-non-gki.md"},p=e("",36),t=[p];function o(c,i,r,d,E,y){return n(),a("div",null,t)}const _=s(l,[["render",o]]);export{u as __pageData,_ as default}; diff --git a/assets/id_ID_guide_installation.md.a7bb551d.js b/assets/id_ID_guide_installation.md.2d1e9f50.js similarity index 58% rename from assets/id_ID_guide_installation.md.a7bb551d.js rename to assets/id_ID_guide_installation.md.2d1e9f50.js index 22fef4540401..bb4cb1230a0b 100644 --- a/assets/id_ID_guide_installation.md.a7bb551d.js +++ b/assets/id_ID_guide_installation.md.2d1e9f50.js @@ -1 +1 @@ -import{_ as a,o as e,c as n,O as t}from"./chunks/framework.43781440.js";const g=JSON.parse('{"title":"Instalasi","description":"","frontmatter":{},"headers":[],"relativePath":"id_ID/guide/installation.md","filePath":"id_ID/guide/installation.md"}'),o={name:"id_ID/guide/installation.md"},s=t('

Instalasi

Periksa apakah perangkat Anda didukung

Unduh aplikasi manajer KernelSU dari github releases atau github actions, lalu instal aplikasi ke perangkat dan buka aplikasi:

  • Jika aplikasi menunjukkan Unsupported, itu berarti Anda harus mengkompilasi kernel sendiri, KernelSU tidak akan dan tidak pernah menyediakan boot image untuk Anda flash.
  • Jika aplikasi menunjukkan Not installed, maka perangkat Anda secara resmi didukung oleh KernelSU.

Temukan boot.img yang tepat

KernelSU menyediakan boot.img umum untuk perangkat GKI, Anda harus mem-flash boot.img ke partisi boot perangkat Anda.

Anda dapat mengunduh boot.img dari [github actions for kernel] (https://github.com/tiann/KernelSU/actions/workflows/build-kernel.yml), perlu diketahui bahwa Anda harus menggunakan versi boot.img yang tepat. Sebagai contoh, jika perangkat Anda menunjukkan bahwa kernelnya adalah 5.10.101, maka Anda harus mengunduh 5.10.101-xxxx.boot.xxx.

Dan juga, silakan periksa format boot.img Anda, Anda harus menggunakan format yang tepat, seperti lz4gz.

Flash boot.img ke perangkat

Hubungkan perangkat Anda dengan adb lalu jalankan adb reboot bootloader untuk masuk ke mode fastboot, lalu gunakan perintah ini untuk mem-flash KernelSU:

sh
fastboot flash boot boot.img

Reboot

Ketika di-flash, Anda harus menyalakan ulang perangkat Anda:

sh
fastboot reboot
',14),i=[s];function l(r,d,p,k,u,h){return e(),n("div",null,i)}const m=a(o,[["render",l]]);export{g as __pageData,m as default}; +import{_ as a,o as e,c as n,Q as t}from"./chunks/framework.ec8f7e8e.js";const g=JSON.parse('{"title":"Instalasi","description":"","frontmatter":{},"headers":[],"relativePath":"id_ID/guide/installation.md","filePath":"id_ID/guide/installation.md"}'),o={name:"id_ID/guide/installation.md"},s=t('

Instalasi

Periksa apakah perangkat Anda didukung

Unduh aplikasi manajer KernelSU dari github releases atau github actions, lalu instal aplikasi ke perangkat dan buka aplikasi:

  • Jika aplikasi menunjukkan Unsupported, itu berarti Anda harus mengkompilasi kernel sendiri, KernelSU tidak akan dan tidak pernah menyediakan boot image untuk Anda flash.
  • Jika aplikasi menunjukkan Not installed, maka perangkat Anda secara resmi didukung oleh KernelSU.

Temukan boot.img yang tepat

KernelSU menyediakan boot.img umum untuk perangkat GKI, Anda harus mem-flash boot.img ke partisi boot perangkat Anda.

Anda dapat mengunduh boot.img dari [github actions for kernel] (https://github.com/tiann/KernelSU/actions/workflows/build-kernel.yml), perlu diketahui bahwa Anda harus menggunakan versi boot.img yang tepat. Sebagai contoh, jika perangkat Anda menunjukkan bahwa kernelnya adalah 5.10.101, maka Anda harus mengunduh 5.10.101-xxxx.boot.xxx.

Dan juga, silakan periksa format boot.img Anda, Anda harus menggunakan format yang tepat, seperti lz4gz.

Flash boot.img ke perangkat

Hubungkan perangkat Anda dengan adb lalu jalankan adb reboot bootloader untuk masuk ke mode fastboot, lalu gunakan perintah ini untuk mem-flash KernelSU:

sh
fastboot flash boot boot.img
fastboot flash boot boot.img

Reboot

Ketika di-flash, Anda harus menyalakan ulang perangkat Anda:

sh
fastboot reboot
fastboot reboot
',14),l=[s];function i(r,p,d,c,k,u){return e(),n("div",null,l)}const b=a(o,[["render",i]]);export{g as __pageData,b as default}; diff --git a/assets/id_ID_guide_installation.md.2d1e9f50.lean.js b/assets/id_ID_guide_installation.md.2d1e9f50.lean.js new file mode 100644 index 000000000000..f9f616572b68 --- /dev/null +++ b/assets/id_ID_guide_installation.md.2d1e9f50.lean.js @@ -0,0 +1 @@ +import{_ as a,o as e,c as n,Q as t}from"./chunks/framework.ec8f7e8e.js";const g=JSON.parse('{"title":"Instalasi","description":"","frontmatter":{},"headers":[],"relativePath":"id_ID/guide/installation.md","filePath":"id_ID/guide/installation.md"}'),o={name:"id_ID/guide/installation.md"},s=t("",14),l=[s];function i(r,p,d,c,k,u){return e(),n("div",null,l)}const b=a(o,[["render",i]]);export{g as __pageData,b as default}; diff --git a/assets/id_ID_guide_installation.md.a7bb551d.lean.js b/assets/id_ID_guide_installation.md.a7bb551d.lean.js deleted file mode 100644 index 416e9b106590..000000000000 --- a/assets/id_ID_guide_installation.md.a7bb551d.lean.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as a,o as e,c as n,O as t}from"./chunks/framework.43781440.js";const g=JSON.parse('{"title":"Instalasi","description":"","frontmatter":{},"headers":[],"relativePath":"id_ID/guide/installation.md","filePath":"id_ID/guide/installation.md"}'),o={name:"id_ID/guide/installation.md"},s=t("",14),i=[s];function l(r,d,p,k,u,h){return e(),n("div",null,i)}const m=a(o,[["render",l]]);export{g as __pageData,m as default}; diff --git a/assets/id_ID_guide_module.md.a65f7310.js b/assets/id_ID_guide_module.md.068bf86d.js similarity index 53% rename from assets/id_ID_guide_module.md.a65f7310.js rename to assets/id_ID_guide_module.md.068bf86d.js index 3eb9354842d2..ef2fe06f3f60 100644 --- a/assets/id_ID_guide_module.md.a65f7310.js +++ b/assets/id_ID_guide_module.md.068bf86d.js @@ -1,81 +1,161 @@ -import{_ as a,o as e,c as n,O as s}from"./chunks/framework.43781440.js";const g=JSON.parse('{"title":"Panduan module","description":"","frontmatter":{},"headers":[],"relativePath":"id_ID/guide/module.md","filePath":"id_ID/guide/module.md"}'),i={name:"id_ID/guide/module.md"},l=s(`

Panduan module

KernelSU menyediakan mekanisme modul yang mencapai efek memodifikasi direktori sistem dengan tetap menjaga integritas partisi sistem. Mekanisme ini umumnya dikenal sebagai "tanpa sistem".

Mekanisme modul KernelSU hampir sama dengan Magisk. Jika Anda terbiasa dengan pengembangan modul Magisk, mengembangkan modul KernelSU sangat mirip. Anda dapat melewati pengenalan modul di bawah ini dan hanya perlu membaca difference-with-magisk.

Busybox

KernelSU dikirimkan dengan fitur biner BusyBox yang lengkap (termasuk dukungan penuh SELinux). Eksekusi terletak di /data/adb/ksu/bin/busybox. BusyBox KernelSU mendukung "Mode Shell Standalone Shell" yang dapat dialihkan waktu proses. Apa yang dimaksud dengan mode mandiri ini adalah bahwa ketika dijalankan di shell ash dari BusyBox, setiap perintah akan langsung menggunakan applet di dalam BusyBox, terlepas dari apa yang ditetapkan sebagai PATH. Misalnya, perintah seperti ls, rm, chmod TIDAK akan menggunakan apa yang ada di PATH (dalam kasus Android secara default akan menjadi /system/bin/ls, /system/bin/rm, dan /system/bin/chmod masing-masing), tetapi akan langsung memanggil applet BusyBox internal. Ini memastikan bahwa skrip selalu berjalan di lingkungan yang dapat diprediksi dan selalu memiliki rangkaian perintah lengkap, apa pun versi Android yang menjalankannya. Untuk memaksa perintah not menggunakan BusyBox, Anda harus memanggil yang dapat dieksekusi dengan path lengkap.

Setiap skrip shell tunggal yang berjalan dalam konteks KernelSU akan dieksekusi di shell ash BusyBox dengan mode mandiri diaktifkan. Untuk apa yang relevan dengan pengembang pihak ke-3, ini termasuk semua skrip boot dan skrip instalasi modul.

Bagi yang ingin menggunakan fitur “Standalone Mode” ini di luar KernelSU, ada 2 cara untuk mengaktifkannya:

  1. Tetapkan variabel lingkungan ASH_STANDALONE ke 1
    Contoh: ASH_STANDALONE=1 /data/adb/ksu/bin/busybox sh <script>
  2. Beralih dengan opsi baris perintah:
    /data/adb/ksu/bin/busybox sh -o mandiri <script>

Untuk memastikan semua shell sh selanjutnya dijalankan juga dalam mode mandiri, opsi 1 adalah metode yang lebih disukai (dan inilah yang digunakan secara internal oleh KernelSU dan manajer KernelSU) karena variabel lingkungan diwariskan ke proses anak.

::: perbedaan tip dengan Magisk

BusyBox KernelSU sekarang menggunakan file biner yang dikompilasi langsung dari proyek Magisk. Berkat Magisk! Oleh karena itu, Anda tidak perlu khawatir tentang masalah kompatibilitas antara skrip BusyBox di Magisk dan KernelSU karena keduanya persis sama! :::

KernelSU module

Modul KernelSU adalah folder yang ditempatkan di /data/adb/modules dengan struktur di bawah ini:

txt
/data/adb/modules
-├── .
-├── .
-|
-├── $MODID                  <--- The folder is named with the ID of the module
-│   │
-│   │      *** Module Identity ***
-│   │
-│   ├── module.prop         <--- This file stores the metadata of the module
-│   │
-│   │      *** Main Contents ***
-│   │
-│   ├── system              <--- This folder will be mounted if skip_mount does not exist
-│   │   ├── ...
-│   │   ├── ...
-│   │   └── ...
-│   │
-│   │      *** Status Flags ***
-│   │
-│   ├── skip_mount          <--- If exists, KernelSU will NOT mount your system folder
-│   ├── disable             <--- If exists, the module will be disabled
-│   ├── remove              <--- If exists, the module will be removed next reboot
-│   │
-│   │      *** Optional Files ***
-│   │
-│   ├── post-fs-data.sh     <--- This script will be executed in post-fs-data
-│   ├── service.sh          <--- This script will be executed in late_start service
-|   ├── uninstall.sh        <--- This script will be executed when KernelSU removes your module
-│   ├── system.prop         <--- Properties in this file will be loaded as system properties by resetprop
-│   ├── sepolicy.rule       <--- Additional custom sepolicy rules
-│   │
-│   │      *** Auto Generated, DO NOT MANUALLY CREATE OR MODIFY ***
-│   │
-│   ├── vendor              <--- A symlink to $MODID/system/vendor
-│   ├── product             <--- A symlink to $MODID/system/product
-│   ├── system_ext          <--- A symlink to $MODID/system/system_ext
-│   │
-│   │      *** Any additional files / folders are allowed ***
-│   │
-│   ├── ...
-│   └── ...
-|
-├── another_module
-│   ├── .
-│   └── .
-├── .
-├── .

::: perbedaan tip dengan Magisk KernelSU tidak memiliki dukungan bawaan untuk Zygisk, jadi tidak ada konten terkait Zygisk dalam modul. Namun, Anda dapat menggunakan ZygiskNext untuk mendukung modul Zygisk. Dalam hal ini, konten modul Zygisk identik dengan yang didukung oleh Magisk. :::

module.prop

module.prop adalah file konfigurasi untuk sebuah modul. Di KernelSU, jika modul tidak berisi file ini, maka tidak akan dikenali sebagai modul. Format file ini adalah sebagai berikut:

txt
id=<string>
-name=<string>
-version=<string>
-versioncode=<int>
-author=<string>
-description=<string>
  • id harus cocok dengan ekspresi reguler ini: ^[a-zA-Z][a-zA-Z0-9._-]+$
    contoh: ✓ a_module, ✓ a.module, ✓ module-101, ✗ a module, ✗ 1_module, ✗ -a-module
    Ini adalah pengidentifikasi unik modul Anda. Anda tidak boleh mengubahnya setelah dipublikasikan.
  • versionCode harus berupa integer. Ini digunakan untuk membandingkan versi
  • Lainnya yang tidak disebutkan di atas dapat berupa string satu baris.
  • Pastikan untuk menggunakan tipe jeda baris UNIX (LF) dan bukan Windows (CR+LF) atau Macintosh (CR).

Shell skrip

Harap baca bagian Boot Scripts untuk memahami perbedaan antara post-fs-data.sh dan service.sh. Untuk sebagian besar pengembang modul, service.sh sudah cukup baik jika Anda hanya perlu menjalankan skrip boot.

Di semua skrip modul Anda, harap gunakan MODDIR=\${0%/*} untuk mendapatkan jalur direktori dasar modul Anda; lakukan TIDAK hardcode jalur modul Anda dalam skrip.

::: perbedaan tip dengan Magisk Anda dapat menggunakan variabel lingkungan KSU untuk menentukan apakah skrip berjalan di KernelSU atau Magisk. Jika berjalan di KernelSU, nilai ini akan disetel ke true. :::

system directory

Isi direktori ini akan dihamparkan di atas partisi sistem /sistem menggunakan overlayfs setelah sistem di-boot. Ini berarti bahwa:

  1. File dengan nama yang sama dengan yang ada di direktori terkait di sistem akan ditimpa oleh file di direktori ini.
  2. Folder dengan nama yang sama dengan yang ada di direktori terkait di sistem akan digabungkan dengan folder di direktori ini.

Jika Anda ingin menghapus file atau folder di direktori sistem asli, Anda perlu membuat file dengan nama yang sama dengan file/folder di direktori modul menggunakan mknod filename c 0 0. Dengan cara ini, sistem overlayfs akan secara otomatis "memutihkan" file ini seolah-olah telah dihapus (partisi / sistem sebenarnya tidak diubah).

Anda juga dapat mendeklarasikan variabel bernama REMOVE yang berisi daftar direktori di customize.sh untuk menjalankan operasi penghapusan, dan KernelSU akan secara otomatis mengeksekusi mknod <TARGET> c 0 0 di direktori modul yang sesuai. Misalnya:

sh
HAPUS = "
-/sistem/aplikasi/YouTube
-/system/app/Bloatware
-"

Daftar di atas akan mengeksekusi mknod $MODPATH/system/app/YouTuBe c 0 0 dan mknod $MODPATH/system/app/Bloatware c 0 0; dan /system/app/YouTube dan /system/app/Bloatware akan dihapus setelah modul berlaku.

Jika Anda ingin mengganti direktori di sistem, Anda perlu membuat direktori dengan jalur yang sama di direktori modul Anda, lalu atur atribut setfattr -n trusted.overlay.opaque -v y <TARGET> untuk direktori ini. Dengan cara ini, sistem overlayfs akan secara otomatis mengganti direktori terkait di sistem (tanpa mengubah partisi /sistem).

Anda dapat mendeklarasikan variabel bernama REPLACE di file customize.sh Anda, yang menyertakan daftar direktori yang akan diganti, dan KernelSU akan secara otomatis melakukan operasi yang sesuai di direktori modul Anda. Misalnya:

REPLACE=" /system/app/YouTube /system/app/Bloatware "

Daftar ini akan secara otomatis membuat direktori $MODPATH/system/app/YouTube dan $MODPATH/system/app/Bloatware, lalu jalankan setfattr -n trusted.overlay.opaque -v y $MODPATH/system/app/ YouTube dan setfattr -n trusted.overlay.opaque -v y $MODPATH/system/app/Bloatware. Setelah modul berlaku, /system/app/YouTube dan /system/app/Bloatware akan diganti dengan direktori kosong.

::: perbedaan tip dengan Magisk

Mekanisme tanpa sistem KernelSU diimplementasikan melalui overlay kernel, sementara Magisk saat ini menggunakan magic mount (bind mount). Kedua metode implementasi tersebut memiliki perbedaan yang signifikan, tetapi tujuan utamanya sama: untuk memodifikasi file / sistem tanpa memodifikasi partisi / sistem secara fisik. :::

Jika Anda tertarik dengan overlayfs, disarankan untuk membaca dokumentasi overlayfs Kernel Linux.

system.prop

File ini mengikuti format yang sama dengan build.prop. Setiap baris terdiri dari [kunci]=[nilai].

sepolicy.rule

Jika modul Anda memerlukan beberapa tambalan sepolicy tambahan, harap tambahkan aturan tersebut ke dalam file ini. Setiap baris dalam file ini akan diperlakukan sebagai pernyataan kebijakan.

Pemasangan module

Penginstal modul KernelSU adalah modul KernelSU yang dikemas dalam file zip yang dapat di-flash di aplikasi pengelola KernelSU. Pemasang modul KernelSU yang paling sederhana hanyalah modul KernelSU yang dikemas sebagai file zip.

txt
module.zip
-
-├── customize.sh                       <--- (Optional, more details later)
-│                                           This script will be sourced by update-binary
-├── ...
-├── ...  /* The rest of module's files */
-

:::peringatan Modul KernelSU TIDAK didukung untuk diinstal dalam pemulihan kustom!! :::

Kostumisasi

Jika Anda perlu menyesuaikan proses penginstalan modul, secara opsional Anda dapat membuat skrip di penginstal bernama customize.sh. Skrip ini akan sourced (tidak dijalankan!) oleh skrip penginstal modul setelah semua file diekstrak dan izin default serta konteks sekon diterapkan. Ini sangat berguna jika modul Anda memerlukan penyiapan tambahan berdasarkan ABI perangkat, atau Anda perlu menyetel izin khusus/konteks kedua untuk beberapa file modul Anda.

Jika Anda ingin sepenuhnya mengontrol dan menyesuaikan proses penginstalan, nyatakan SKIPUNZIP=1 di customize.sh untuk melewati semua langkah penginstalan default. Dengan melakukannya, customize.sh Anda akan bertanggung jawab untuk menginstal semuanya dengan sendirinya.

Skrip customize.sh berjalan di shell BusyBox ash KernelSU dengan "Mode Mandiri" diaktifkan. Variabel dan fungsi berikut tersedia:

Variable

  • KSU (bool): variabel untuk menandai bahwa skrip berjalan di lingkungan KernelSU, dan nilai variabel ini akan selalu benar. Anda dapat menggunakannya untuk membedakan antara KernelSU dan Magisk.
  • KSU_VER (string): string versi dari KernelSU yang diinstal saat ini (mis. v0.4.0)
  • KSU_VER_CODE (int): kode versi KernelSU yang terpasang saat ini di ruang pengguna (mis. 10672)
  • KSU_KERNEL_VER_CODE (int): kode versi KernelSU yang terpasang saat ini di ruang kernel (mis. 10672)
  • BOOTMODE (bool): selalu true di KernelSU
  • MODPATH (jalur): jalur tempat file modul Anda harus diinstal
  • TMPDIR (jalur): tempat di mana Anda dapat menyimpan file untuk sementara
  • ZIPFILE (jalur): zip instalasi modul Anda
  • ARCH (string): arsitektur CPU perangkat. Nilainya adalah arm, arm64, x86, atau x64
  • IS64BIT (bool): true jika $ARCH adalah arm64 atau x64
  • API (int): level API (versi Android) perangkat (mis. 23 untuk Android 6.0)

::: peringatan Di KernelSU, MAGISK_VER_CODE selalu 25200 dan MAGISK_VER selalu v25.2. Tolong jangan gunakan kedua variabel ini untuk menentukan apakah itu berjalan di KernelSU atau tidak. :::

Fungsi

txt
ui_print <msg>
-    print <msg> to console
-    Avoid using 'echo' as it will not display in custom recovery's console
-
-abort <msg>
-    print error message <msg> to console and terminate the installation
-    Avoid using 'exit' as it will skip the termination cleanup steps
-
-set_perm <target> <owner> <group> <permission> [context]
-    if [context] is not set, the default is "u:object_r:system_file:s0"
-    this function is a shorthand for the following commands:
-       chown owner.group target
-       chmod permission target
-       chcon context target
-
-set_perm_recursive <directory> <owner> <group> <dirpermission> <filepermission> [context]
-    if [context] is not set, the default is "u:object_r:system_file:s0"
-    for all files in <directory>, it will call:
-       set_perm file owner group filepermission context
-    for all directories in <directory> (including itself), it will call:
-       set_perm dir owner group dirpermission context

Boot scripts

Di KernelSU, skrip dibagi menjadi dua jenis berdasarkan mode operasinya: mode post-fs-data dan mode layanan late_start:

  • mode pasca-fs-data
    • Tahap ini adalah BLOKIR. Proses boot dijeda sebelum eksekusi selesai, atau 10 detik telah berlalu.
    • Skrip dijalankan sebelum modul apa pun dipasang. Ini memungkinkan pengembang modul untuk menyesuaikan modul mereka secara dinamis sebelum dipasang.
    • Tahap ini terjadi sebelum Zygote dimulai, yang berarti segalanya di Android
    • PERINGATAN: menggunakan setprop akan menghentikan proses booting! Silakan gunakan resetprop -n <prop_name> <prop_value> sebagai gantinya.
    • Hanya jalankan skrip dalam mode ini jika perlu.
  • mode layanan late_start
    • Tahap ini NON-BLOCKING. Skrip Anda berjalan paralel dengan proses booting lainnya.
    • Ini adalah tahap yang disarankan untuk menjalankan sebagian besar skrip.

Di KernelSU, skrip startup dibagi menjadi dua jenis berdasarkan lokasi penyimpanannya: skrip umum dan skrip modul:

  • Skrip Umum
    • Ditempatkan di /data/adb/post-fs-data.d atau /data/adb/service.d
    • Hanya dieksekusi jika skrip disetel sebagai dapat dieksekusi (chmod +x script.sh)
    • Skrip di post-fs-data.d berjalan dalam mode post-fs-data, dan skrip di service.d berjalan di mode layanan late_start.
    • Modul seharusnya TIDAK menambahkan skrip umum selama instalasi
  • Skrip Modul
    • Ditempatkan di folder modul itu sendiri
    • Hanya dijalankan jika modul diaktifkan
    • post-fs-data.sh berjalan dalam mode post-fs-data, dan service.sh berjalan dalam mode layanan late_start.

Semua skrip boot akan berjalan di shell BusyBox ash KernelSU dengan "Mode Mandiri" diaktifkan.

`,60),o=[l];function t(d,p,r,c,u,m){return e(),n("div",null,o)}const y=a(i,[["render",t]]);export{g as __pageData,y as default}; +import{_ as a,o as e,c as s,Q as n}from"./chunks/framework.ec8f7e8e.js";const g=JSON.parse('{"title":"Panduan module","description":"","frontmatter":{},"headers":[],"relativePath":"id_ID/guide/module.md","filePath":"id_ID/guide/module.md"}'),l={name:"id_ID/guide/module.md"},i=n(`

Panduan module

KernelSU menyediakan mekanisme modul yang mencapai efek memodifikasi direktori sistem dengan tetap menjaga integritas partisi sistem. Mekanisme ini umumnya dikenal sebagai "tanpa sistem".

Mekanisme modul KernelSU hampir sama dengan Magisk. Jika Anda terbiasa dengan pengembangan modul Magisk, mengembangkan modul KernelSU sangat mirip. Anda dapat melewati pengenalan modul di bawah ini dan hanya perlu membaca difference-with-magisk.

Busybox

KernelSU dikirimkan dengan fitur biner BusyBox yang lengkap (termasuk dukungan penuh SELinux). Eksekusi terletak di /data/adb/ksu/bin/busybox. BusyBox KernelSU mendukung "Mode Shell Standalone Shell" yang dapat dialihkan waktu proses. Apa yang dimaksud dengan mode mandiri ini adalah bahwa ketika dijalankan di shell ash dari BusyBox, setiap perintah akan langsung menggunakan applet di dalam BusyBox, terlepas dari apa yang ditetapkan sebagai PATH. Misalnya, perintah seperti ls, rm, chmod TIDAK akan menggunakan apa yang ada di PATH (dalam kasus Android secara default akan menjadi /system/bin/ls, /system/bin/rm, dan /system/bin/chmod masing-masing), tetapi akan langsung memanggil applet BusyBox internal. Ini memastikan bahwa skrip selalu berjalan di lingkungan yang dapat diprediksi dan selalu memiliki rangkaian perintah lengkap, apa pun versi Android yang menjalankannya. Untuk memaksa perintah not menggunakan BusyBox, Anda harus memanggil yang dapat dieksekusi dengan path lengkap.

Setiap skrip shell tunggal yang berjalan dalam konteks KernelSU akan dieksekusi di shell ash BusyBox dengan mode mandiri diaktifkan. Untuk apa yang relevan dengan pengembang pihak ke-3, ini termasuk semua skrip boot dan skrip instalasi modul.

Bagi yang ingin menggunakan fitur “Standalone Mode” ini di luar KernelSU, ada 2 cara untuk mengaktifkannya:

  1. Tetapkan variabel lingkungan ASH_STANDALONE ke 1
    Contoh: ASH_STANDALONE=1 /data/adb/ksu/bin/busybox sh <script>
  2. Beralih dengan opsi baris perintah:
    /data/adb/ksu/bin/busybox sh -o mandiri <script>

Untuk memastikan semua shell sh selanjutnya dijalankan juga dalam mode mandiri, opsi 1 adalah metode yang lebih disukai (dan inilah yang digunakan secara internal oleh KernelSU dan manajer KernelSU) karena variabel lingkungan diwariskan ke proses anak.

::: perbedaan tip dengan Magisk

BusyBox KernelSU sekarang menggunakan file biner yang dikompilasi langsung dari proyek Magisk. Berkat Magisk! Oleh karena itu, Anda tidak perlu khawatir tentang masalah kompatibilitas antara skrip BusyBox di Magisk dan KernelSU karena keduanya persis sama! :::

KernelSU module

Modul KernelSU adalah folder yang ditempatkan di /data/adb/modules dengan struktur di bawah ini:

txt
/data/adb/modules
+├── .
+├── .
+|
+├── $MODID                  <--- The folder is named with the ID of the module
+│   │
+│   │      *** Module Identity ***
+│   │
+│   ├── module.prop         <--- This file stores the metadata of the module
+│   │
+│   │      *** Main Contents ***
+│   │
+│   ├── system              <--- This folder will be mounted if skip_mount does not exist
+│   │   ├── ...
+│   │   ├── ...
+│   │   └── ...
+│   │
+│   │      *** Status Flags ***
+│   │
+│   ├── skip_mount          <--- If exists, KernelSU will NOT mount your system folder
+│   ├── disable             <--- If exists, the module will be disabled
+│   ├── remove              <--- If exists, the module will be removed next reboot
+│   │
+│   │      *** Optional Files ***
+│   │
+│   ├── post-fs-data.sh     <--- This script will be executed in post-fs-data
+│   ├── service.sh          <--- This script will be executed in late_start service
+|   ├── uninstall.sh        <--- This script will be executed when KernelSU removes your module
+│   ├── system.prop         <--- Properties in this file will be loaded as system properties by resetprop
+│   ├── sepolicy.rule       <--- Additional custom sepolicy rules
+│   │
+│   │      *** Auto Generated, DO NOT MANUALLY CREATE OR MODIFY ***
+│   │
+│   ├── vendor              <--- A symlink to $MODID/system/vendor
+│   ├── product             <--- A symlink to $MODID/system/product
+│   ├── system_ext          <--- A symlink to $MODID/system/system_ext
+│   │
+│   │      *** Any additional files / folders are allowed ***
+│   │
+│   ├── ...
+│   └── ...
+|
+├── another_module
+│   ├── .
+│   └── .
+├── .
+├── .
/data/adb/modules
+├── .
+├── .
+|
+├── $MODID                  <--- The folder is named with the ID of the module
+│   │
+│   │      *** Module Identity ***
+│   │
+│   ├── module.prop         <--- This file stores the metadata of the module
+│   │
+│   │      *** Main Contents ***
+│   │
+│   ├── system              <--- This folder will be mounted if skip_mount does not exist
+│   │   ├── ...
+│   │   ├── ...
+│   │   └── ...
+│   │
+│   │      *** Status Flags ***
+│   │
+│   ├── skip_mount          <--- If exists, KernelSU will NOT mount your system folder
+│   ├── disable             <--- If exists, the module will be disabled
+│   ├── remove              <--- If exists, the module will be removed next reboot
+│   │
+│   │      *** Optional Files ***
+│   │
+│   ├── post-fs-data.sh     <--- This script will be executed in post-fs-data
+│   ├── service.sh          <--- This script will be executed in late_start service
+|   ├── uninstall.sh        <--- This script will be executed when KernelSU removes your module
+│   ├── system.prop         <--- Properties in this file will be loaded as system properties by resetprop
+│   ├── sepolicy.rule       <--- Additional custom sepolicy rules
+│   │
+│   │      *** Auto Generated, DO NOT MANUALLY CREATE OR MODIFY ***
+│   │
+│   ├── vendor              <--- A symlink to $MODID/system/vendor
+│   ├── product             <--- A symlink to $MODID/system/product
+│   ├── system_ext          <--- A symlink to $MODID/system/system_ext
+│   │
+│   │      *** Any additional files / folders are allowed ***
+│   │
+│   ├── ...
+│   └── ...
+|
+├── another_module
+│   ├── .
+│   └── .
+├── .
+├── .

::: perbedaan tip dengan Magisk KernelSU tidak memiliki dukungan bawaan untuk Zygisk, jadi tidak ada konten terkait Zygisk dalam modul. Namun, Anda dapat menggunakan ZygiskNext untuk mendukung modul Zygisk. Dalam hal ini, konten modul Zygisk identik dengan yang didukung oleh Magisk. :::

module.prop

module.prop adalah file konfigurasi untuk sebuah modul. Di KernelSU, jika modul tidak berisi file ini, maka tidak akan dikenali sebagai modul. Format file ini adalah sebagai berikut:

txt
id=<string>
+name=<string>
+version=<string>
+versioncode=<int>
+author=<string>
+description=<string>
id=<string>
+name=<string>
+version=<string>
+versioncode=<int>
+author=<string>
+description=<string>
  • id harus cocok dengan ekspresi reguler ini: ^[a-zA-Z][a-zA-Z0-9._-]+$
    contoh: ✓ a_module, ✓ a.module, ✓ module-101, ✗ a module, ✗ 1_module, ✗ -a-module
    Ini adalah pengidentifikasi unik modul Anda. Anda tidak boleh mengubahnya setelah dipublikasikan.
  • versionCode harus berupa integer. Ini digunakan untuk membandingkan versi
  • Lainnya yang tidak disebutkan di atas dapat berupa string satu baris.
  • Pastikan untuk menggunakan tipe jeda baris UNIX (LF) dan bukan Windows (CR+LF) atau Macintosh (CR).

Shell skrip

Harap baca bagian Boot Scripts untuk memahami perbedaan antara post-fs-data.sh dan service.sh. Untuk sebagian besar pengembang modul, service.sh sudah cukup baik jika Anda hanya perlu menjalankan skrip boot.

Di semua skrip modul Anda, harap gunakan MODDIR=\${0%/*} untuk mendapatkan jalur direktori dasar modul Anda; lakukan TIDAK hardcode jalur modul Anda dalam skrip.

::: perbedaan tip dengan Magisk Anda dapat menggunakan variabel lingkungan KSU untuk menentukan apakah skrip berjalan di KernelSU atau Magisk. Jika berjalan di KernelSU, nilai ini akan disetel ke true. :::

system directory

Isi direktori ini akan dihamparkan di atas partisi sistem /sistem menggunakan overlayfs setelah sistem di-boot. Ini berarti bahwa:

  1. File dengan nama yang sama dengan yang ada di direktori terkait di sistem akan ditimpa oleh file di direktori ini.
  2. Folder dengan nama yang sama dengan yang ada di direktori terkait di sistem akan digabungkan dengan folder di direktori ini.

Jika Anda ingin menghapus file atau folder di direktori sistem asli, Anda perlu membuat file dengan nama yang sama dengan file/folder di direktori modul menggunakan mknod filename c 0 0. Dengan cara ini, sistem overlayfs akan secara otomatis "memutihkan" file ini seolah-olah telah dihapus (partisi / sistem sebenarnya tidak diubah).

Anda juga dapat mendeklarasikan variabel bernama REMOVE yang berisi daftar direktori di customize.sh untuk menjalankan operasi penghapusan, dan KernelSU akan secara otomatis mengeksekusi mknod <TARGET> c 0 0 di direktori modul yang sesuai. Misalnya:

sh
HAPUS = "
+/sistem/aplikasi/YouTube
+/system/app/Bloatware
+"
HAPUS = "
+/sistem/aplikasi/YouTube
+/system/app/Bloatware
+"

Daftar di atas akan mengeksekusi mknod $MODPATH/system/app/YouTuBe c 0 0 dan mknod $MODPATH/system/app/Bloatware c 0 0; dan /system/app/YouTube dan /system/app/Bloatware akan dihapus setelah modul berlaku.

Jika Anda ingin mengganti direktori di sistem, Anda perlu membuat direktori dengan jalur yang sama di direktori modul Anda, lalu atur atribut setfattr -n trusted.overlay.opaque -v y <TARGET> untuk direktori ini. Dengan cara ini, sistem overlayfs akan secara otomatis mengganti direktori terkait di sistem (tanpa mengubah partisi /sistem).

Anda dapat mendeklarasikan variabel bernama REPLACE di file customize.sh Anda, yang menyertakan daftar direktori yang akan diganti, dan KernelSU akan secara otomatis melakukan operasi yang sesuai di direktori modul Anda. Misalnya:

REPLACE=" /system/app/YouTube /system/app/Bloatware "

Daftar ini akan secara otomatis membuat direktori $MODPATH/system/app/YouTube dan $MODPATH/system/app/Bloatware, lalu jalankan setfattr -n trusted.overlay.opaque -v y $MODPATH/system/app/ YouTube dan setfattr -n trusted.overlay.opaque -v y $MODPATH/system/app/Bloatware. Setelah modul berlaku, /system/app/YouTube dan /system/app/Bloatware akan diganti dengan direktori kosong.

::: perbedaan tip dengan Magisk

Mekanisme tanpa sistem KernelSU diimplementasikan melalui overlay kernel, sementara Magisk saat ini menggunakan magic mount (bind mount). Kedua metode implementasi tersebut memiliki perbedaan yang signifikan, tetapi tujuan utamanya sama: untuk memodifikasi file / sistem tanpa memodifikasi partisi / sistem secara fisik. :::

Jika Anda tertarik dengan overlayfs, disarankan untuk membaca dokumentasi overlayfs Kernel Linux.

system.prop

File ini mengikuti format yang sama dengan build.prop. Setiap baris terdiri dari [kunci]=[nilai].

sepolicy.rule

Jika modul Anda memerlukan beberapa tambalan sepolicy tambahan, harap tambahkan aturan tersebut ke dalam file ini. Setiap baris dalam file ini akan diperlakukan sebagai pernyataan kebijakan.

Pemasangan module

Penginstal modul KernelSU adalah modul KernelSU yang dikemas dalam file zip yang dapat di-flash di aplikasi pengelola KernelSU. Pemasang modul KernelSU yang paling sederhana hanyalah modul KernelSU yang dikemas sebagai file zip.

txt
module.zip
+
+├── customize.sh                       <--- (Optional, more details later)
+│                                           This script will be sourced by update-binary
+├── ...
+├── ...  /* The rest of module's files */
+
module.zip
+
+├── customize.sh                       <--- (Optional, more details later)
+│                                           This script will be sourced by update-binary
+├── ...
+├── ...  /* The rest of module's files */
+

:::peringatan Modul KernelSU TIDAK didukung untuk diinstal dalam pemulihan kustom!! :::

Kostumisasi

Jika Anda perlu menyesuaikan proses penginstalan modul, secara opsional Anda dapat membuat skrip di penginstal bernama customize.sh. Skrip ini akan sourced (tidak dijalankan!) oleh skrip penginstal modul setelah semua file diekstrak dan izin default serta konteks sekon diterapkan. Ini sangat berguna jika modul Anda memerlukan penyiapan tambahan berdasarkan ABI perangkat, atau Anda perlu menyetel izin khusus/konteks kedua untuk beberapa file modul Anda.

Jika Anda ingin sepenuhnya mengontrol dan menyesuaikan proses penginstalan, nyatakan SKIPUNZIP=1 di customize.sh untuk melewati semua langkah penginstalan default. Dengan melakukannya, customize.sh Anda akan bertanggung jawab untuk menginstal semuanya dengan sendirinya.

Skrip customize.sh berjalan di shell BusyBox ash KernelSU dengan "Mode Mandiri" diaktifkan. Variabel dan fungsi berikut tersedia:

Variable

  • KSU (bool): variabel untuk menandai bahwa skrip berjalan di lingkungan KernelSU, dan nilai variabel ini akan selalu benar. Anda dapat menggunakannya untuk membedakan antara KernelSU dan Magisk.
  • KSU_VER (string): string versi dari KernelSU yang diinstal saat ini (mis. v0.4.0)
  • KSU_VER_CODE (int): kode versi KernelSU yang terpasang saat ini di ruang pengguna (mis. 10672)
  • KSU_KERNEL_VER_CODE (int): kode versi KernelSU yang terpasang saat ini di ruang kernel (mis. 10672)
  • BOOTMODE (bool): selalu true di KernelSU
  • MODPATH (jalur): jalur tempat file modul Anda harus diinstal
  • TMPDIR (jalur): tempat di mana Anda dapat menyimpan file untuk sementara
  • ZIPFILE (jalur): zip instalasi modul Anda
  • ARCH (string): arsitektur CPU perangkat. Nilainya adalah arm, arm64, x86, atau x64
  • IS64BIT (bool): true jika $ARCH adalah arm64 atau x64
  • API (int): level API (versi Android) perangkat (mis. 23 untuk Android 6.0)

::: peringatan Di KernelSU, MAGISK_VER_CODE selalu 25200 dan MAGISK_VER selalu v25.2. Tolong jangan gunakan kedua variabel ini untuk menentukan apakah itu berjalan di KernelSU atau tidak. :::

Fungsi

txt
ui_print <msg>
+    print <msg> to console
+    Avoid using 'echo' as it will not display in custom recovery's console
+
+abort <msg>
+    print error message <msg> to console and terminate the installation
+    Avoid using 'exit' as it will skip the termination cleanup steps
+
+set_perm <target> <owner> <group> <permission> [context]
+    if [context] is not set, the default is "u:object_r:system_file:s0"
+    this function is a shorthand for the following commands:
+       chown owner.group target
+       chmod permission target
+       chcon context target
+
+set_perm_recursive <directory> <owner> <group> <dirpermission> <filepermission> [context]
+    if [context] is not set, the default is "u:object_r:system_file:s0"
+    for all files in <directory>, it will call:
+       set_perm file owner group filepermission context
+    for all directories in <directory> (including itself), it will call:
+       set_perm dir owner group dirpermission context
ui_print <msg>
+    print <msg> to console
+    Avoid using 'echo' as it will not display in custom recovery's console
+
+abort <msg>
+    print error message <msg> to console and terminate the installation
+    Avoid using 'exit' as it will skip the termination cleanup steps
+
+set_perm <target> <owner> <group> <permission> [context]
+    if [context] is not set, the default is "u:object_r:system_file:s0"
+    this function is a shorthand for the following commands:
+       chown owner.group target
+       chmod permission target
+       chcon context target
+
+set_perm_recursive <directory> <owner> <group> <dirpermission> <filepermission> [context]
+    if [context] is not set, the default is "u:object_r:system_file:s0"
+    for all files in <directory>, it will call:
+       set_perm file owner group filepermission context
+    for all directories in <directory> (including itself), it will call:
+       set_perm dir owner group dirpermission context

Boot scripts

Di KernelSU, skrip dibagi menjadi dua jenis berdasarkan mode operasinya: mode post-fs-data dan mode layanan late_start:

  • mode pasca-fs-data
    • Tahap ini adalah BLOKIR. Proses boot dijeda sebelum eksekusi selesai, atau 10 detik telah berlalu.
    • Skrip dijalankan sebelum modul apa pun dipasang. Ini memungkinkan pengembang modul untuk menyesuaikan modul mereka secara dinamis sebelum dipasang.
    • Tahap ini terjadi sebelum Zygote dimulai, yang berarti segalanya di Android
    • PERINGATAN: menggunakan setprop akan menghentikan proses booting! Silakan gunakan resetprop -n <prop_name> <prop_value> sebagai gantinya.
    • Hanya jalankan skrip dalam mode ini jika perlu.
  • mode layanan late_start
    • Tahap ini NON-BLOCKING. Skrip Anda berjalan paralel dengan proses booting lainnya.
    • Ini adalah tahap yang disarankan untuk menjalankan sebagian besar skrip.

Di KernelSU, skrip startup dibagi menjadi dua jenis berdasarkan lokasi penyimpanannya: skrip umum dan skrip modul:

  • Skrip Umum
    • Ditempatkan di /data/adb/post-fs-data.d atau /data/adb/service.d
    • Hanya dieksekusi jika skrip disetel sebagai dapat dieksekusi (chmod +x script.sh)
    • Skrip di post-fs-data.d berjalan dalam mode post-fs-data, dan skrip di service.d berjalan di mode layanan late_start.
    • Modul seharusnya TIDAK menambahkan skrip umum selama instalasi
  • Skrip Modul
    • Ditempatkan di folder modul itu sendiri
    • Hanya dijalankan jika modul diaktifkan
    • post-fs-data.sh berjalan dalam mode post-fs-data, dan service.sh berjalan dalam mode layanan late_start.

Semua skrip boot akan berjalan di shell BusyBox ash KernelSU dengan "Mode Mandiri" diaktifkan.

`,60),o=[i];function t(p,r,d,c,u,m){return e(),s("div",null,o)}const y=a(l,[["render",t]]);export{g as __pageData,y as default}; diff --git a/assets/id_ID_guide_module.md.068bf86d.lean.js b/assets/id_ID_guide_module.md.068bf86d.lean.js new file mode 100644 index 000000000000..4a4d1c2aa1d1 --- /dev/null +++ b/assets/id_ID_guide_module.md.068bf86d.lean.js @@ -0,0 +1 @@ +import{_ as a,o as e,c as s,Q as n}from"./chunks/framework.ec8f7e8e.js";const g=JSON.parse('{"title":"Panduan module","description":"","frontmatter":{},"headers":[],"relativePath":"id_ID/guide/module.md","filePath":"id_ID/guide/module.md"}'),l={name:"id_ID/guide/module.md"},i=n("",60),o=[i];function t(p,r,d,c,u,m){return e(),s("div",null,o)}const y=a(l,[["render",t]]);export{g as __pageData,y as default}; diff --git a/assets/id_ID_guide_module.md.a65f7310.lean.js b/assets/id_ID_guide_module.md.a65f7310.lean.js deleted file mode 100644 index 65e22586fbef..000000000000 --- a/assets/id_ID_guide_module.md.a65f7310.lean.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as a,o as e,c as n,O as s}from"./chunks/framework.43781440.js";const g=JSON.parse('{"title":"Panduan module","description":"","frontmatter":{},"headers":[],"relativePath":"id_ID/guide/module.md","filePath":"id_ID/guide/module.md"}'),i={name:"id_ID/guide/module.md"},l=s("",60),o=[l];function t(d,p,r,c,u,m){return e(),n("div",null,o)}const y=a(i,[["render",t]]);export{g as __pageData,y as default}; diff --git a/assets/id_ID_guide_rescue-from-bootloop.md.ec3ef7ee.js b/assets/id_ID_guide_rescue-from-bootloop.md.5cce45dd.js similarity index 98% rename from assets/id_ID_guide_rescue-from-bootloop.md.ec3ef7ee.js rename to assets/id_ID_guide_rescue-from-bootloop.md.5cce45dd.js index b170e461cbb8..7652abef79e8 100644 --- a/assets/id_ID_guide_rescue-from-bootloop.md.ec3ef7ee.js +++ b/assets/id_ID_guide_rescue-from-bootloop.md.5cce45dd.js @@ -1 +1 @@ -import{_ as a,o as e,c as n,O as i}from"./chunks/framework.43781440.js";const g=JSON.parse('{"title":"Recovery dari bootloop","description":"","frontmatter":{},"headers":[],"relativePath":"id_ID/guide/rescue-from-bootloop.md","filePath":"id_ID/guide/rescue-from-bootloop.md"}'),t={name:"id_ID/guide/rescue-from-bootloop.md"},m=i('

Recovery dari bootloop

Saat mem-flash perangkat, kami mungkin menghadapi situasi di mana perangkat menjadi "bata". Secara teori, jika Anda hanya menggunakan fastboot untuk mem-flash partisi boot atau menginstal modul yang tidak sesuai yang menyebabkan perangkat gagal melakukan booting, ini dapat dipulihkan dengan operasi yang sesuai. Dokumen ini bertujuan untuk memberikan beberapa metode darurat untuk membantu Anda pulih dari perangkat "bricked".

Brick saat memflash partisi boot

Di KernelSU, situasi berikut dapat menyebabkan bata boot saat mem-flash partisi boot:

  1. Anda mem-flash image boot dalam format yang salah. Misalnya, jika format booting ponsel Anda adalah gz, tetapi Anda mem-flash image berformat lz4, maka ponsel tidak akan dapat melakukan booting.
  2. Ponsel Anda perlu menonaktifkan verifikasi AVB agar dapat boot dengan benar (biasanya perlu menghapus semua data di ponsel).
  3. Kernel Anda memiliki beberapa bug atau tidak cocok untuk flash ponsel Anda.

Apa pun situasinya, Anda dapat memulihkannya dengan mem-flash gambar boot stok. Oleh karena itu, di awal tutorial instalasi, kami sangat menyarankan Anda untuk mem-backup stock boot Anda sebelum melakukan flashing. Jika Anda belum mencadangkan, Anda dapat memperoleh boot pabrik asli dari pengguna lain dengan perangkat yang sama dengan Anda atau dari firmware resmi.

Brick disebabkan modul

Memasang modul dapat menjadi penyebab yang lebih umum dari bricking perangkat Anda, tetapi kami harus memperingatkan Anda dengan serius: Jangan memasang modul dari sumber yang tidak dikenal! Karena modul memiliki hak akses root, mereka berpotensi menyebabkan kerusakan permanen pada perangkat Anda!

Module normal

Jika Anda telah mem-flash modul yang telah terbukti aman tetapi menyebabkan perangkat Anda gagal booting, maka situasi ini dapat dipulihkan dengan mudah di KernelSU tanpa rasa khawatir. KernelSU memiliki mekanisme bawaan untuk menyelamatkan perangkat Anda, termasuk yang berikut:

  1. Pembaruan AB
  2. Selamatkan dengan menekan Volume Turun

Pembaruan AB

Pembaruan modul KernelSU menarik inspirasi dari mekanisme pembaruan AB sistem Android yang digunakan dalam pembaruan OTA. Jika Anda menginstal modul baru atau memperbarui modul yang sudah ada, itu tidak akan langsung mengubah file modul yang sedang digunakan. Sebagai gantinya, semua modul akan dibangun ke gambar pembaruan lainnya. Setelah sistem dimulai ulang, sistem akan mencoba untuk mulai menggunakan gambar pembaruan ini. Jika sistem Android berhasil melakukan booting, modul akan benar-benar diperbarui.

Oleh karena itu, metode paling sederhana dan paling umum digunakan untuk menyelamatkan perangkat Anda adalah dengan memaksa reboot. Jika Anda tidak dapat memulai sistem Anda setelah mem-flash modul, Anda dapat menekan dan menahan tombol daya selama lebih dari 10 detik, dan sistem akan melakukan reboot secara otomatis; setelah mem-boot ulang, itu akan kembali ke keadaan sebelum memperbarui modul, dan modul yang diperbarui sebelumnya akan dinonaktifkan secara otomatis.

Recovery dengan menekan Volume Bawah

Jika pembaruan AB masih tidak dapat menyelesaikan masalah, Anda dapat mencoba menggunakan Safe Mode. Dalam Safe Mode, semua modul dinonaktifkan.

Ada dua cara untuk masuk ke Safe Mode:

  1. Mode Aman bawaan dari beberapa sistem; beberapa sistem memiliki Safe Mode bawaan yang dapat diakses dengan menekan lama tombol volume turun, sementara yang lain (seperti MIUI) dapat mengaktifkan Safe Mode di Recovery. Saat memasuki Safe Mode sistem, KernelSU juga akan masuk ke Safe Mode dan secara otomatis menonaktifkan modul.
  2. Safe Mode bawaan dari KernelSU; metode pengoperasiannya adalah tekan tombol volume turun secara terus-menerus selama lebih dari tiga kali setelah layar boot pertama. Perhatikan bahwa ini adalah rilis pers, rilis pers, rilis pers, bukan tekan dan tahan.

Setelah memasuki mode aman, semua modul pada halaman modul KernelSU Manager dinonaktifkan, tetapi Anda dapat melakukan operasi "uninstall" untuk menghapus semua modul yang mungkin menyebabkan masalah.

Mode aman bawaan diimplementasikan di kernel, jadi tidak ada kemungkinan peristiwa penting yang hilang karena intersepsi. Namun, untuk kernel non-GKI, integrasi kode secara manual mungkin diperlukan, dan Anda dapat merujuk ke dokumentasi resmi untuk mendapatkan panduan.

Module berbahaya

Jika metode di atas tidak dapat menyelamatkan perangkat Anda, kemungkinan besar modul yang Anda instal memiliki operasi jahat atau telah merusak perangkat Anda melalui cara lain. Dalam hal ini, hanya ada dua saran:

  1. Hapus data dan flash sistem resmi.
  2. Konsultasikan layanan purna jual.
',23),o=[m];function r(d,l,u,s,k,b){return e(),n("div",null,o)}const h=a(t,[["render",r]]);export{g as __pageData,h as default}; +import{_ as a,o as e,c as n,Q as i}from"./chunks/framework.ec8f7e8e.js";const g=JSON.parse('{"title":"Recovery dari bootloop","description":"","frontmatter":{},"headers":[],"relativePath":"id_ID/guide/rescue-from-bootloop.md","filePath":"id_ID/guide/rescue-from-bootloop.md"}'),t={name:"id_ID/guide/rescue-from-bootloop.md"},m=i('

Recovery dari bootloop

Saat mem-flash perangkat, kami mungkin menghadapi situasi di mana perangkat menjadi "bata". Secara teori, jika Anda hanya menggunakan fastboot untuk mem-flash partisi boot atau menginstal modul yang tidak sesuai yang menyebabkan perangkat gagal melakukan booting, ini dapat dipulihkan dengan operasi yang sesuai. Dokumen ini bertujuan untuk memberikan beberapa metode darurat untuk membantu Anda pulih dari perangkat "bricked".

Brick saat memflash partisi boot

Di KernelSU, situasi berikut dapat menyebabkan bata boot saat mem-flash partisi boot:

  1. Anda mem-flash image boot dalam format yang salah. Misalnya, jika format booting ponsel Anda adalah gz, tetapi Anda mem-flash image berformat lz4, maka ponsel tidak akan dapat melakukan booting.
  2. Ponsel Anda perlu menonaktifkan verifikasi AVB agar dapat boot dengan benar (biasanya perlu menghapus semua data di ponsel).
  3. Kernel Anda memiliki beberapa bug atau tidak cocok untuk flash ponsel Anda.

Apa pun situasinya, Anda dapat memulihkannya dengan mem-flash gambar boot stok. Oleh karena itu, di awal tutorial instalasi, kami sangat menyarankan Anda untuk mem-backup stock boot Anda sebelum melakukan flashing. Jika Anda belum mencadangkan, Anda dapat memperoleh boot pabrik asli dari pengguna lain dengan perangkat yang sama dengan Anda atau dari firmware resmi.

Brick disebabkan modul

Memasang modul dapat menjadi penyebab yang lebih umum dari bricking perangkat Anda, tetapi kami harus memperingatkan Anda dengan serius: Jangan memasang modul dari sumber yang tidak dikenal! Karena modul memiliki hak akses root, mereka berpotensi menyebabkan kerusakan permanen pada perangkat Anda!

Module normal

Jika Anda telah mem-flash modul yang telah terbukti aman tetapi menyebabkan perangkat Anda gagal booting, maka situasi ini dapat dipulihkan dengan mudah di KernelSU tanpa rasa khawatir. KernelSU memiliki mekanisme bawaan untuk menyelamatkan perangkat Anda, termasuk yang berikut:

  1. Pembaruan AB
  2. Selamatkan dengan menekan Volume Turun

Pembaruan AB

Pembaruan modul KernelSU menarik inspirasi dari mekanisme pembaruan AB sistem Android yang digunakan dalam pembaruan OTA. Jika Anda menginstal modul baru atau memperbarui modul yang sudah ada, itu tidak akan langsung mengubah file modul yang sedang digunakan. Sebagai gantinya, semua modul akan dibangun ke gambar pembaruan lainnya. Setelah sistem dimulai ulang, sistem akan mencoba untuk mulai menggunakan gambar pembaruan ini. Jika sistem Android berhasil melakukan booting, modul akan benar-benar diperbarui.

Oleh karena itu, metode paling sederhana dan paling umum digunakan untuk menyelamatkan perangkat Anda adalah dengan memaksa reboot. Jika Anda tidak dapat memulai sistem Anda setelah mem-flash modul, Anda dapat menekan dan menahan tombol daya selama lebih dari 10 detik, dan sistem akan melakukan reboot secara otomatis; setelah mem-boot ulang, itu akan kembali ke keadaan sebelum memperbarui modul, dan modul yang diperbarui sebelumnya akan dinonaktifkan secara otomatis.

Recovery dengan menekan Volume Bawah

Jika pembaruan AB masih tidak dapat menyelesaikan masalah, Anda dapat mencoba menggunakan Safe Mode. Dalam Safe Mode, semua modul dinonaktifkan.

Ada dua cara untuk masuk ke Safe Mode:

  1. Mode Aman bawaan dari beberapa sistem; beberapa sistem memiliki Safe Mode bawaan yang dapat diakses dengan menekan lama tombol volume turun, sementara yang lain (seperti MIUI) dapat mengaktifkan Safe Mode di Recovery. Saat memasuki Safe Mode sistem, KernelSU juga akan masuk ke Safe Mode dan secara otomatis menonaktifkan modul.
  2. Safe Mode bawaan dari KernelSU; metode pengoperasiannya adalah tekan tombol volume turun secara terus-menerus selama lebih dari tiga kali setelah layar boot pertama. Perhatikan bahwa ini adalah rilis pers, rilis pers, rilis pers, bukan tekan dan tahan.

Setelah memasuki mode aman, semua modul pada halaman modul KernelSU Manager dinonaktifkan, tetapi Anda dapat melakukan operasi "uninstall" untuk menghapus semua modul yang mungkin menyebabkan masalah.

Mode aman bawaan diimplementasikan di kernel, jadi tidak ada kemungkinan peristiwa penting yang hilang karena intersepsi. Namun, untuk kernel non-GKI, integrasi kode secara manual mungkin diperlukan, dan Anda dapat merujuk ke dokumentasi resmi untuk mendapatkan panduan.

Module berbahaya

Jika metode di atas tidak dapat menyelamatkan perangkat Anda, kemungkinan besar modul yang Anda instal memiliki operasi jahat atau telah merusak perangkat Anda melalui cara lain. Dalam hal ini, hanya ada dua saran:

  1. Hapus data dan flash sistem resmi.
  2. Konsultasikan layanan purna jual.
',23),o=[m];function r(d,l,u,s,k,b){return e(),n("div",null,o)}const h=a(t,[["render",r]]);export{g as __pageData,h as default}; diff --git a/assets/id_ID_guide_rescue-from-bootloop.md.ec3ef7ee.lean.js b/assets/id_ID_guide_rescue-from-bootloop.md.5cce45dd.lean.js similarity index 72% rename from assets/id_ID_guide_rescue-from-bootloop.md.ec3ef7ee.lean.js rename to assets/id_ID_guide_rescue-from-bootloop.md.5cce45dd.lean.js index 89463e97f172..03aafb454844 100644 --- a/assets/id_ID_guide_rescue-from-bootloop.md.ec3ef7ee.lean.js +++ b/assets/id_ID_guide_rescue-from-bootloop.md.5cce45dd.lean.js @@ -1 +1 @@ -import{_ as a,o as e,c as n,O as i}from"./chunks/framework.43781440.js";const g=JSON.parse('{"title":"Recovery dari bootloop","description":"","frontmatter":{},"headers":[],"relativePath":"id_ID/guide/rescue-from-bootloop.md","filePath":"id_ID/guide/rescue-from-bootloop.md"}'),t={name:"id_ID/guide/rescue-from-bootloop.md"},m=i("",23),o=[m];function r(d,l,u,s,k,b){return e(),n("div",null,o)}const h=a(t,[["render",r]]);export{g as __pageData,h as default}; +import{_ as a,o as e,c as n,Q as i}from"./chunks/framework.ec8f7e8e.js";const g=JSON.parse('{"title":"Recovery dari bootloop","description":"","frontmatter":{},"headers":[],"relativePath":"id_ID/guide/rescue-from-bootloop.md","filePath":"id_ID/guide/rescue-from-bootloop.md"}'),t={name:"id_ID/guide/rescue-from-bootloop.md"},m=i("",23),o=[m];function r(d,l,u,s,k,b){return e(),n("div",null,o)}const h=a(t,[["render",r]]);export{g as __pageData,h as default}; diff --git a/assets/id_ID_guide_unofficially-support-devices.md.3238b4be.js b/assets/id_ID_guide_unofficially-support-devices.md.78d9391e.js similarity index 81% rename from assets/id_ID_guide_unofficially-support-devices.md.3238b4be.js rename to assets/id_ID_guide_unofficially-support-devices.md.78d9391e.js index 799e0ecc79ab..14b544fcae80 100644 --- a/assets/id_ID_guide_unofficially-support-devices.md.3238b4be.js +++ b/assets/id_ID_guide_unofficially-support-devices.md.78d9391e.js @@ -1 +1 @@ -import{d as r}from"./chunks/repos.9a031e32.js";import{o as n,c as t,z as e,F as l,L as d,b as s,O as u,t as i}from"./chunks/framework.43781440.js";const o=u('

Perangkat Yang Didukung Tidak Resmi

:::peringatan

di halaman ini, terdapat kernel untuk perangkat non-GKI yang mendukung KernelSU yang dikelola oleh pengembang lain.

:::

:::peringatan

Halaman ini hanya untuk Anda yang ingin menemukan kode sumber yang sesuai dengan perangkat Anda, itu BUKAN berarti kode sumber telah ditinjau oleh KernelSU Developers. Anda harus menggunakannya dengan risiko Anda sendiri.

:::

',7),g=e("thead",null,[e("tr",null,[e("th",null,"Pengelola"),e("th",null,"Repository"),e("th",null,"Perangkat yang didukung")])],-1),_=["href"],k=["href"],b=JSON.parse('{"title":"Perangkat Yang Didukung Tidak Resmi","description":"","frontmatter":{},"headers":[],"relativePath":"id_ID/guide/unofficially-support-devices.md","filePath":"id_ID/guide/unofficially-support-devices.md"}'),p={name:"id_ID/guide/unofficially-support-devices.md"},D=Object.assign(p,{setup(m){return(c,h)=>(n(),t("div",null,[o,e("table",null,[g,e("tbody",null,[(n(!0),t(l,null,d(s(r),a=>(n(),t("tr",{key:a.devices},[e("td",null,[e("a",{href:a.maintainer_link,target:"_blank",rel:"noreferrer"},i(a.maintainer),9,_)]),e("td",null,[e("a",{href:a.kernel_link,target:"_blank",rel:"noreferrer"},i(a.kernel_name),9,k)]),e("td",null,i(a.devices),1)]))),128))])])]))}});export{b as __pageData,D as default}; +import{d as r}from"./chunks/repos.9a031e32.js";import{o as n,c as t,k as e,F as l,D as d,l as s,Q as u,t as i}from"./chunks/framework.ec8f7e8e.js";const o=u('

Perangkat Yang Didukung Tidak Resmi

:::peringatan

di halaman ini, terdapat kernel untuk perangkat non-GKI yang mendukung KernelSU yang dikelola oleh pengembang lain.

:::

:::peringatan

Halaman ini hanya untuk Anda yang ingin menemukan kode sumber yang sesuai dengan perangkat Anda, itu BUKAN berarti kode sumber telah ditinjau oleh KernelSU Developers. Anda harus menggunakannya dengan risiko Anda sendiri.

:::

',7),g=e("thead",null,[e("tr",null,[e("th",null,"Pengelola"),e("th",null,"Repository"),e("th",null,"Perangkat yang didukung")])],-1),k=["href"],_=["href"],b=JSON.parse('{"title":"Perangkat Yang Didukung Tidak Resmi","description":"","frontmatter":{},"headers":[],"relativePath":"id_ID/guide/unofficially-support-devices.md","filePath":"id_ID/guide/unofficially-support-devices.md"}'),p={name:"id_ID/guide/unofficially-support-devices.md"},D=Object.assign(p,{setup(m){return(c,h)=>(n(),t("div",null,[o,e("table",null,[g,e("tbody",null,[(n(!0),t(l,null,d(s(r),a=>(n(),t("tr",{key:a.devices},[e("td",null,[e("a",{href:a.maintainer_link,target:"_blank",rel:"noreferrer"},i(a.maintainer),9,k)]),e("td",null,[e("a",{href:a.kernel_link,target:"_blank",rel:"noreferrer"},i(a.kernel_name),9,_)]),e("td",null,i(a.devices),1)]))),128))])])]))}});export{b as __pageData,D as default}; diff --git a/assets/id_ID_guide_unofficially-support-devices.md.3238b4be.lean.js b/assets/id_ID_guide_unofficially-support-devices.md.78d9391e.lean.js similarity index 68% rename from assets/id_ID_guide_unofficially-support-devices.md.3238b4be.lean.js rename to assets/id_ID_guide_unofficially-support-devices.md.78d9391e.lean.js index 59896bc50e59..2c4af0850076 100644 --- a/assets/id_ID_guide_unofficially-support-devices.md.3238b4be.lean.js +++ b/assets/id_ID_guide_unofficially-support-devices.md.78d9391e.lean.js @@ -1 +1 @@ -import{d as r}from"./chunks/repos.9a031e32.js";import{o as n,c as t,z as e,F as l,L as d,b as s,O as u,t as i}from"./chunks/framework.43781440.js";const o=u("",7),g=e("thead",null,[e("tr",null,[e("th",null,"Pengelola"),e("th",null,"Repository"),e("th",null,"Perangkat yang didukung")])],-1),_=["href"],k=["href"],b=JSON.parse('{"title":"Perangkat Yang Didukung Tidak Resmi","description":"","frontmatter":{},"headers":[],"relativePath":"id_ID/guide/unofficially-support-devices.md","filePath":"id_ID/guide/unofficially-support-devices.md"}'),p={name:"id_ID/guide/unofficially-support-devices.md"},D=Object.assign(p,{setup(m){return(c,h)=>(n(),t("div",null,[o,e("table",null,[g,e("tbody",null,[(n(!0),t(l,null,d(s(r),a=>(n(),t("tr",{key:a.devices},[e("td",null,[e("a",{href:a.maintainer_link,target:"_blank",rel:"noreferrer"},i(a.maintainer),9,_)]),e("td",null,[e("a",{href:a.kernel_link,target:"_blank",rel:"noreferrer"},i(a.kernel_name),9,k)]),e("td",null,i(a.devices),1)]))),128))])])]))}});export{b as __pageData,D as default}; +import{d as r}from"./chunks/repos.9a031e32.js";import{o as n,c as t,k as e,F as l,D as d,l as s,Q as u,t as i}from"./chunks/framework.ec8f7e8e.js";const o=u("",7),g=e("thead",null,[e("tr",null,[e("th",null,"Pengelola"),e("th",null,"Repository"),e("th",null,"Perangkat yang didukung")])],-1),k=["href"],_=["href"],b=JSON.parse('{"title":"Perangkat Yang Didukung Tidak Resmi","description":"","frontmatter":{},"headers":[],"relativePath":"id_ID/guide/unofficially-support-devices.md","filePath":"id_ID/guide/unofficially-support-devices.md"}'),p={name:"id_ID/guide/unofficially-support-devices.md"},D=Object.assign(p,{setup(m){return(c,h)=>(n(),t("div",null,[o,e("table",null,[g,e("tbody",null,[(n(!0),t(l,null,d(s(r),a=>(n(),t("tr",{key:a.devices},[e("td",null,[e("a",{href:a.maintainer_link,target:"_blank",rel:"noreferrer"},i(a.maintainer),9,k)]),e("td",null,[e("a",{href:a.kernel_link,target:"_blank",rel:"noreferrer"},i(a.kernel_name),9,_)]),e("td",null,i(a.devices),1)]))),128))])])]))}});export{b as __pageData,D as default}; diff --git a/assets/id_ID_guide_what-is-kernelsu.md.aee47f37.js b/assets/id_ID_guide_what-is-kernelsu.md.b6c7dc2f.js similarity index 94% rename from assets/id_ID_guide_what-is-kernelsu.md.aee47f37.js rename to assets/id_ID_guide_what-is-kernelsu.md.b6c7dc2f.js index afe1e846fdfa..e031eba6ffb4 100644 --- a/assets/id_ID_guide_what-is-kernelsu.md.aee47f37.js +++ b/assets/id_ID_guide_what-is-kernelsu.md.b6c7dc2f.js @@ -1 +1 @@ -import{_ as a,o as e,c as n,O as i}from"./chunks/framework.43781440.js";const c=JSON.parse('{"title":"Apa itu KernelSU?","description":"","frontmatter":{},"headers":[],"relativePath":"id_ID/guide/what-is-kernelsu.md","filePath":"id_ID/guide/what-is-kernelsu.md"}'),r={name:"id_ID/guide/what-is-kernelsu.md"},t=i('

Apa itu KernelSU?

KernelSU adalah solusi root untuk perangkat GKI Android, ia bekerja dalam mode kernel dan memberikan izin root ke aplikasi userspace secara langsung di ruang kernel.

Fitur

Fitur utama dari KernelSU adalah berbasis kernel. KernelSU bekerja dalam mode kernel, sehingga dapat menyediakan antarmuka kernel yang belum pernah kita miliki sebelumnya. Sebagai contoh, kita dapat menambahkan breakpoint perangkat keras ke proses apa pun dalam mode kernel; Kita dapat mengakses memori fisik dari proses apa pun tanpa diketahui oleh siapa pun; Kita dapat mencegat syscall apa pun di ruang kernel; dll.

Dan juga, KernelSU menyediakan sistem modul melalui overlayfs, yang memungkinkan Anda untuk memuat plugin kustom Anda ke dalam sistem. KernelSU juga menyediakan mekanisme untuk memodifikasi berkas-berkas pada partisi /system.

Bagaimana cara menggunakannya

Silakan merujuk ke: Installation

Bagaimana cara men-buildnya

How to build

Diskusi

',11),l=[t];function s(u,d,o,m,k,p){return e(),n("div",null,l)}const g=a(r,[["render",s]]);export{c as __pageData,g as default}; +import{_ as a,o as e,c as n,Q as i}from"./chunks/framework.ec8f7e8e.js";const c=JSON.parse('{"title":"Apa itu KernelSU?","description":"","frontmatter":{},"headers":[],"relativePath":"id_ID/guide/what-is-kernelsu.md","filePath":"id_ID/guide/what-is-kernelsu.md"}'),r={name:"id_ID/guide/what-is-kernelsu.md"},t=i('

Apa itu KernelSU?

KernelSU adalah solusi root untuk perangkat GKI Android, ia bekerja dalam mode kernel dan memberikan izin root ke aplikasi userspace secara langsung di ruang kernel.

Fitur

Fitur utama dari KernelSU adalah berbasis kernel. KernelSU bekerja dalam mode kernel, sehingga dapat menyediakan antarmuka kernel yang belum pernah kita miliki sebelumnya. Sebagai contoh, kita dapat menambahkan breakpoint perangkat keras ke proses apa pun dalam mode kernel; Kita dapat mengakses memori fisik dari proses apa pun tanpa diketahui oleh siapa pun; Kita dapat mencegat syscall apa pun di ruang kernel; dll.

Dan juga, KernelSU menyediakan sistem modul melalui overlayfs, yang memungkinkan Anda untuk memuat plugin kustom Anda ke dalam sistem. KernelSU juga menyediakan mekanisme untuk memodifikasi berkas-berkas pada partisi /system.

Bagaimana cara menggunakannya

Silakan merujuk ke: Installation

Bagaimana cara men-buildnya

How to build

Diskusi

',11),l=[t];function s(u,d,o,m,k,p){return e(),n("div",null,l)}const g=a(r,[["render",s]]);export{c as __pageData,g as default}; diff --git a/assets/id_ID_guide_what-is-kernelsu.md.aee47f37.lean.js b/assets/id_ID_guide_what-is-kernelsu.md.b6c7dc2f.lean.js similarity index 71% rename from assets/id_ID_guide_what-is-kernelsu.md.aee47f37.lean.js rename to assets/id_ID_guide_what-is-kernelsu.md.b6c7dc2f.lean.js index 7f4afc0843b4..ae561b91c6ff 100644 --- a/assets/id_ID_guide_what-is-kernelsu.md.aee47f37.lean.js +++ b/assets/id_ID_guide_what-is-kernelsu.md.b6c7dc2f.lean.js @@ -1 +1 @@ -import{_ as a,o as e,c as n,O as i}from"./chunks/framework.43781440.js";const c=JSON.parse('{"title":"Apa itu KernelSU?","description":"","frontmatter":{},"headers":[],"relativePath":"id_ID/guide/what-is-kernelsu.md","filePath":"id_ID/guide/what-is-kernelsu.md"}'),r={name:"id_ID/guide/what-is-kernelsu.md"},t=i("",11),l=[t];function s(u,d,o,m,k,p){return e(),n("div",null,l)}const g=a(r,[["render",s]]);export{c as __pageData,g as default}; +import{_ as a,o as e,c as n,Q as i}from"./chunks/framework.ec8f7e8e.js";const c=JSON.parse('{"title":"Apa itu KernelSU?","description":"","frontmatter":{},"headers":[],"relativePath":"id_ID/guide/what-is-kernelsu.md","filePath":"id_ID/guide/what-is-kernelsu.md"}'),r={name:"id_ID/guide/what-is-kernelsu.md"},t=i("",11),l=[t];function s(u,d,o,m,k,p){return e(),n("div",null,l)}const g=a(r,[["render",s]]);export{c as __pageData,g as default}; diff --git a/assets/id_ID_index.md.9ad2e601.js b/assets/id_ID_index.md.06fbc917.js similarity index 95% rename from assets/id_ID_index.md.9ad2e601.js rename to assets/id_ID_index.md.06fbc917.js index c9995354ba8d..47fe1dea1d65 100644 --- a/assets/id_ID_index.md.9ad2e601.js +++ b/assets/id_ID_index.md.06fbc917.js @@ -1 +1 @@ -import{_ as e,o as a,c as t}from"./chunks/framework.43781440.js";const k=JSON.parse('{"title":"Sebuah solusi root kernel-based untuk Android","description":"","frontmatter":{"layout":"home","title":"Sebuah solusi root kernel-based untuk Android","hero":{"name":"KernelSU","text":"Sebuah solusi root kernel-based untuk Android","tagline":"","image":{"src":"/logo.png","alt":"KernelSU"},"actions":[{"theme":"brand","text":"Permulaan","link":"/id_ID/guide/what-is-kernelsu"},{"theme":"alt","text":"Lihat di GitHub","link":"https://github.com/tiann/KernelSU"}]},"features":[{"title":"Kernel-based","details":"KernelSU bekerja dalam mode Linux kernel, dan mempunyai kelebihan diatas aplikasi userspace."},{"title":"Kontrol akses daftar putih","details":"Hanya aplikasi yang diberikan izin root yang bisa mengakses `su`, aplikasi lain tidak bisa mengakses su."},{"title":"Dukungan modul","details":"KernelSU mendukung modifikasi /system tanpa-sistem dalam overlayfs, bahkan bisa membuat system dapat ditulis."},{"title":"Sumber terbuka","details":"KernelSU adalah projek sumber terbuka dibawah lisensi GPL-3."}]},"headers":[],"relativePath":"id_ID/index.md","filePath":"id_ID/index.md"}'),i={name:"id_ID/index.md"};function n(s,r,l,d,o,u){return a(),t("div")}const b=e(i,[["render",n]]);export{k as __pageData,b as default}; +import{_ as e,o as a,c as t}from"./chunks/framework.ec8f7e8e.js";const k=JSON.parse('{"title":"Sebuah solusi root kernel-based untuk Android","description":"","frontmatter":{"layout":"home","title":"Sebuah solusi root kernel-based untuk Android","hero":{"name":"KernelSU","text":"Sebuah solusi root kernel-based untuk Android","tagline":"","image":{"src":"/logo.png","alt":"KernelSU"},"actions":[{"theme":"brand","text":"Permulaan","link":"/id_ID/guide/what-is-kernelsu"},{"theme":"alt","text":"Lihat di GitHub","link":"https://github.com/tiann/KernelSU"}]},"features":[{"title":"Kernel-based","details":"KernelSU bekerja dalam mode Linux kernel, dan mempunyai kelebihan diatas aplikasi userspace."},{"title":"Kontrol akses daftar putih","details":"Hanya aplikasi yang diberikan izin root yang bisa mengakses `su`, aplikasi lain tidak bisa mengakses su."},{"title":"Dukungan modul","details":"KernelSU mendukung modifikasi /system tanpa-sistem dalam overlayfs, bahkan bisa membuat system dapat ditulis."},{"title":"Sumber terbuka","details":"KernelSU adalah projek sumber terbuka dibawah lisensi GPL-3."}]},"headers":[],"relativePath":"id_ID/index.md","filePath":"id_ID/index.md"}'),i={name:"id_ID/index.md"};function n(s,r,l,d,o,u){return a(),t("div")}const b=e(i,[["render",n]]);export{k as __pageData,b as default}; diff --git a/assets/id_ID_index.md.9ad2e601.lean.js b/assets/id_ID_index.md.06fbc917.lean.js similarity index 95% rename from assets/id_ID_index.md.9ad2e601.lean.js rename to assets/id_ID_index.md.06fbc917.lean.js index c9995354ba8d..47fe1dea1d65 100644 --- a/assets/id_ID_index.md.9ad2e601.lean.js +++ b/assets/id_ID_index.md.06fbc917.lean.js @@ -1 +1 @@ -import{_ as e,o as a,c as t}from"./chunks/framework.43781440.js";const k=JSON.parse('{"title":"Sebuah solusi root kernel-based untuk Android","description":"","frontmatter":{"layout":"home","title":"Sebuah solusi root kernel-based untuk Android","hero":{"name":"KernelSU","text":"Sebuah solusi root kernel-based untuk Android","tagline":"","image":{"src":"/logo.png","alt":"KernelSU"},"actions":[{"theme":"brand","text":"Permulaan","link":"/id_ID/guide/what-is-kernelsu"},{"theme":"alt","text":"Lihat di GitHub","link":"https://github.com/tiann/KernelSU"}]},"features":[{"title":"Kernel-based","details":"KernelSU bekerja dalam mode Linux kernel, dan mempunyai kelebihan diatas aplikasi userspace."},{"title":"Kontrol akses daftar putih","details":"Hanya aplikasi yang diberikan izin root yang bisa mengakses `su`, aplikasi lain tidak bisa mengakses su."},{"title":"Dukungan modul","details":"KernelSU mendukung modifikasi /system tanpa-sistem dalam overlayfs, bahkan bisa membuat system dapat ditulis."},{"title":"Sumber terbuka","details":"KernelSU adalah projek sumber terbuka dibawah lisensi GPL-3."}]},"headers":[],"relativePath":"id_ID/index.md","filePath":"id_ID/index.md"}'),i={name:"id_ID/index.md"};function n(s,r,l,d,o,u){return a(),t("div")}const b=e(i,[["render",n]]);export{k as __pageData,b as default}; +import{_ as e,o as a,c as t}from"./chunks/framework.ec8f7e8e.js";const k=JSON.parse('{"title":"Sebuah solusi root kernel-based untuk Android","description":"","frontmatter":{"layout":"home","title":"Sebuah solusi root kernel-based untuk Android","hero":{"name":"KernelSU","text":"Sebuah solusi root kernel-based untuk Android","tagline":"","image":{"src":"/logo.png","alt":"KernelSU"},"actions":[{"theme":"brand","text":"Permulaan","link":"/id_ID/guide/what-is-kernelsu"},{"theme":"alt","text":"Lihat di GitHub","link":"https://github.com/tiann/KernelSU"}]},"features":[{"title":"Kernel-based","details":"KernelSU bekerja dalam mode Linux kernel, dan mempunyai kelebihan diatas aplikasi userspace."},{"title":"Kontrol akses daftar putih","details":"Hanya aplikasi yang diberikan izin root yang bisa mengakses `su`, aplikasi lain tidak bisa mengakses su."},{"title":"Dukungan modul","details":"KernelSU mendukung modifikasi /system tanpa-sistem dalam overlayfs, bahkan bisa membuat system dapat ditulis."},{"title":"Sumber terbuka","details":"KernelSU adalah projek sumber terbuka dibawah lisensi GPL-3."}]},"headers":[],"relativePath":"id_ID/index.md","filePath":"id_ID/index.md"}'),i={name:"id_ID/index.md"};function n(s,r,l,d,o,u){return a(),t("div")}const b=e(i,[["render",n]]);export{k as __pageData,b as default}; diff --git a/assets/index.md.042b4c20.js b/assets/index.md.133d8307.js similarity index 95% rename from assets/index.md.042b4c20.js rename to assets/index.md.133d8307.js index c81b66da230f..8c1a67abeee6 100644 --- a/assets/index.md.042b4c20.js +++ b/assets/index.md.133d8307.js @@ -1 +1 @@ -import{_ as e,o as t,c as o}from"./chunks/framework.43781440.js";const p=JSON.parse('{"title":"A kernel-based root solution for Android","description":"","frontmatter":{"layout":"home","title":"A kernel-based root solution for Android","hero":{"name":"KernelSU","text":"A kernel-based root solution for Android","tagline":"","image":{"src":"/logo.png","alt":"KernelSU"},"actions":[{"theme":"brand","text":"Get Started","link":"/guide/what-is-kernelsu"},{"theme":"alt","text":"View on GitHub","link":"https://github.com/tiann/KernelSU"}]},"features":[{"title":"Kernel-based","details":"As the name suggests, KernelSU works under the Linux kernel giving it more control over userspace applications."},{"title":"Root Access Control","details":"Only permitted apps may access or see `su`, all other apps are not aware of this."},{"title":"Customizable Root Privileges","details":"KernelSU allows customization of `su`\'s uid, gid, groups, capabilities, and SELinux rules, locking up root privileges."},{"title":"Modules","details":"Modules may modify /system systemlessly using overlayfs enabling great power."}]},"headers":[],"relativePath":"index.md","filePath":"index.md"}'),s={name:"index.md"};function i(r,a,n,l,d,u){return t(),o("div")}const m=e(s,[["render",i]]);export{p as __pageData,m as default}; +import{_ as e,o as t,c as o}from"./chunks/framework.ec8f7e8e.js";const p=JSON.parse('{"title":"A kernel-based root solution for Android","description":"","frontmatter":{"layout":"home","title":"A kernel-based root solution for Android","hero":{"name":"KernelSU","text":"A kernel-based root solution for Android","tagline":"","image":{"src":"/logo.png","alt":"KernelSU"},"actions":[{"theme":"brand","text":"Get Started","link":"/guide/what-is-kernelsu"},{"theme":"alt","text":"View on GitHub","link":"https://github.com/tiann/KernelSU"}]},"features":[{"title":"Kernel-based","details":"As the name suggests, KernelSU works under the Linux kernel giving it more control over userspace applications."},{"title":"Root Access Control","details":"Only permitted apps may access or see `su`, all other apps are not aware of this."},{"title":"Customizable Root Privileges","details":"KernelSU allows customization of `su`\'s uid, gid, groups, capabilities, and SELinux rules, locking up root privileges."},{"title":"Modules","details":"Modules may modify /system systemlessly using overlayfs enabling great power."}]},"headers":[],"relativePath":"index.md","filePath":"index.md"}'),s={name:"index.md"};function i(r,a,n,l,d,u){return t(),o("div")}const m=e(s,[["render",i]]);export{p as __pageData,m as default}; diff --git a/assets/index.md.042b4c20.lean.js b/assets/index.md.133d8307.lean.js similarity index 95% rename from assets/index.md.042b4c20.lean.js rename to assets/index.md.133d8307.lean.js index c81b66da230f..8c1a67abeee6 100644 --- a/assets/index.md.042b4c20.lean.js +++ b/assets/index.md.133d8307.lean.js @@ -1 +1 @@ -import{_ as e,o as t,c as o}from"./chunks/framework.43781440.js";const p=JSON.parse('{"title":"A kernel-based root solution for Android","description":"","frontmatter":{"layout":"home","title":"A kernel-based root solution for Android","hero":{"name":"KernelSU","text":"A kernel-based root solution for Android","tagline":"","image":{"src":"/logo.png","alt":"KernelSU"},"actions":[{"theme":"brand","text":"Get Started","link":"/guide/what-is-kernelsu"},{"theme":"alt","text":"View on GitHub","link":"https://github.com/tiann/KernelSU"}]},"features":[{"title":"Kernel-based","details":"As the name suggests, KernelSU works under the Linux kernel giving it more control over userspace applications."},{"title":"Root Access Control","details":"Only permitted apps may access or see `su`, all other apps are not aware of this."},{"title":"Customizable Root Privileges","details":"KernelSU allows customization of `su`\'s uid, gid, groups, capabilities, and SELinux rules, locking up root privileges."},{"title":"Modules","details":"Modules may modify /system systemlessly using overlayfs enabling great power."}]},"headers":[],"relativePath":"index.md","filePath":"index.md"}'),s={name:"index.md"};function i(r,a,n,l,d,u){return t(),o("div")}const m=e(s,[["render",i]]);export{p as __pageData,m as default}; +import{_ as e,o as t,c as o}from"./chunks/framework.ec8f7e8e.js";const p=JSON.parse('{"title":"A kernel-based root solution for Android","description":"","frontmatter":{"layout":"home","title":"A kernel-based root solution for Android","hero":{"name":"KernelSU","text":"A kernel-based root solution for Android","tagline":"","image":{"src":"/logo.png","alt":"KernelSU"},"actions":[{"theme":"brand","text":"Get Started","link":"/guide/what-is-kernelsu"},{"theme":"alt","text":"View on GitHub","link":"https://github.com/tiann/KernelSU"}]},"features":[{"title":"Kernel-based","details":"As the name suggests, KernelSU works under the Linux kernel giving it more control over userspace applications."},{"title":"Root Access Control","details":"Only permitted apps may access or see `su`, all other apps are not aware of this."},{"title":"Customizable Root Privileges","details":"KernelSU allows customization of `su`\'s uid, gid, groups, capabilities, and SELinux rules, locking up root privileges."},{"title":"Modules","details":"Modules may modify /system systemlessly using overlayfs enabling great power."}]},"headers":[],"relativePath":"index.md","filePath":"index.md"}'),s={name:"index.md"};function i(r,a,n,l,d,u){return t(),o("div")}const m=e(s,[["render",i]]);export{p as __pageData,m as default}; diff --git a/assets/ja_JP_guide_difference-with-magisk.md.34107b11.js b/assets/ja_JP_guide_difference-with-magisk.md.665d74c8.js similarity index 96% rename from assets/ja_JP_guide_difference-with-magisk.md.34107b11.js rename to assets/ja_JP_guide_difference-with-magisk.md.665d74c8.js index 75e0357341d5..b2e0c7b0b8c9 100644 --- a/assets/ja_JP_guide_difference-with-magisk.md.34107b11.js +++ b/assets/ja_JP_guide_difference-with-magisk.md.665d74c8.js @@ -1 +1 @@ -import{_ as e,o as a,c as i,O as o}from"./chunks/framework.43781440.js";const k=JSON.parse('{"title":"Magisk との違い","description":"","frontmatter":{},"headers":[],"relativePath":"ja_JP/guide/difference-with-magisk.md","filePath":"ja_JP/guide/difference-with-magisk.md"}'),s={name:"ja_JP/guide/difference-with-magisk.md"},l=o('

Magisk との違い

KernelSU モジュールと Magisk モジュールには多くの共通点がありますが、実装の仕組みが全く異なるため、必然的にいくつかの相違点が存在します。Magisk と KernelSU の両方でモジュールを動作させたい場合、これらの違いを理解する必要があります。

似ているところ

  • モジュールファイルの形式:どちらもzip形式でモジュールを整理しており、モジュールの形式はほぼ同じです。
  • モジュールのインストールディレクトリ:どちらも /data/adb/modules に配置されます。
  • システムレス:どちらもモジュールによるシステムレスな方法で /system を変更できます。
  • post-fs-data.sh: 実行時間と意味は全く同じです。
  • service.sh: 実行時間と意味は全く同じです。
  • system.prop:全く同じです。
  • sepolicy.rule:全く同じです。
  • BusyBox:スクリプトは BusyBox で実行され、どちらの場合も「スタンドアロンモード」が有効です。

違うところ

違いを理解する前に、モジュールが KernelSU で動作しているか Magisk で動作しているかを区別する方法を知っておく必要があります。環境変数 KSU を使うとモジュールスクリプトを実行できるすべての場所 (customize.sh, post-fs-data.sh, service.sh) で区別できます。KernelSU では、この環境変数に true が設定されます。

以下は違いです:

  • KernelSU モジュールは、リカバリーモードではインストールできません。
  • KernelSU モジュールには Zygisk のサポートが組み込まれていません(ただしZygiskNextを使うと Zygisk モジュールを使用できます)。
  • KernelSU モジュールにおけるファイルの置換や削除の方法は、Magisk とは全く異なります。KernelSU は .replace メソッドをサポートしていません。その代わり、mknod filename c 0 0 で同名のファイルを作成し、対応するファイルを削除する必要があります。
  • BusyBox 用のディレクトリが違います。KernelSU の組み込み BusyBox は /data/adb/ksu/bin/busybox に、Magisk では /data/adb/magisk/busybox に配置されます。これは KernelSU の内部動作であり、将来的に変更される可能性があることに注意してください!
  • KernelSU は .replace ファイルをサポートしていません。しかし、KernelSU はファイルやフォルダを削除したり置き換えたりするための REMOVEREPLACE 変数をサポートしています。
',8),t=[l];function r(d,c,n,h,_,u){return a(),i("div",null,t)}const p=e(s,[["render",r]]);export{k as __pageData,p as default}; +import{_ as e,o as a,c as i,Q as o}from"./chunks/framework.ec8f7e8e.js";const k=JSON.parse('{"title":"Magisk との違い","description":"","frontmatter":{},"headers":[],"relativePath":"ja_JP/guide/difference-with-magisk.md","filePath":"ja_JP/guide/difference-with-magisk.md"}'),s={name:"ja_JP/guide/difference-with-magisk.md"},l=o('

Magisk との違い

KernelSU モジュールと Magisk モジュールには多くの共通点がありますが、実装の仕組みが全く異なるため、必然的にいくつかの相違点が存在します。Magisk と KernelSU の両方でモジュールを動作させたい場合、これらの違いを理解する必要があります。

似ているところ

  • モジュールファイルの形式:どちらもzip形式でモジュールを整理しており、モジュールの形式はほぼ同じです。
  • モジュールのインストールディレクトリ:どちらも /data/adb/modules に配置されます。
  • システムレス:どちらもモジュールによるシステムレスな方法で /system を変更できます。
  • post-fs-data.sh: 実行時間と意味は全く同じです。
  • service.sh: 実行時間と意味は全く同じです。
  • system.prop:全く同じです。
  • sepolicy.rule:全く同じです。
  • BusyBox:スクリプトは BusyBox で実行され、どちらの場合も「スタンドアロンモード」が有効です。

違うところ

違いを理解する前に、モジュールが KernelSU で動作しているか Magisk で動作しているかを区別する方法を知っておく必要があります。環境変数 KSU を使うとモジュールスクリプトを実行できるすべての場所 (customize.sh, post-fs-data.sh, service.sh) で区別できます。KernelSU では、この環境変数に true が設定されます。

以下は違いです:

  • KernelSU モジュールは、リカバリーモードではインストールできません。
  • KernelSU モジュールには Zygisk のサポートが組み込まれていません(ただしZygiskNextを使うと Zygisk モジュールを使用できます)。
  • KernelSU モジュールにおけるファイルの置換や削除の方法は、Magisk とは全く異なります。KernelSU は .replace メソッドをサポートしていません。その代わり、mknod filename c 0 0 で同名のファイルを作成し、対応するファイルを削除する必要があります。
  • BusyBox 用のディレクトリが違います。KernelSU の組み込み BusyBox は /data/adb/ksu/bin/busybox に、Magisk では /data/adb/magisk/busybox に配置されます。これは KernelSU の内部動作であり、将来的に変更される可能性があることに注意してください!
  • KernelSU は .replace ファイルをサポートしていません。しかし、KernelSU はファイルやフォルダを削除したり置き換えたりするための REMOVEREPLACE 変数をサポートしています。
',8),t=[l];function r(d,c,n,h,_,u){return a(),i("div",null,t)}const p=e(s,[["render",r]]);export{k as __pageData,p as default}; diff --git a/assets/ja_JP_guide_difference-with-magisk.md.34107b11.lean.js b/assets/ja_JP_guide_difference-with-magisk.md.665d74c8.lean.js similarity index 72% rename from assets/ja_JP_guide_difference-with-magisk.md.34107b11.lean.js rename to assets/ja_JP_guide_difference-with-magisk.md.665d74c8.lean.js index 3d3ce0d86eaa..ce228e79dee3 100644 --- a/assets/ja_JP_guide_difference-with-magisk.md.34107b11.lean.js +++ b/assets/ja_JP_guide_difference-with-magisk.md.665d74c8.lean.js @@ -1 +1 @@ -import{_ as e,o as a,c as i,O as o}from"./chunks/framework.43781440.js";const k=JSON.parse('{"title":"Magisk との違い","description":"","frontmatter":{},"headers":[],"relativePath":"ja_JP/guide/difference-with-magisk.md","filePath":"ja_JP/guide/difference-with-magisk.md"}'),s={name:"ja_JP/guide/difference-with-magisk.md"},l=o("",8),t=[l];function r(d,c,n,h,_,u){return a(),i("div",null,t)}const p=e(s,[["render",r]]);export{k as __pageData,p as default}; +import{_ as e,o as a,c as i,Q as o}from"./chunks/framework.ec8f7e8e.js";const k=JSON.parse('{"title":"Magisk との違い","description":"","frontmatter":{},"headers":[],"relativePath":"ja_JP/guide/difference-with-magisk.md","filePath":"ja_JP/guide/difference-with-magisk.md"}'),s={name:"ja_JP/guide/difference-with-magisk.md"},l=o("",8),t=[l];function r(d,c,n,h,_,u){return a(),i("div",null,t)}const p=e(s,[["render",r]]);export{k as __pageData,p as default}; diff --git a/assets/ja_JP_guide_faq.md.998d2dfd.js b/assets/ja_JP_guide_faq.md.b51546fa.js similarity index 98% rename from assets/ja_JP_guide_faq.md.998d2dfd.js rename to assets/ja_JP_guide_faq.md.b51546fa.js index 38267366ede6..2b25184b0ed1 100644 --- a/assets/ja_JP_guide_faq.md.998d2dfd.js +++ b/assets/ja_JP_guide_faq.md.b51546fa.js @@ -1 +1 @@ -import{_ as e,o as a,c as r,O as n}from"./chunks/framework.43781440.js";const m=JSON.parse('{"title":"よくある質問","description":"","frontmatter":{},"headers":[],"relativePath":"ja_JP/guide/faq.md","filePath":"ja_JP/guide/faq.md"}'),l={name:"ja_JP/guide/faq.md"},i=n('

よくある質問

私のデバイスは KernelSU に対応していますか?

まず、お使いのデバイスがブートローダーのロックを解除できる必要があります。もしできないのであれば、サポート外です。

もし KernelSU アプリで「非対応」と表示されたら、そのデバイスは最初からサポートされていないことになりますが、カーネルソースをビルドして KernelSU を組み込むか、非公式の対応デバイスで動作させることが可能です。

KernelSU を使うにはブートローダーのロックを解除する必要がありますか?

はい。

KernelSU はモジュールに対応していますか?

はい。ただし初期バージョンであるためバグがある可能性があります。安定するのをお待ちください。

KernelSU は Xposed に対応していますか?

はい。DreamlandTaiChi が動作します。LSPosed については、ZygiskNext を使うと動作するようにできます。

KernelSU は Zygisk に対応していますか?

KernelSU は Zygisk サポートを内蔵していません。ZygiskNext を使ってください。

KernelSU は Magisk と互換性がありますか?

KernelSU のモジュールシステムは Magisk のマジックマウントと競合しており、KernelSU で有効になっているモジュールがある場合、Magisk 全体が動作しなくなります。

しかし、KernelSU の su だけを使うのであれば、Magisk とうまく連携することができます。KernelSU は kernel を、Magisk は ramdisk を修正するため、両者は共存できます。

KernelSU は Magisk の代わりになりますか?

私たちはそうは思っていませんし、それが目標でもありません。Magisk はユーザ空間の root ソリューションとして十分であり、長く使われ続けるでしょう。KernelSU の目標は、ユーザーにカーネルインターフェースを提供することであり、Magisk の代用ではありません。

KernelSU は GKI 以外のデバイスに対応できますか?

可能です。ただしカーネルソースをダウンロードし、KernelSU をソースツリーに統合して、自分でカーネルをビルドする必要があります。

KernelSU は Android 12 以下のデバイスに対応できますか?

KernelSU の互換性に影響を与えるのはデバイスのカーネルであり、Android のバージョンとは無関係です。唯一の制限は、Android 12 で発売されたデバイスはカーネル5.10以上(GKI デバイス)でなければならないことです:

  1. Android 12 をプリインストールして発売された端末は対応しているはずです。
  2. カーネルが古い端末(一部の Android 12 端末はカーネルも古い)は対応可能ですが、カーネルは自分でビルドする必要があります。

KernelSU は古いカーネルに対応できますか?

KernelSU は現在カーネル4.14にバックポートされていますが、それ以前のカーネルについては手動でバックポートする必要があります。プルリクエスト歓迎です!

古いカーネルに KernelSU を組み込むには?

ガイド を参考にしてください。

Android のバージョンが13なのに、カーネルは「android12-5.10」と表示されるのはなぜ?

カーネルのバージョンは Android のバージョンと関係ありません。カーネルを書き込む必要がある場合は、常にカーネルのバージョンを使用してください。Android のバージョンはそれほど重要ではありません。

KernelSU に-mount-master/global のマウント名前空間はありますか?

今はまだありませんが(将来的にはあるかもしれません)、グローバルマウントの名前空間に手動で切り替える方法は、以下のようにたくさんあります:

  1. nsenter -t 1 -m sh でシェルをグローバル名前空間にします。
  2. nsenter --mount=/proc/1/ns/mnt を実行したいコマンドに追加すればグローバル名前空間で実行されます。 KernelSU は このような使い方 もできます。

GKI 1.0 なのですが、使えますか?

GKI1 は GKI2 と全く異なるため、カーネルは自分でビルドする必要があります。

',33),o=[i];function t(s,d,h,u,k,c){return a(),r("div",null,o)}const g=e(l,[["render",t]]);export{m as __pageData,g as default}; +import{_ as e,o as a,c as r,Q as n}from"./chunks/framework.ec8f7e8e.js";const m=JSON.parse('{"title":"よくある質問","description":"","frontmatter":{},"headers":[],"relativePath":"ja_JP/guide/faq.md","filePath":"ja_JP/guide/faq.md"}'),l={name:"ja_JP/guide/faq.md"},i=n('

よくある質問

私のデバイスは KernelSU に対応していますか?

まず、お使いのデバイスがブートローダーのロックを解除できる必要があります。もしできないのであれば、サポート外です。

もし KernelSU アプリで「非対応」と表示されたら、そのデバイスは最初からサポートされていないことになりますが、カーネルソースをビルドして KernelSU を組み込むか、非公式の対応デバイスで動作させることが可能です。

KernelSU を使うにはブートローダーのロックを解除する必要がありますか?

はい。

KernelSU はモジュールに対応していますか?

はい。ただし初期バージョンであるためバグがある可能性があります。安定するのをお待ちください。

KernelSU は Xposed に対応していますか?

はい。DreamlandTaiChi が動作します。LSPosed については、ZygiskNext を使うと動作するようにできます。

KernelSU は Zygisk に対応していますか?

KernelSU は Zygisk サポートを内蔵していません。ZygiskNext を使ってください。

KernelSU は Magisk と互換性がありますか?

KernelSU のモジュールシステムは Magisk のマジックマウントと競合しており、KernelSU で有効になっているモジュールがある場合、Magisk 全体が動作しなくなります。

しかし、KernelSU の su だけを使うのであれば、Magisk とうまく連携することができます。KernelSU は kernel を、Magisk は ramdisk を修正するため、両者は共存できます。

KernelSU は Magisk の代わりになりますか?

私たちはそうは思っていませんし、それが目標でもありません。Magisk はユーザ空間の root ソリューションとして十分であり、長く使われ続けるでしょう。KernelSU の目標は、ユーザーにカーネルインターフェースを提供することであり、Magisk の代用ではありません。

KernelSU は GKI 以外のデバイスに対応できますか?

可能です。ただしカーネルソースをダウンロードし、KernelSU をソースツリーに統合して、自分でカーネルをビルドする必要があります。

KernelSU は Android 12 以下のデバイスに対応できますか?

KernelSU の互換性に影響を与えるのはデバイスのカーネルであり、Android のバージョンとは無関係です。唯一の制限は、Android 12 で発売されたデバイスはカーネル5.10以上(GKI デバイス)でなければならないことです:

  1. Android 12 をプリインストールして発売された端末は対応しているはずです。
  2. カーネルが古い端末(一部の Android 12 端末はカーネルも古い)は対応可能ですが、カーネルは自分でビルドする必要があります。

KernelSU は古いカーネルに対応できますか?

KernelSU は現在カーネル4.14にバックポートされていますが、それ以前のカーネルについては手動でバックポートする必要があります。プルリクエスト歓迎です!

古いカーネルに KernelSU を組み込むには?

ガイド を参考にしてください。

Android のバージョンが13なのに、カーネルは「android12-5.10」と表示されるのはなぜ?

カーネルのバージョンは Android のバージョンと関係ありません。カーネルを書き込む必要がある場合は、常にカーネルのバージョンを使用してください。Android のバージョンはそれほど重要ではありません。

KernelSU に-mount-master/global のマウント名前空間はありますか?

今はまだありませんが(将来的にはあるかもしれません)、グローバルマウントの名前空間に手動で切り替える方法は、以下のようにたくさんあります:

  1. nsenter -t 1 -m sh でシェルをグローバル名前空間にします。
  2. nsenter --mount=/proc/1/ns/mnt を実行したいコマンドに追加すればグローバル名前空間で実行されます。 KernelSU は このような使い方 もできます。

GKI 1.0 なのですが、使えますか?

GKI1 は GKI2 と全く異なるため、カーネルは自分でビルドする必要があります。

',33),o=[i];function t(s,d,h,u,k,c){return a(),r("div",null,o)}const g=e(l,[["render",t]]);export{m as __pageData,g as default}; diff --git a/assets/ja_JP_guide_faq.md.998d2dfd.lean.js b/assets/ja_JP_guide_faq.md.b51546fa.lean.js similarity index 68% rename from assets/ja_JP_guide_faq.md.998d2dfd.lean.js rename to assets/ja_JP_guide_faq.md.b51546fa.lean.js index 79e0aa61a619..552ae64dc836 100644 --- a/assets/ja_JP_guide_faq.md.998d2dfd.lean.js +++ b/assets/ja_JP_guide_faq.md.b51546fa.lean.js @@ -1 +1 @@ -import{_ as e,o as a,c as r,O as n}from"./chunks/framework.43781440.js";const m=JSON.parse('{"title":"よくある質問","description":"","frontmatter":{},"headers":[],"relativePath":"ja_JP/guide/faq.md","filePath":"ja_JP/guide/faq.md"}'),l={name:"ja_JP/guide/faq.md"},i=n("",33),o=[i];function t(s,d,h,u,k,c){return a(),r("div",null,o)}const g=e(l,[["render",t]]);export{m as __pageData,g as default}; +import{_ as e,o as a,c as r,Q as n}from"./chunks/framework.ec8f7e8e.js";const m=JSON.parse('{"title":"よくある質問","description":"","frontmatter":{},"headers":[],"relativePath":"ja_JP/guide/faq.md","filePath":"ja_JP/guide/faq.md"}'),l={name:"ja_JP/guide/faq.md"},i=n("",33),o=[i];function t(s,d,h,u,k,c){return a(),r("div",null,o)}const g=e(l,[["render",t]]);export{m as __pageData,g as default}; diff --git a/assets/ja_JP_guide_hidden-features.md.9c5dddb2.js b/assets/ja_JP_guide_hidden-features.md.cdaa1636.js similarity index 87% rename from assets/ja_JP_guide_hidden-features.md.9c5dddb2.js rename to assets/ja_JP_guide_hidden-features.md.cdaa1636.js index 08753283772a..8a39fce217d3 100644 --- a/assets/ja_JP_guide_hidden-features.md.9c5dddb2.js +++ b/assets/ja_JP_guide_hidden-features.md.cdaa1636.js @@ -1 +1 @@ -import{_ as e,o as a,c as t,O as s}from"./chunks/framework.43781440.js";const m=JSON.parse('{"title":"隠し機能","description":"","frontmatter":{},"headers":[],"relativePath":"ja_JP/guide/hidden-features.md","filePath":"ja_JP/guide/hidden-features.md"}'),r={name:"ja_JP/guide/hidden-features.md"},d=s('

隠し機能

.ksurc

デフォルトでは /system/bin/sh/system/etc/mkshrc を読み込みます。

/data/adb/ksu/.ksurc ファイルを作成することで、カスタマイズした rc ファイルを su に読み込ませられます。

',4),c=[d];function o(_,i,n,u,h,l){return a(),t("div",null,c)}const f=e(r,[["render",o]]);export{m as __pageData,f as default}; +import{_ as e,o as a,c as t,Q as s}from"./chunks/framework.ec8f7e8e.js";const m=JSON.parse('{"title":"隠し機能","description":"","frontmatter":{},"headers":[],"relativePath":"ja_JP/guide/hidden-features.md","filePath":"ja_JP/guide/hidden-features.md"}'),r={name:"ja_JP/guide/hidden-features.md"},d=s('

隠し機能

.ksurc

デフォルトでは /system/bin/sh/system/etc/mkshrc を読み込みます。

/data/adb/ksu/.ksurc ファイルを作成することで、カスタマイズした rc ファイルを su に読み込ませられます。

',4),c=[d];function o(_,i,n,u,h,l){return a(),t("div",null,c)}const f=e(r,[["render",o]]);export{m as __pageData,f as default}; diff --git a/assets/ja_JP_guide_hidden-features.md.9c5dddb2.lean.js b/assets/ja_JP_guide_hidden-features.md.cdaa1636.lean.js similarity index 70% rename from assets/ja_JP_guide_hidden-features.md.9c5dddb2.lean.js rename to assets/ja_JP_guide_hidden-features.md.cdaa1636.lean.js index 768701b1cd0c..b934f841f237 100644 --- a/assets/ja_JP_guide_hidden-features.md.9c5dddb2.lean.js +++ b/assets/ja_JP_guide_hidden-features.md.cdaa1636.lean.js @@ -1 +1 @@ -import{_ as e,o as a,c as t,O as s}from"./chunks/framework.43781440.js";const m=JSON.parse('{"title":"隠し機能","description":"","frontmatter":{},"headers":[],"relativePath":"ja_JP/guide/hidden-features.md","filePath":"ja_JP/guide/hidden-features.md"}'),r={name:"ja_JP/guide/hidden-features.md"},d=s("",4),c=[d];function o(_,i,n,u,h,l){return a(),t("div",null,c)}const f=e(r,[["render",o]]);export{m as __pageData,f as default}; +import{_ as e,o as a,c as t,Q as s}from"./chunks/framework.ec8f7e8e.js";const m=JSON.parse('{"title":"隠し機能","description":"","frontmatter":{},"headers":[],"relativePath":"ja_JP/guide/hidden-features.md","filePath":"ja_JP/guide/hidden-features.md"}'),r={name:"ja_JP/guide/hidden-features.md"},d=s("",4),c=[d];function o(_,i,n,u,h,l){return a(),t("div",null,c)}const f=e(r,[["render",o]]);export{m as __pageData,f as default}; diff --git a/assets/ja_JP_guide_installation.md.2cc6f4de.js b/assets/ja_JP_guide_installation.md.7f4d5495.js similarity index 60% rename from assets/ja_JP_guide_installation.md.2cc6f4de.js rename to assets/ja_JP_guide_installation.md.7f4d5495.js index 111c98eff232..918bb67394ad 100644 --- a/assets/ja_JP_guide_installation.md.2cc6f4de.js +++ b/assets/ja_JP_guide_installation.md.7f4d5495.js @@ -1,3 +1,5 @@ -import{_ as e,o,c as a,O as l}from"./chunks/framework.43781440.js";const m=JSON.parse('{"title":"インストール","description":"","frontmatter":{},"headers":[],"relativePath":"ja_JP/guide/installation.md","filePath":"ja_JP/guide/installation.md"}'),t={name:"ja_JP/guide/installation.md"},i=l(`

インストール

デバイスが対応しているか確認する

GitHub Releases または Coolapk market から KernelSU Manager アプリをダウンロードし、お使いのデバイスにインストールしてください。

  • アプリが「非対応」と表示した場合は、自分でカーネルをコンパイルする必要があるという意味です。KernelSU は書き込むためのブートイメージを提供しません。
  • アプリが「未インストール」と表示した場合、お使いのデバイスは KernelSU に対応しています。

ヒント

非対応と表示されているデバイスについては、非公式の対応デバイスであればご自身でカーネルをビルドできます。

純正の boot.img をバックアップ

書き込む前に、まず純正の boot.img をバックアップする必要があります。ブートループが発生した場合は、fastboot を使用して純正のブートイメージを書き込むことでいつでもシステムを復旧できます。

警告

書き込みによりデータ損失を引き起こす可能性があります。次のステップに進む前に、このステップを必ず行うようにしてください!また、可能であればすべてのデータをバックアップしてください。

必要な知識

ADB と fastboot

このチュートリアルでは、デフォルトで ADB と fastboot のツールを使用します。ご存じない方は、まず検索エンジンを使って勉強されることをおすすめします。

KMI

同じ Kernel Module Interface (KMI) のカーネルバージョンは互換性があります。これが GKI の「汎用」という意味です。逆に言えば KMI が異なればカーネルには互換性がなく、お使いのデバイスと異なる KMI のカーネルイメージを書き込むと、ブートループが発生する場合があります。

具体的には GKI デバイスの場合、カーネルバージョンの形式は以下のようになります:

txt
KernelRelease :=
-Version.PatchLevel.SubLevel-AndroidRelease-KmiGeneration-suffix
-w      .x         .y       -zzz           -k            -something

w.x-zzz-k は KMI のバージョンです。例えば、デバイスのカーネルバージョンが 5.10.101-android12-9-g30979850fc20 である場合、その KMIは 5.10-android12-9 であり、理論的には他の KMI カーネルでも正常に起動できます。

ヒント

カーネルバージョンの SubLevel は、KMI の一部ではないことに注意してください。5.10.101-android12-9-g30979850fc205.10.137-android12-9-g30979850fc20 と同じ KMI を持っているということになります。

Kernel バージョンと Android バージョンの違い

注意: カーネルバージョンと Android バージョンは必ずしも同じではありません

カーネルのバージョンは「android12-5.10.101」なのに、Android システムのバージョンは「Android 13」などとなっている場合、驚かないでください。Linux カーネルのバージョン番号は、必ずしもデバイスの出荷時にプリインストールされている Android システムのバージョンと一致していません。Android システムが後でアップグレードされた場合、一般的にはカーネルのバージョンは変更されません。書き込む際は、必ずカーネルバージョンを参照してください!!!

インストール方法

KernelSU のインストール方法はいくつかあり、それぞれ適したシーンが異なりますので、必要に応じて選択してください。

  1. カスタムリカバリー(TWRPなど)でインストールする
  2. Franco Kernel Manager などのカーネル管理アプリでインストールする
  3. KernelSU が提供する boot.img を使用し、fastboot でインストールする
  4. boot.img を手動でパッチしてインストールする

カスタムリカバリーでインストール

前提条件:デバイスに TWRP などのカスタムリカバリーがあること。ない場合、または公式リカバリーしかない場合は他の方法を使用してください。

手順:

  1. KernelSUのリリースページから、お使いのデバイスのバージョンにあった AnyKernel3 で始まる ZIP パッケージをダウンロードします。例えば、デバイスのカーネルのバージョンがandroid12-5.10. 66の場合、AnyKernel3-android12-5.10.66_yyyy-MM.zip(yyyyは年、MMは月)のファイルをダウンロードします。
  2. デバイスを TWRP へ再起動します。
  3. adb を使用して AnyKernel3-*.zip をデバイスの /sdcard に入れ、TWRP GUI でインストールを選択します。または直接adb sideload AnyKernel-*.zip でインストールできます。

この方法は TWRP を使用できるならどのようなインストール(初期インストールやその後のアップグレード)にも適しています。

カーネル管理アプリでインストール

前提条件:お使いのデバイスが root 化されている必要があります。例えば、Magisk をインストールして root を取得した場合、または古いバージョンの KernelSU をインストールしており、別のバージョンの KernelSU にアップグレードする必要がある場合などです。お使いのデバイスが root 化されていない場合、他の方法をお試しください。

手順:

  1. AnyKernel3 ZIP をダウンロードします。ダウンロード方法は、「カスタムリカバリーでインストール」を参照してください。
  2. カーネル管理アプリを開き、AnyKernel3 の ZIP をインストールします。

カーネル管理アプリは以下のようなものが人気です:

  1. Kernel Flasher
  2. Franco Kernel Manager
  3. Ex Kernel Manager

この方法は KernelSU をアップグレードするときに便利で、パソコンがなくてもできます。(まずはバックアップしてください!)

KernelSU が提供する boot.img を使用してインストール

この方法は TWRP や root 権限を必要としないので、KernelSU を初めてインストールする場合に適しています。

正しい boot.img を見つける

KernelSU では、GKI デバイス用の汎用 boot.img を提供しています。デバイスの boot パーティションに boot.img をフラッシュする必要があります。

boot.img は、GitHub Release からダウンロードできます。例えば、あなたのデバイスがカーネル android12-5.10.101 の場合、android-5.10.101_yyyy-MM.boot-<format>.imgをダウンロードする必要があります。(KMI を同じにしてください!)。

<format>は純正 boot.img のカーネル圧縮形式を指します。純正の boot.img のカーネル圧縮形式を確認してください。間違った圧縮形式を使うと、ブートループするかもしれません。

情報

  1. magiskboot を使えば、元のブートの圧縮形式を知ることができます。もちろん、あなたのデバイスと同じモデルを持つ、より経験豊富な他の人にも聞くこともできます。また、カーネルの圧縮形式は通常変更されないので、ある圧縮形式でうまく起動した場合、後でその形式を試すことも可能です。
  2. Xiaomi デバイスでは通常 gz無圧縮が使われます。
  3. Pixel デバイスでは以下の手順に従ってください。

boot.img をデバイスに書き込む

adb でデバイスを接続し、adb reboot bootloader で fastboot モードにし、このコマンドで KernelSU を書き込んでください:

sh
fastboot flash boot boot.img

情報

デバイスが fastboot boot をサポートしている場合、まず fastboot boot.img を使えば書き込みせずにシステムを起動できます。予期せぬことが起こった場合は、もう一度再起動して起動してください。

再起動

書き込みが完了したら、デバイスを再起動します:

sh
fastboot reboot

boot.img を手動でパッチ

デバイスによっては、boot.img のフォーマットが lz4 でない、gz である、無圧縮であるなど、あまり一般的でないことがあります。最も典型的なのは Pixel で、boot.img フォーマットは lz4_legacy 圧縮、RAM ディスクは gzlz4_legacy 圧縮です。この時、KernelSU が提供した boot.img を直接書き込むとデバイスが起動できなくなる場合があります。その場合は手動で boot.img に対してパッチしてください。

パッチ方式は一般的に2種類あります:

  1. Android-Image-Kitchen
  2. magiskboot

このうち、Android-Image-Kitchen は PC での操作に適しており、magiskboot はデバイスとの連携が必要です。

準備

  1. お使いのデバイスの純正 boot.img を入手します。デバイスメーカーから入手できます。payload-dumper-goが必要かもしれません。
  2. お使いのデバイスの KMI バージョンに合った、KernelSU が提供する AnyKernel3 の ZIP ファイルをダウンロードします(カスタムリカバリーでインストールを参照してください)。
  3. AnyKernel3 パッケージを展開し、KernelSU のカーネルファイルである Image ファイルを取得します。

Android-Image-Kitchen を使う

  1. Android-Image-Kitchen を PC にダウンロードします。
  2. 純正 boot.img を Android-Image-Kitchen のルートフォルダに入れます。
  3. Android-Image-Kitchen のルートディレクトリで ./unpackimg.sh boot.img を実行して、boot.imgを展開します。
  4. split_img ディレクトリの boot.img-kernel を AnyKernel3 から展開した Image に置き換えます(boot.img-kernelに名前が変わっていることに注意してください)。
  5. Android-Image-Kitchen のルートディレクトリで ./repackimg.sh を実行すると、 image-new.img というファイルが生成されます。

magiskboot を使う

  1. 最新の Magisk をリリースページからダウンロードしてください。
  2. Magisk-*.apk を Magisk-vesion.zip に名前を変更して展開してください。
  3. Magisk-v25.2/lib/arm64-v8a/libmagiskboot.soを adb でデバイスに転送します:adb push Magisk-v25.2/lib/arm64-v8a/libmagiskboot.so /data/local/tmp/magiskboot
  4. 純正 boot.img と AnyKernel3 の中の Image をデバイスに転送します。
  5. adb shell に入り、cd /data/local/tmp/ し、chmod +x magiskboot を実行します。
  6. adb shell に入り、cd /data/local/tmp/ し、./magiskboot unpack boot.img を実行して boot.img を抽出します。kernel ファイルが純正カーネルです。
  7. kernelImage で置き換えます: mv -f Image kernel
  8. ./magiskboot repack boot.img を実行してブートイメージをリパックします。出来上がった new-boot.img を fastboot でデバイスに書き込んでください。

その他の方法

実はこれらのインストール方法はすべて、元のカーネルを KernelSU が提供するカーネルに置き換えるという主旨でしかなく、これが実現できれば他の方法でもインストール可能です:

  1. まず Magisk をインストールし、Magisk を通じて root 権限を取得し、カーネル管理アプリで KernelSU の AnyKernel ZIPをインストールする
  2. PC 上で何らかの書き込みツールを使用し、KernelSU が提供するカーネルを書き込む
`,63),r=[i];function n(s,d,c,p,h,b){return o(),a("div",null,r)}const k=e(t,[["render",n]]);export{m as __pageData,k as default}; +import{_ as e,o,c as a,Q as l}from"./chunks/framework.ec8f7e8e.js";const m=JSON.parse('{"title":"インストール","description":"","frontmatter":{},"headers":[],"relativePath":"ja_JP/guide/installation.md","filePath":"ja_JP/guide/installation.md"}'),t={name:"ja_JP/guide/installation.md"},i=l(`

インストール

デバイスが対応しているか確認する

GitHub Releases または Coolapk market から KernelSU Manager アプリをダウンロードし、お使いのデバイスにインストールしてください。

  • アプリが「非対応」と表示した場合は、自分でカーネルをコンパイルする必要があるという意味です。KernelSU は書き込むためのブートイメージを提供しません。
  • アプリが「未インストール」と表示した場合、お使いのデバイスは KernelSU に対応しています。

ヒント

非対応と表示されているデバイスについては、非公式の対応デバイスであればご自身でカーネルをビルドできます。

純正の boot.img をバックアップ

書き込む前に、まず純正の boot.img をバックアップする必要があります。ブートループが発生した場合は、fastboot を使用して純正のブートイメージを書き込むことでいつでもシステムを復旧できます。

警告

書き込みによりデータ損失を引き起こす可能性があります。次のステップに進む前に、このステップを必ず行うようにしてください!また、可能であればすべてのデータをバックアップしてください。

必要な知識

ADB と fastboot

このチュートリアルでは、デフォルトで ADB と fastboot のツールを使用します。ご存じない方は、まず検索エンジンを使って勉強されることをおすすめします。

KMI

同じ Kernel Module Interface (KMI) のカーネルバージョンは互換性があります。これが GKI の「汎用」という意味です。逆に言えば KMI が異なればカーネルには互換性がなく、お使いのデバイスと異なる KMI のカーネルイメージを書き込むと、ブートループが発生する場合があります。

具体的には GKI デバイスの場合、カーネルバージョンの形式は以下のようになります:

txt
KernelRelease :=
+Version.PatchLevel.SubLevel-AndroidRelease-KmiGeneration-suffix
+w      .x         .y       -zzz           -k            -something
KernelRelease :=
+Version.PatchLevel.SubLevel-AndroidRelease-KmiGeneration-suffix
+w      .x         .y       -zzz           -k            -something

w.x-zzz-k は KMI のバージョンです。例えば、デバイスのカーネルバージョンが 5.10.101-android12-9-g30979850fc20 である場合、その KMIは 5.10-android12-9 であり、理論的には他の KMI カーネルでも正常に起動できます。

ヒント

カーネルバージョンの SubLevel は、KMI の一部ではないことに注意してください。5.10.101-android12-9-g30979850fc205.10.137-android12-9-g30979850fc20 と同じ KMI を持っているということになります。

Kernel バージョンと Android バージョンの違い

注意: カーネルバージョンと Android バージョンは必ずしも同じではありません

カーネルのバージョンは「android12-5.10.101」なのに、Android システムのバージョンは「Android 13」などとなっている場合、驚かないでください。Linux カーネルのバージョン番号は、必ずしもデバイスの出荷時にプリインストールされている Android システムのバージョンと一致していません。Android システムが後でアップグレードされた場合、一般的にはカーネルのバージョンは変更されません。書き込む際は、必ずカーネルバージョンを参照してください!!!

インストール方法

KernelSU のインストール方法はいくつかあり、それぞれ適したシーンが異なりますので、必要に応じて選択してください。

  1. カスタムリカバリー(TWRPなど)でインストールする
  2. Franco Kernel Manager などのカーネル管理アプリでインストールする
  3. KernelSU が提供する boot.img を使用し、fastboot でインストールする
  4. boot.img を手動でパッチしてインストールする

カスタムリカバリーでインストール

前提条件:デバイスに TWRP などのカスタムリカバリーがあること。ない場合、または公式リカバリーしかない場合は他の方法を使用してください。

手順:

  1. KernelSUのリリースページから、お使いのデバイスのバージョンにあった AnyKernel3 で始まる ZIP パッケージをダウンロードします。例えば、デバイスのカーネルのバージョンがandroid12-5.10. 66の場合、AnyKernel3-android12-5.10.66_yyyy-MM.zip(yyyyは年、MMは月)のファイルをダウンロードします。
  2. デバイスを TWRP へ再起動します。
  3. adb を使用して AnyKernel3-*.zip をデバイスの /sdcard に入れ、TWRP GUI でインストールを選択します。または直接adb sideload AnyKernel-*.zip でインストールできます。

この方法は TWRP を使用できるならどのようなインストール(初期インストールやその後のアップグレード)にも適しています。

カーネル管理アプリでインストール

前提条件:お使いのデバイスが root 化されている必要があります。例えば、Magisk をインストールして root を取得した場合、または古いバージョンの KernelSU をインストールしており、別のバージョンの KernelSU にアップグレードする必要がある場合などです。お使いのデバイスが root 化されていない場合、他の方法をお試しください。

手順:

  1. AnyKernel3 ZIP をダウンロードします。ダウンロード方法は、「カスタムリカバリーでインストール」を参照してください。
  2. カーネル管理アプリを開き、AnyKernel3 の ZIP をインストールします。

カーネル管理アプリは以下のようなものが人気です:

  1. Kernel Flasher
  2. Franco Kernel Manager
  3. Ex Kernel Manager

この方法は KernelSU をアップグレードするときに便利で、パソコンがなくてもできます。(まずはバックアップしてください!)

KernelSU が提供する boot.img を使用してインストール

この方法は TWRP や root 権限を必要としないので、KernelSU を初めてインストールする場合に適しています。

正しい boot.img を見つける

KernelSU では、GKI デバイス用の汎用 boot.img を提供しています。デバイスの boot パーティションに boot.img をフラッシュする必要があります。

boot.img は、GitHub Release からダウンロードできます。例えば、あなたのデバイスがカーネル android12-5.10.101 の場合、android-5.10.101_yyyy-MM.boot-<format>.imgをダウンロードする必要があります。(KMI を同じにしてください!)。

<format>は純正 boot.img のカーネル圧縮形式を指します。純正の boot.img のカーネル圧縮形式を確認してください。間違った圧縮形式を使うと、ブートループするかもしれません。

情報

  1. magiskboot を使えば、元のブートの圧縮形式を知ることができます。もちろん、あなたのデバイスと同じモデルを持つ、より経験豊富な他の人にも聞くこともできます。また、カーネルの圧縮形式は通常変更されないので、ある圧縮形式でうまく起動した場合、後でその形式を試すことも可能です。
  2. Xiaomi デバイスでは通常 gz無圧縮が使われます。
  3. Pixel デバイスでは以下の手順に従ってください。

boot.img をデバイスに書き込む

adb でデバイスを接続し、adb reboot bootloader で fastboot モードにし、このコマンドで KernelSU を書き込んでください:

sh
fastboot flash boot boot.img
fastboot flash boot boot.img

情報

デバイスが fastboot boot をサポートしている場合、まず fastboot boot.img を使えば書き込みせずにシステムを起動できます。予期せぬことが起こった場合は、もう一度再起動して起動してください。

再起動

書き込みが完了したら、デバイスを再起動します:

sh
fastboot reboot
fastboot reboot

boot.img を手動でパッチ

デバイスによっては、boot.img のフォーマットが lz4 でない、gz である、無圧縮であるなど、あまり一般的でないことがあります。最も典型的なのは Pixel で、boot.img フォーマットは lz4_legacy 圧縮、RAM ディスクは gzlz4_legacy 圧縮です。この時、KernelSU が提供した boot.img を直接書き込むとデバイスが起動できなくなる場合があります。その場合は手動で boot.img に対してパッチしてください。

パッチ方式は一般的に2種類あります:

  1. Android-Image-Kitchen
  2. magiskboot

このうち、Android-Image-Kitchen は PC での操作に適しており、magiskboot はデバイスとの連携が必要です。

準備

  1. お使いのデバイスの純正 boot.img を入手します。デバイスメーカーから入手できます。payload-dumper-goが必要かもしれません。
  2. お使いのデバイスの KMI バージョンに合った、KernelSU が提供する AnyKernel3 の ZIP ファイルをダウンロードします(カスタムリカバリーでインストールを参照してください)。
  3. AnyKernel3 パッケージを展開し、KernelSU のカーネルファイルである Image ファイルを取得します。

Android-Image-Kitchen を使う

  1. Android-Image-Kitchen を PC にダウンロードします。
  2. 純正 boot.img を Android-Image-Kitchen のルートフォルダに入れます。
  3. Android-Image-Kitchen のルートディレクトリで ./unpackimg.sh boot.img を実行して、boot.imgを展開します。
  4. split_img ディレクトリの boot.img-kernel を AnyKernel3 から展開した Image に置き換えます(boot.img-kernelに名前が変わっていることに注意してください)。
  5. Android-Image-Kitchen のルートディレクトリで ./repackimg.sh を実行すると、 image-new.img というファイルが生成されます。

magiskboot を使う

  1. 最新の Magisk をリリースページからダウンロードしてください。
  2. Magisk-*.apk を Magisk-vesion.zip に名前を変更して展開してください。
  3. Magisk-v25.2/lib/arm64-v8a/libmagiskboot.soを adb でデバイスに転送します:adb push Magisk-v25.2/lib/arm64-v8a/libmagiskboot.so /data/local/tmp/magiskboot
  4. 純正 boot.img と AnyKernel3 の中の Image をデバイスに転送します。
  5. adb shell に入り、cd /data/local/tmp/ し、chmod +x magiskboot を実行します。
  6. adb shell に入り、cd /data/local/tmp/ し、./magiskboot unpack boot.img を実行して boot.img を抽出します。kernel ファイルが純正カーネルです。
  7. kernelImage で置き換えます: mv -f Image kernel
  8. ./magiskboot repack boot.img を実行してブートイメージをリパックします。出来上がった new-boot.img を fastboot でデバイスに書き込んでください。

その他の方法

実はこれらのインストール方法はすべて、元のカーネルを KernelSU が提供するカーネルに置き換えるという主旨でしかなく、これが実現できれば他の方法でもインストール可能です:

  1. まず Magisk をインストールし、Magisk を通じて root 権限を取得し、カーネル管理アプリで KernelSU の AnyKernel ZIPをインストールする
  2. PC 上で何らかの書き込みツールを使用し、KernelSU が提供するカーネルを書き込む
`,63),r=[i];function s(n,d,c,p,h,b){return o(),a("div",null,r)}const k=e(t,[["render",s]]);export{m as __pageData,k as default}; diff --git a/assets/ja_JP_guide_installation.md.2cc6f4de.lean.js b/assets/ja_JP_guide_installation.md.7f4d5495.lean.js similarity index 54% rename from assets/ja_JP_guide_installation.md.2cc6f4de.lean.js rename to assets/ja_JP_guide_installation.md.7f4d5495.lean.js index 055aa627da35..97b0c7ca7a03 100644 --- a/assets/ja_JP_guide_installation.md.2cc6f4de.lean.js +++ b/assets/ja_JP_guide_installation.md.7f4d5495.lean.js @@ -1 +1 @@ -import{_ as e,o,c as a,O as l}from"./chunks/framework.43781440.js";const m=JSON.parse('{"title":"インストール","description":"","frontmatter":{},"headers":[],"relativePath":"ja_JP/guide/installation.md","filePath":"ja_JP/guide/installation.md"}'),t={name:"ja_JP/guide/installation.md"},i=l("",63),r=[i];function n(s,d,c,p,h,b){return o(),a("div",null,r)}const k=e(t,[["render",n]]);export{m as __pageData,k as default}; +import{_ as e,o,c as a,Q as l}from"./chunks/framework.ec8f7e8e.js";const m=JSON.parse('{"title":"インストール","description":"","frontmatter":{},"headers":[],"relativePath":"ja_JP/guide/installation.md","filePath":"ja_JP/guide/installation.md"}'),t={name:"ja_JP/guide/installation.md"},i=l("",63),r=[i];function s(n,d,c,p,h,b){return o(),a("div",null,r)}const k=e(t,[["render",s]]);export{m as __pageData,k as default}; diff --git a/assets/ja_JP_guide_module.md.8abad8eb.lean.js b/assets/ja_JP_guide_module.md.8abad8eb.lean.js deleted file mode 100644 index 7cb4ca3c025a..000000000000 --- a/assets/ja_JP_guide_module.md.8abad8eb.lean.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as s,o as e,c as a,O as n}from"./chunks/framework.43781440.js";const y=JSON.parse('{"title":"モジュールのガイド","description":"","frontmatter":{},"headers":[],"relativePath":"ja_JP/guide/module.md","filePath":"ja_JP/guide/module.md"}'),l={name:"ja_JP/guide/module.md"},o=n("",58),p=[o];function t(c,i,r,d,A,C){return e(),a("div",null,p)}const m=s(l,[["render",t]]);export{y as __pageData,m as default}; diff --git a/assets/ja_JP_guide_module.md.8abad8eb.js b/assets/ja_JP_guide_module.md.dbefd5cf.js similarity index 58% rename from assets/ja_JP_guide_module.md.8abad8eb.js rename to assets/ja_JP_guide_module.md.dbefd5cf.js index 1342a4bd5caf..30c65f67bf2d 100644 --- a/assets/ja_JP_guide_module.md.8abad8eb.js +++ b/assets/ja_JP_guide_module.md.dbefd5cf.js @@ -1,81 +1,161 @@ -import{_ as s,o as e,c as a,O as n}from"./chunks/framework.43781440.js";const y=JSON.parse('{"title":"モジュールのガイド","description":"","frontmatter":{},"headers":[],"relativePath":"ja_JP/guide/module.md","filePath":"ja_JP/guide/module.md"}'),l={name:"ja_JP/guide/module.md"},o=n(`

モジュールのガイド

KernelSU はシステムパーティションの整合性を維持しながら、システムディレクトリを変更する効果を実現するモジュール機構を提供します。この機構は一般に「システムレス」と呼ばれています。

KernelSU のモジュール機構は、Magisk とほぼ同じです。Magisk のモジュール開発に慣れている方であれば、KernelSU のモジュール開発も簡単でしょう。その場合は以下のモジュールの紹介は読み飛ばして、Magisk との違いの内容だけ読めばOKです。

Busybox

KernelSU には、機能的に完全な Busybox バイナリ (SELinux の完全サポートを含む) が同梱されています。実行ファイルは /data/adb/ksu/bin/busybox に配置されています。KernelSU の Busybox はランタイムに切り替え可能な「ASH スタンドアローンシェルモード」をサポートしています。このスタンドアロンモードとは、Busybox の ash シェルで実行する場合 PATH として設定されているものに関係なく、すべてのコマンドが Busybox 内のアプレットを直接使用するというものです。たとえば、lsrmchmod などのコマンドは、PATH にあるもの(Android の場合、デフォルトではそれぞれ /system/bin/ls, /system/bin/rm, /system/bin/chmod)ではなく、直接 Busybox 内部のアプレットを呼び出すことになります。これにより、スクリプトは常に予測可能な環境で実行され、どの Android バージョンで実行されていても常にコマンドを利用できます。Busybox を使用しないコマンドを強制的に実行するには、フルパスで実行ファイルを呼び出す必要があります。

KernelSU のコンテキストで実行されるすべてのシェルスクリプトは、Busybox の ash シェルでスタンドアロンモードが有効な状態で実行されます。サードパーティの開発者に関係するものとしては、すべてのブートスクリプトとモジュールのインストールスクリプトが含まれます。

この「スタンドアロンモード」機能を KernelSU 以外で使用したい場合、2つの方法で有効にできます:

  1. 環境変数 ASH_STANDALONE1 にする
    例: ASH_STANDALONE=1 /data/adb/ksu/bin/busybox sh <script>
  2. コマンドラインのオプションで変更する:
    /data/adb/ksu/bin/busybox sh -o standalone <script>

環境変数が子プロセスに継承されるため、その後に実行されるすべての sh シェルもスタンドアロンモードで実行されるようにするにはオプション 1 が望ましい方法です(KernelSU と KernelSU Managerが内部的に使用しているのもこちらです)。

Magisk との違い

KernelSU の Busybox は、Magisk プロジェクトから直接コンパイルされたバイナリファイルを使用するようになりました。Magisk と KernelSU の Busybox スクリプトはまったく同じものなので、互換性の問題を心配する必要はありません!

KernelSU モジュール

KernelSU モジュールは、/data/adb/modules に配置された以下の構造を持つフォルダーです:

txt
/data/adb/modules
-├── .
-├── .
-|
-├── $MODID                  <--- フォルダの名前はモジュールの ID で付けます
-│   │
-│   │      *** モジュールの ID ***
-│   │
-│   ├── module.prop         <--- このファイルにモジュールのメタデータを保存します
-│   │
-│   │      *** メインコンテンツ ***
-│   │
-│   ├── system              <--- skip_mount が存在しない場合、このフォルダがマウントされます
-│   │   ├── ...
-│   │   ├── ...
-│   │   └── ...
-│   │
-│   │      *** ステータスフラグ ***
-│   │
-│   ├── skip_mount          <--- 存在する場合、KernelSU はシステムフォルダをマウントしません
-│   ├── disable             <--- 存在する場合、モジュールは無効化されます
-│   ├── remove              <--- 存在する場合、次の再起動時にモジュールが削除されます
-│   │
-│   │      *** 任意のファイル ***
-│   │
-│   ├── post-fs-data.sh     <--- このスクリプトは post-fs-data で実行されます
-│   ├── service.sh          <--- このスクリプトは late_start サービスで実行されます
-|   ├── uninstall.sh        <--- このスクリプトは KernelSU がモジュールを削除するときに実行されます
-│   ├── system.prop         <--- このファイルのプロパティは resetprop によってシステムプロパティとして読み込まれます
-│   ├── sepolicy.rule       <--- カスタム SEPolicy ルールを追加します
-│   │
-│   │      *** 自動生成されるため、手動で作成または変更しないでください ***
-│   │
-│   ├── vendor              <--- $MODID/system/vendor へのシンボリックリンク
-│   ├── product             <--- $MODID/system/product へのシンボリックリンク
-│   ├── system_ext          <--- $MODID/system/system_ext へのシンボリックリンク
-│   │
-│   │      *** その他のファイル/フォルダの追加も可能です ***
-│   │
-│   ├── ...
-│   └── ...
-|
-├── another_module
-│   ├── .
-│   └── .
-├── .
-├── .

Magisk との違い

KernelSU は Zygisk をビルトインでサポートしていないため、モジュール内に Zygisk に関連するコンテンツは存在しません。 しかし、ZygiskNext をインストールすれば Zygisk モジュールを使えます。その場合の Zygisk モジュールのコンテンツは Magisk と同じです。

module.prop

module.prop はモジュールの設定ファイルです。KernelSU ではこのファイルを含まないモジュールは、モジュールとして認識されません。このファイルの形式は以下の通りです:

txt
id=<string>
-name=<string>
-version=<string>
-versionCode=<int>
-author=<string>
-description=<string>
  • id はこの正規表現に一致していなければいけません: ^[a-zA-Z][a-zA-Z0-9._-]+$
    例: ✓ a_module, ✓ a.module, ✓ module-101, ✗ a module, ✗ 1_module, ✗ -a-module
    これはモジュールのユニークな ID です。公開後は変更しないでください。
  • versionCodeinteger です。バージョンの比較に使います。
  • 他のものには単一行 の文字であれば何でも使えます。
  • 改行文字は UNIX (LF) を使ってください。Windows (CR+LF)Macintosh (CR) は使ってはいけません。

シェルスクリプト

post-fs-data.shservice.sh の違いについては、ブートスクリプトのセクションを読んでください。ほとんどのモジュール開発者にとって、ブートスクリプトを実行するだけなら service.sh で十分なはずです。

モジュールのすべてのスクリプトでは、MODDIR=\${0%/*}を使えばモジュールのベースディレクトリのパスを取得できます。スクリプト内でモジュールのパスをハードコードしないでください。

Magisk との違い

環境変数 KSU を使用すると、スクリプトが KernelSU と Magisk どちらで実行されているかを判断できます。KernelSU で実行されている場合、この値は true に設定されます。

system ディレクトリ

このディレクトリの内容は、システムの起動後に OverlayFS を使用してシステムの /system パーティションの上にオーバーレイされます:

  1. システム内の対応するディレクトリにあるファイルと同名のファイルは、このディレクトリにあるファイルで上書きされます。
  2. システム内の対応するディレクトリにあるフォルダと同じ名前のフォルダは、このディレクトリにあるフォルダと統合されます。

元のシステムディレクトリにあるファイルやフォルダを削除したい場合は、mknod filename c 0 0 を使ってモジュールディレクトリにそのファイル/フォルダと同じ名前のファイルを作成する必要があります。こうすることで、OverlayFS システムはこのファイルを削除したかのように自動的に「ホワイトアウト」します(/system パーティションは実際には変更されません)。

また、customize.sh 内で REMOVE という変数に削除操作を実行するディレクトリのリストを宣言すると、KernelSU は自動的にそのモジュールの対応するディレクトリで mknod <TARGET> c 0 0 を実行します。例えば

sh
REMOVE="
-/system/app/YouTube
-/system/app/Bloatware
-"

上記の場合は、mknod $MODPATH/system/app/YouTuBe c 0 0mknod $MODPATH/system/app/Bloatware c 0 0を実行し、/system/app/YouTube/system/app/Bloatwareはモジュール有効化後に削除されます。

システム内のディレクトリを置き換えたい場合は、モジュールディレクトリに同じパスのディレクトリを作成し、このディレクトリに setfattr -n trusted.overlay.opaque -v y <TARGET> という属性を設定する必要があります。こうすることで、OverlayFS システムは(/system パーティションを変更することなく)システム内の対応するディレクトリを自動的に置き換えることができます。

customize.sh ファイル内に REPLACE という変数を宣言し、その中に置換するディレクトリのリストを入れておけば、KernelSU は自動的にモジュールディレクトリに対応した処理を行います。例えば:

REPLACE=" /system/app/YouTube /system/app/Bloatware "

このリストは、自動的に $MODPATH/system/app/YouTube$MODPATH/system/app/Bloatware というディレクトリを作成し、 setfattr -n trusted.overlay.opaque -v y $MODPATH/system/app/YouTubesetfattr -n trusted.overlay.opaque -v y $MODPATH/system/app/Bloatware を実行します。モジュールが有効になると、/system/app/YouTube/system/app/Bloatware は空のディレクトリに置き換えられます。

Magisk との違い

KernelSU のシステムレスメカニズムはカーネルの OverlayFS によって実装され、Magisk は現在マジックマウント(bind mount)を使用しています。この2つの実装方法には大きな違いがありますが最終的な目的は同じで、/system パーティションを物理的に変更することなく、/system のファイルを変更できます。

OverlayFS に興味があれば、Linux カーネルの OverlayFS のドキュメンテーション を読んでみてください。

system.prop

このファイルは build.prop と同じ形式をとっています。各行は [key]=[value] で構成されます。

sepolicy.rule

もしあなたのモジュールが追加の SEPolicy パッチを必要とする場合は、それらのルールをこのファイルに追加してください。このファイルの各行は、ポリシーステートメントとして扱われます。

モジュールのインストーラー

KernelSU モジュールインストーラーは、KernelSU Manager アプリでインストールできる、ZIP ファイルにパッケージされた KernelSU モジュールです。最もシンプルな KernelSU モジュールインストーラーは、KernelSU モジュールを ZIP ファイルとしてパックしただけのものです。

txt
module.zip
-
-├── customize.sh                       <--- (任意、詳細は後述)
-│                                           このスクリプトは update-binary から読み込まれます
-├── ...
-├── ...  /* 残りのモジュールのファイル */
-

警告

KernelSU モジュールは、カスタムリカバリーからのインストールには非対応です!

カスタマイズ

モジュールのインストールプロセスをカスタマイズする必要がある場合、customize.sh という名前のスクリプトを作成してください。このスクリプトは、すべてのファイルが抽出され、デフォルトのパーミッションと secontext が適用された後、モジュールインストーラースクリプトによって読み込み (実行ではなく) されます。これは、モジュールがデバイスの ABI に基づいて追加設定を必要とする場合や、モジュールファイルの一部に特別なパーミッション/コンテキストを設定する必要がある場合に、非常に便利です。

インストールプロセスを完全に制御しカスタマイズしたい場合は、customize.shSKIPUNZIP=1 と宣言すればデフォルトのインストールステップをすべてスキップできます。そうすることで、customize.sh が責任をもってすべてをインストールするようになります。

customize.shスクリプトは、KernelSU の Busybox ash シェルで、「スタンドアロンモード」を有効にして実行します。以下の変数と関数が利用可能です:

変数

  • KSU (bool): スクリプトが KernelSU 環境で実行されていることを示すための変数で、この変数の値は常に true になります。KernelSU と Magisk を区別するために使用できます。
  • KSU_VER (string): 現在インストールされている KernelSU のバージョン文字列 (例: v0.4.0)
  • KSU_VER_CODE (int): ユーザー空間に現在インストールされているKernelSUのバージョンコード (例: 10672)
  • KSU_KERNEL_VER_CODE (int): 現在インストールされている KernelSU のカーネル空間でのバージョンコード(例:10672
  • BOOTMODE (bool): KernelSU では常に true
  • MODPATH (path): モジュールファイルがインストールされるパス
  • TMPDIR (path): ファイルを一時的に保存しておく場所
  • ZIPFILE (path): あなたのモジュールのインストールZIP
  • ARCH (string): デバイスの CPU アーキテクチャ。値は armarm64x86x64 のいずれか
  • IS64BIT (bool): ARCHarm64 または x64 のときは true
  • API (int): 端末の API レベル・Android のバージョン(例:Android 6.0 なら23

警告

KernelSU では、MAGISK_VER_CODE は常に25200、MAGISK_VER は常にv25.2です。この2つの変数で KernelSU 上で動作しているかどうかを判断するのはやめてください。

機能

txt
ui_print <msg>
-    コンソールに <msg> を表示します
-    カスタムリカバリーのコンソールでは表示されないため、「echo」の使用は避けてください
-
-abort <msg>
-    エラーメッセージ<msg>をコンソールに出力し、インストールを終了させます
-    終了時のクリーンアップがスキップされてしまうため、「exit」の使用は避けてください
-
-set_perm <target> <owner> <group> <permission> [context]
-    [context] が設定されていない場合、デフォルトは "u:object_r:system_file:s0" です。
-    この機能は、次のコマンドの略記です:
-       chown owner.group target
-       chmod permission target
-       chcon context target
-
-set_perm_recursive <directory> <owner> <group> <dirpermission> <filepermission> [context]
-    [context] が設定されていない場合、デフォルトは "u:object_r:system_file:s0" です。
-    <directory> 内のすべてのファイルに対しては以下が実行されます:
-       set_perm file owner group filepermission context
-    <directory> 内のすべてのディレクトリ(自身を含む)に対しては以下が実行されます:
-       set_perm dir owner group dirpermission context

ブートスクリプト

KernelSU では、スクリプトは実行モードによって post-fs-data モードと late_start サービスモードの2種類に分けられます:

  • post-fs-data モード
    • 同期処理です。実行が終わるか、10秒が経過するまでブートプロセスが一時停止されます。
    • スクリプトはモジュールがマウントされる前に実行されます。モジュール開発者はモジュールがマウントされる前に、動的にモジュールを調整できます。
    • このステージは Zygote が始まる前に起こるので、Android のほぼすべての処理の前に割り込めます
    • 警告: setprop を使うとブートプロセスのデッドロックを引き起こします! resetprop -n <prop_name> <prop_value> を使ってください。
    • 本当に必要な場合だけこのモードでコマンド実行してください
  • late_start サービスモード
    • 非同期処理です。スクリプトは、起動プロセスの残りの部分と並行して実行されます。
    • ほとんどのスクリプトにはこちらがおすすめです

KernelSU では、起動スクリプトは保存場所によって一般スクリプトとモジュールスクリプトの2種類に分けられます:

  • 一般スクリプト
    • /data/adb/post-fs-data.d/data/adb/service.d に配置されます
    • スクリプトが実行可能な状態に設定されている場合にのみ実行されます (chmod +x script.sh)
    • post-fs-data.d のスクリプトは post-fs-data モードで実行され、service.d のスクリプトは late_start サービスモードで実行されます
    • モジュールはインストール時に一般スクリプトを追加するべきではありません
  • モジュールスクリプト
    • モジュール独自のフォルダに配置されます
    • モジュールが有効な場合のみ実行されます
    • post-fs-data.sh は post-fs-data モードで実行され、service.sh は late_start サービスモードで実行されます

すべてのブートスクリプトは、KernelSU の Busybox ash シェルで「スタンドアロンモード」を有効にした状態で実行されます。

`,58),p=[o];function t(c,i,r,d,A,C){return e(),a("div",null,p)}const m=s(l,[["render",t]]);export{y as __pageData,m as default}; +import{_ as s,o as e,c as n,Q as a}from"./chunks/framework.ec8f7e8e.js";const g=JSON.parse('{"title":"モジュールのガイド","description":"","frontmatter":{},"headers":[],"relativePath":"ja_JP/guide/module.md","filePath":"ja_JP/guide/module.md"}'),l={name:"ja_JP/guide/module.md"},p=a(`

モジュールのガイド

KernelSU はシステムパーティションの整合性を維持しながら、システムディレクトリを変更する効果を実現するモジュール機構を提供します。この機構は一般に「システムレス」と呼ばれています。

KernelSU のモジュール機構は、Magisk とほぼ同じです。Magisk のモジュール開発に慣れている方であれば、KernelSU のモジュール開発も簡単でしょう。その場合は以下のモジュールの紹介は読み飛ばして、Magisk との違いの内容だけ読めばOKです。

Busybox

KernelSU には、機能的に完全な Busybox バイナリ (SELinux の完全サポートを含む) が同梱されています。実行ファイルは /data/adb/ksu/bin/busybox に配置されています。KernelSU の Busybox はランタイムに切り替え可能な「ASH スタンドアローンシェルモード」をサポートしています。このスタンドアロンモードとは、Busybox の ash シェルで実行する場合 PATH として設定されているものに関係なく、すべてのコマンドが Busybox 内のアプレットを直接使用するというものです。たとえば、lsrmchmod などのコマンドは、PATH にあるもの(Android の場合、デフォルトではそれぞれ /system/bin/ls, /system/bin/rm, /system/bin/chmod)ではなく、直接 Busybox 内部のアプレットを呼び出すことになります。これにより、スクリプトは常に予測可能な環境で実行され、どの Android バージョンで実行されていても常にコマンドを利用できます。Busybox を使用しないコマンドを強制的に実行するには、フルパスで実行ファイルを呼び出す必要があります。

KernelSU のコンテキストで実行されるすべてのシェルスクリプトは、Busybox の ash シェルでスタンドアロンモードが有効な状態で実行されます。サードパーティの開発者に関係するものとしては、すべてのブートスクリプトとモジュールのインストールスクリプトが含まれます。

この「スタンドアロンモード」機能を KernelSU 以外で使用したい場合、2つの方法で有効にできます:

  1. 環境変数 ASH_STANDALONE1 にする
    例: ASH_STANDALONE=1 /data/adb/ksu/bin/busybox sh <script>
  2. コマンドラインのオプションで変更する:
    /data/adb/ksu/bin/busybox sh -o standalone <script>

環境変数が子プロセスに継承されるため、その後に実行されるすべての sh シェルもスタンドアロンモードで実行されるようにするにはオプション 1 が望ましい方法です(KernelSU と KernelSU Managerが内部的に使用しているのもこちらです)。

Magisk との違い

KernelSU の Busybox は、Magisk プロジェクトから直接コンパイルされたバイナリファイルを使用するようになりました。Magisk と KernelSU の Busybox スクリプトはまったく同じものなので、互換性の問題を心配する必要はありません!

KernelSU モジュール

KernelSU モジュールは、/data/adb/modules に配置された以下の構造を持つフォルダーです:

txt
/data/adb/modules
+├── .
+├── .
+|
+├── $MODID                  <--- フォルダの名前はモジュールの ID で付けます
+│   │
+│   │      *** モジュールの ID ***
+│   │
+│   ├── module.prop         <--- このファイルにモジュールのメタデータを保存します
+│   │
+│   │      *** メインコンテンツ ***
+│   │
+│   ├── system              <--- skip_mount が存在しない場合、このフォルダがマウントされます
+│   │   ├── ...
+│   │   ├── ...
+│   │   └── ...
+│   │
+│   │      *** ステータスフラグ ***
+│   │
+│   ├── skip_mount          <--- 存在する場合、KernelSU はシステムフォルダをマウントしません
+│   ├── disable             <--- 存在する場合、モジュールは無効化されます
+│   ├── remove              <--- 存在する場合、次の再起動時にモジュールが削除されます
+│   │
+│   │      *** 任意のファイル ***
+│   │
+│   ├── post-fs-data.sh     <--- このスクリプトは post-fs-data で実行されます
+│   ├── service.sh          <--- このスクリプトは late_start サービスで実行されます
+|   ├── uninstall.sh        <--- このスクリプトは KernelSU がモジュールを削除するときに実行されます
+│   ├── system.prop         <--- このファイルのプロパティは resetprop によってシステムプロパティとして読み込まれます
+│   ├── sepolicy.rule       <--- カスタム SEPolicy ルールを追加します
+│   │
+│   │      *** 自動生成されるため、手動で作成または変更しないでください ***
+│   │
+│   ├── vendor              <--- $MODID/system/vendor へのシンボリックリンク
+│   ├── product             <--- $MODID/system/product へのシンボリックリンク
+│   ├── system_ext          <--- $MODID/system/system_ext へのシンボリックリンク
+│   │
+│   │      *** その他のファイル/フォルダの追加も可能です ***
+│   │
+│   ├── ...
+│   └── ...
+|
+├── another_module
+│   ├── .
+│   └── .
+├── .
+├── .
/data/adb/modules
+├── .
+├── .
+|
+├── $MODID                  <--- フォルダの名前はモジュールの ID で付けます
+│   │
+│   │      *** モジュールの ID ***
+│   │
+│   ├── module.prop         <--- このファイルにモジュールのメタデータを保存します
+│   │
+│   │      *** メインコンテンツ ***
+│   │
+│   ├── system              <--- skip_mount が存在しない場合、このフォルダがマウントされます
+│   │   ├── ...
+│   │   ├── ...
+│   │   └── ...
+│   │
+│   │      *** ステータスフラグ ***
+│   │
+│   ├── skip_mount          <--- 存在する場合、KernelSU はシステムフォルダをマウントしません
+│   ├── disable             <--- 存在する場合、モジュールは無効化されます
+│   ├── remove              <--- 存在する場合、次の再起動時にモジュールが削除されます
+│   │
+│   │      *** 任意のファイル ***
+│   │
+│   ├── post-fs-data.sh     <--- このスクリプトは post-fs-data で実行されます
+│   ├── service.sh          <--- このスクリプトは late_start サービスで実行されます
+|   ├── uninstall.sh        <--- このスクリプトは KernelSU がモジュールを削除するときに実行されます
+│   ├── system.prop         <--- このファイルのプロパティは resetprop によってシステムプロパティとして読み込まれます
+│   ├── sepolicy.rule       <--- カスタム SEPolicy ルールを追加します
+│   │
+│   │      *** 自動生成されるため、手動で作成または変更しないでください ***
+│   │
+│   ├── vendor              <--- $MODID/system/vendor へのシンボリックリンク
+│   ├── product             <--- $MODID/system/product へのシンボリックリンク
+│   ├── system_ext          <--- $MODID/system/system_ext へのシンボリックリンク
+│   │
+│   │      *** その他のファイル/フォルダの追加も可能です ***
+│   │
+│   ├── ...
+│   └── ...
+|
+├── another_module
+│   ├── .
+│   └── .
+├── .
+├── .

Magisk との違い

KernelSU は Zygisk をビルトインでサポートしていないため、モジュール内に Zygisk に関連するコンテンツは存在しません。 しかし、ZygiskNext をインストールすれば Zygisk モジュールを使えます。その場合の Zygisk モジュールのコンテンツは Magisk と同じです。

module.prop

module.prop はモジュールの設定ファイルです。KernelSU ではこのファイルを含まないモジュールは、モジュールとして認識されません。このファイルの形式は以下の通りです:

txt
id=<string>
+name=<string>
+version=<string>
+versionCode=<int>
+author=<string>
+description=<string>
id=<string>
+name=<string>
+version=<string>
+versionCode=<int>
+author=<string>
+description=<string>
  • id はこの正規表現に一致していなければいけません: ^[a-zA-Z][a-zA-Z0-9._-]+$
    例: ✓ a_module, ✓ a.module, ✓ module-101, ✗ a module, ✗ 1_module, ✗ -a-module
    これはモジュールのユニークな ID です。公開後は変更しないでください。
  • versionCodeinteger です。バージョンの比較に使います。
  • 他のものには単一行 の文字であれば何でも使えます。
  • 改行文字は UNIX (LF) を使ってください。Windows (CR+LF)Macintosh (CR) は使ってはいけません。

シェルスクリプト

post-fs-data.shservice.sh の違いについては、ブートスクリプトのセクションを読んでください。ほとんどのモジュール開発者にとって、ブートスクリプトを実行するだけなら service.sh で十分なはずです。

モジュールのすべてのスクリプトでは、MODDIR=\${0%/*}を使えばモジュールのベースディレクトリのパスを取得できます。スクリプト内でモジュールのパスをハードコードしないでください。

Magisk との違い

環境変数 KSU を使用すると、スクリプトが KernelSU と Magisk どちらで実行されているかを判断できます。KernelSU で実行されている場合、この値は true に設定されます。

system ディレクトリ

このディレクトリの内容は、システムの起動後に OverlayFS を使用してシステムの /system パーティションの上にオーバーレイされます:

  1. システム内の対応するディレクトリにあるファイルと同名のファイルは、このディレクトリにあるファイルで上書きされます。
  2. システム内の対応するディレクトリにあるフォルダと同じ名前のフォルダは、このディレクトリにあるフォルダと統合されます。

元のシステムディレクトリにあるファイルやフォルダを削除したい場合は、mknod filename c 0 0 を使ってモジュールディレクトリにそのファイル/フォルダと同じ名前のファイルを作成する必要があります。こうすることで、OverlayFS システムはこのファイルを削除したかのように自動的に「ホワイトアウト」します(/system パーティションは実際には変更されません)。

また、customize.sh 内で REMOVE という変数に削除操作を実行するディレクトリのリストを宣言すると、KernelSU は自動的にそのモジュールの対応するディレクトリで mknod <TARGET> c 0 0 を実行します。例えば

sh
REMOVE="
+/system/app/YouTube
+/system/app/Bloatware
+"
REMOVE="
+/system/app/YouTube
+/system/app/Bloatware
+"

上記の場合は、mknod $MODPATH/system/app/YouTuBe c 0 0mknod $MODPATH/system/app/Bloatware c 0 0を実行し、/system/app/YouTube/system/app/Bloatwareはモジュール有効化後に削除されます。

システム内のディレクトリを置き換えたい場合は、モジュールディレクトリに同じパスのディレクトリを作成し、このディレクトリに setfattr -n trusted.overlay.opaque -v y <TARGET> という属性を設定する必要があります。こうすることで、OverlayFS システムは(/system パーティションを変更することなく)システム内の対応するディレクトリを自動的に置き換えることができます。

customize.sh ファイル内に REPLACE という変数を宣言し、その中に置換するディレクトリのリストを入れておけば、KernelSU は自動的にモジュールディレクトリに対応した処理を行います。例えば:

REPLACE=" /system/app/YouTube /system/app/Bloatware "

このリストは、自動的に $MODPATH/system/app/YouTube$MODPATH/system/app/Bloatware というディレクトリを作成し、 setfattr -n trusted.overlay.opaque -v y $MODPATH/system/app/YouTubesetfattr -n trusted.overlay.opaque -v y $MODPATH/system/app/Bloatware を実行します。モジュールが有効になると、/system/app/YouTube/system/app/Bloatware は空のディレクトリに置き換えられます。

Magisk との違い

KernelSU のシステムレスメカニズムはカーネルの OverlayFS によって実装され、Magisk は現在マジックマウント(bind mount)を使用しています。この2つの実装方法には大きな違いがありますが最終的な目的は同じで、/system パーティションを物理的に変更することなく、/system のファイルを変更できます。

OverlayFS に興味があれば、Linux カーネルの OverlayFS のドキュメンテーション を読んでみてください。

system.prop

このファイルは build.prop と同じ形式をとっています。各行は [key]=[value] で構成されます。

sepolicy.rule

もしあなたのモジュールが追加の SEPolicy パッチを必要とする場合は、それらのルールをこのファイルに追加してください。このファイルの各行は、ポリシーステートメントとして扱われます。

モジュールのインストーラー

KernelSU モジュールインストーラーは、KernelSU Manager アプリでインストールできる、ZIP ファイルにパッケージされた KernelSU モジュールです。最もシンプルな KernelSU モジュールインストーラーは、KernelSU モジュールを ZIP ファイルとしてパックしただけのものです。

txt
module.zip
+
+├── customize.sh                       <--- (任意、詳細は後述)
+│                                           このスクリプトは update-binary から読み込まれます
+├── ...
+├── ...  /* 残りのモジュールのファイル */
+
module.zip
+
+├── customize.sh                       <--- (任意、詳細は後述)
+│                                           このスクリプトは update-binary から読み込まれます
+├── ...
+├── ...  /* 残りのモジュールのファイル */
+

警告

KernelSU モジュールは、カスタムリカバリーからのインストールには非対応です!

カスタマイズ

モジュールのインストールプロセスをカスタマイズする必要がある場合、customize.sh という名前のスクリプトを作成してください。このスクリプトは、すべてのファイルが抽出され、デフォルトのパーミッションと secontext が適用された後、モジュールインストーラースクリプトによって読み込み (実行ではなく) されます。これは、モジュールがデバイスの ABI に基づいて追加設定を必要とする場合や、モジュールファイルの一部に特別なパーミッション/コンテキストを設定する必要がある場合に、非常に便利です。

インストールプロセスを完全に制御しカスタマイズしたい場合は、customize.shSKIPUNZIP=1 と宣言すればデフォルトのインストールステップをすべてスキップできます。そうすることで、customize.sh が責任をもってすべてをインストールするようになります。

customize.shスクリプトは、KernelSU の Busybox ash シェルで、「スタンドアロンモード」を有効にして実行します。以下の変数と関数が利用可能です:

変数

  • KSU (bool): スクリプトが KernelSU 環境で実行されていることを示すための変数で、この変数の値は常に true になります。KernelSU と Magisk を区別するために使用できます。
  • KSU_VER (string): 現在インストールされている KernelSU のバージョン文字列 (例: v0.4.0)
  • KSU_VER_CODE (int): ユーザー空間に現在インストールされているKernelSUのバージョンコード (例: 10672)
  • KSU_KERNEL_VER_CODE (int): 現在インストールされている KernelSU のカーネル空間でのバージョンコード(例:10672
  • BOOTMODE (bool): KernelSU では常に true
  • MODPATH (path): モジュールファイルがインストールされるパス
  • TMPDIR (path): ファイルを一時的に保存しておく場所
  • ZIPFILE (path): あなたのモジュールのインストールZIP
  • ARCH (string): デバイスの CPU アーキテクチャ。値は armarm64x86x64 のいずれか
  • IS64BIT (bool): ARCHarm64 または x64 のときは true
  • API (int): 端末の API レベル・Android のバージョン(例:Android 6.0 なら23

警告

KernelSU では、MAGISK_VER_CODE は常に25200、MAGISK_VER は常にv25.2です。この2つの変数で KernelSU 上で動作しているかどうかを判断するのはやめてください。

機能

txt
ui_print <msg>
+    コンソールに <msg> を表示します
+    カスタムリカバリーのコンソールでは表示されないため、「echo」の使用は避けてください
+
+abort <msg>
+    エラーメッセージ<msg>をコンソールに出力し、インストールを終了させます
+    終了時のクリーンアップがスキップされてしまうため、「exit」の使用は避けてください
+
+set_perm <target> <owner> <group> <permission> [context]
+    [context] が設定されていない場合、デフォルトは "u:object_r:system_file:s0" です。
+    この機能は、次のコマンドの略記です:
+       chown owner.group target
+       chmod permission target
+       chcon context target
+
+set_perm_recursive <directory> <owner> <group> <dirpermission> <filepermission> [context]
+    [context] が設定されていない場合、デフォルトは "u:object_r:system_file:s0" です。
+    <directory> 内のすべてのファイルに対しては以下が実行されます:
+       set_perm file owner group filepermission context
+    <directory> 内のすべてのディレクトリ(自身を含む)に対しては以下が実行されます:
+       set_perm dir owner group dirpermission context
ui_print <msg>
+    コンソールに <msg> を表示します
+    カスタムリカバリーのコンソールでは表示されないため、「echo」の使用は避けてください
+
+abort <msg>
+    エラーメッセージ<msg>をコンソールに出力し、インストールを終了させます
+    終了時のクリーンアップがスキップされてしまうため、「exit」の使用は避けてください
+
+set_perm <target> <owner> <group> <permission> [context]
+    [context] が設定されていない場合、デフォルトは "u:object_r:system_file:s0" です。
+    この機能は、次のコマンドの略記です:
+       chown owner.group target
+       chmod permission target
+       chcon context target
+
+set_perm_recursive <directory> <owner> <group> <dirpermission> <filepermission> [context]
+    [context] が設定されていない場合、デフォルトは "u:object_r:system_file:s0" です。
+    <directory> 内のすべてのファイルに対しては以下が実行されます:
+       set_perm file owner group filepermission context
+    <directory> 内のすべてのディレクトリ(自身を含む)に対しては以下が実行されます:
+       set_perm dir owner group dirpermission context

ブートスクリプト

KernelSU では、スクリプトは実行モードによって post-fs-data モードと late_start サービスモードの2種類に分けられます:

  • post-fs-data モード
    • 同期処理です。実行が終わるか、10秒が経過するまでブートプロセスが一時停止されます。
    • スクリプトはモジュールがマウントされる前に実行されます。モジュール開発者はモジュールがマウントされる前に、動的にモジュールを調整できます。
    • このステージは Zygote が始まる前に起こるので、Android のほぼすべての処理の前に割り込めます
    • 警告: setprop を使うとブートプロセスのデッドロックを引き起こします! resetprop -n <prop_name> <prop_value> を使ってください。
    • 本当に必要な場合だけこのモードでコマンド実行してください
  • late_start サービスモード
    • 非同期処理です。スクリプトは、起動プロセスの残りの部分と並行して実行されます。
    • ほとんどのスクリプトにはこちらがおすすめです

KernelSU では、起動スクリプトは保存場所によって一般スクリプトとモジュールスクリプトの2種類に分けられます:

  • 一般スクリプト
    • /data/adb/post-fs-data.d/data/adb/service.d に配置されます
    • スクリプトが実行可能な状態に設定されている場合にのみ実行されます (chmod +x script.sh)
    • post-fs-data.d のスクリプトは post-fs-data モードで実行され、service.d のスクリプトは late_start サービスモードで実行されます
    • モジュールはインストール時に一般スクリプトを追加するべきではありません
  • モジュールスクリプト
    • モジュール独自のフォルダに配置されます
    • モジュールが有効な場合のみ実行されます
    • post-fs-data.sh は post-fs-data モードで実行され、service.sh は late_start サービスモードで実行されます

すべてのブートスクリプトは、KernelSU の Busybox ash シェルで「スタンドアロンモード」を有効にした状態で実行されます。

`,58),o=[p];function t(c,r,i,d,y,u){return e(),n("div",null,o)}const h=s(l,[["render",t]]);export{g as __pageData,h as default}; diff --git a/assets/ja_JP_guide_module.md.dbefd5cf.lean.js b/assets/ja_JP_guide_module.md.dbefd5cf.lean.js new file mode 100644 index 000000000000..b6d3a660d3a8 --- /dev/null +++ b/assets/ja_JP_guide_module.md.dbefd5cf.lean.js @@ -0,0 +1 @@ +import{_ as s,o as e,c as n,Q as a}from"./chunks/framework.ec8f7e8e.js";const g=JSON.parse('{"title":"モジュールのガイド","description":"","frontmatter":{},"headers":[],"relativePath":"ja_JP/guide/module.md","filePath":"ja_JP/guide/module.md"}'),l={name:"ja_JP/guide/module.md"},p=a("",58),o=[p];function t(c,r,i,d,y,u){return e(),n("div",null,o)}const h=s(l,[["render",t]]);export{g as __pageData,h as default}; diff --git a/assets/ja_JP_guide_rescue-from-bootloop.md.d26f95c4.js b/assets/ja_JP_guide_rescue-from-bootloop.md.7bb5da8e.js similarity index 98% rename from assets/ja_JP_guide_rescue-from-bootloop.md.d26f95c4.js rename to assets/ja_JP_guide_rescue-from-bootloop.md.7bb5da8e.js index dfdbdc8211fc..405d73df3edd 100644 --- a/assets/ja_JP_guide_rescue-from-bootloop.md.d26f95c4.js +++ b/assets/ja_JP_guide_rescue-from-bootloop.md.7bb5da8e.js @@ -1 +1 @@ -import{_ as o,o as e,c as a,O as t}from"./chunks/framework.43781440.js";const u=JSON.parse('{"title":"ブートループからの復旧","description":"","frontmatter":{},"headers":[],"relativePath":"ja_JP/guide/rescue-from-bootloop.md","filePath":"ja_JP/guide/rescue-from-bootloop.md"}'),r={name:"ja_JP/guide/rescue-from-bootloop.md"},l=t('

ブートループからの復旧

デバイスに書き込む際、デバイスが「文鎮化」状態になる場面に遭遇することがあります。理論的には、fastboot で boot パーティションを書き込んだだけだったり、不適切なモジュールをインストールしてデバイスが起動しなくなったりした場合なら、適切な操作で復旧できます。このページでは、「文鎮化」状態になったデバイスを復旧させるための緊急手段を紹介します。

boot パーティションの書き込みによる文鎮化

KernelSU では、以下のような状況で boot パーティションを書き込んだときに文鎮化する場合があります:

  1. 間違った形式の boot イメージを書き込んでしまった場合。例えばお使いのデバイスのパーティション形式が gz なのに lz4 形式のイメージを書き込んでしまうと、起動しなくなります。
  2. お使いのデバイスが起動するために AVB 検証を無効にする必要がある場合(通常、無効にするにはすべてのデータを消去する必要があります)。
  3. カーネルにバグがある、または書き込みに適していない場合。

どのような状況であっても、純正の boot イメージを書き込むことで復旧できます。したがって、インストールする前にまずは純正の boot パーティションをバックアップすることを強くおすすめします。バックアップしていない場合は、あなたと同じデバイスを持つ他のユーザー、または公式ファームウェアから純正の boot イメージを入手できます。

モジュールによる文鎮化

モジュールのインストールはデバイスを文鎮化させる一般的な原因です。モジュールを未知のソースからインストールしないでください!モジュールは root 権限を持つため、あなたのデバイスに不可逆的なダメージを与える可能性があります!

通常のモジュール

安全であることが確認されているモジュールをインストールしてデバイスが起動しなくなった場合、KernelSU では心配することなく簡単に復旧できます。KernelSU には、以下のようなデバイスを救出するための仕組みが組み込まれています:

  1. AB アップデート
  2. 音量下ボタンでの復旧

AB アップデート

KernelSU のモジュール更新は、OTA アップデートで使用される Android システムの AB アップデート機構からヒントを得ています。新しいモジュールをインストールしたり、既存のモジュールを更新したりする場合、現在使用されているモジュールファイルを直接変更することはありません。代わりに、すべてのモジュールが別のアップデートイメージに組み込まれます。システムが再起動された後、このアップデートイメージの使用を開始しようとします。Android システムが正常に起動した場合、モジュールは本当に更新されます。

そのため、デバイスを復旧する最もシンプルで一般的な方法は、強制的に再起動することです。モジュールをインストールした後にシステムを起動できなくなった場合、電源ボタンを10秒以上長押しするとシステムが自動的に再起動します。再起動後はモジュールを更新する前の状態にロールバックされ、以前に更新したモジュールは自動的に無効化されます。

音量下ボタンでの復旧

AB アップデートでも解決しない場合は、セーフモードを使用してみてください。セーフモードでは、すべてのモジュールが無効化されます。

セーフモードに入るには、2つの方法があります:

  1. 一部のシステムの内蔵セーフモード:音量下ボタンの長押しでセーフモードに入れるシステムもあれば、リカバリーでセーフモードに入れるシステム(MIUI など)もあります。システムのセーフモードに入ると KernelSU もセーフモードに入り、自動的にモジュールを無効化します。
  2. KernelSU の内蔵セーフモード:最初の起動画面の後、音量下キーを3回以上連続して押すと入れます。なお、押す→離すを三回繰り返すのであって、長押しではありません。

セーフモードに入ると、KernelSU Manager のモジュールページにあるすべてのモジュールが無効になります。「アンインストール」操作を行うことで、問題を起こしている可能性のあるモジュールをアンインストールできます。

内蔵のセーフモードはカーネルに実装されているため、キーイベントを見逃す可能性はありません。ただし、GKI 以外のカーネルでは手動によるコードの統合が必要な場合があるため、公式ドキュメントを参考にしてください。

悪意のあるモジュール

上記の方法でデバイスを救出できない場合、インストールしたモジュールが悪意のある操作をしているか、他の手段でデバイスを損傷している可能性が高いです。この場合、2つの方法しかありません:

  1. データを消去して純正システムをインストールし直す
  2. アフターセールスサービスに問い合わせする
',23),i=[l];function n(s,d,c,h,p,b){return e(),a("div",null,i)}const m=o(r,[["render",n]]);export{u as __pageData,m as default}; +import{_ as o,o as e,c as a,Q as t}from"./chunks/framework.ec8f7e8e.js";const u=JSON.parse('{"title":"ブートループからの復旧","description":"","frontmatter":{},"headers":[],"relativePath":"ja_JP/guide/rescue-from-bootloop.md","filePath":"ja_JP/guide/rescue-from-bootloop.md"}'),r={name:"ja_JP/guide/rescue-from-bootloop.md"},l=t('

ブートループからの復旧

デバイスに書き込む際、デバイスが「文鎮化」状態になる場面に遭遇することがあります。理論的には、fastboot で boot パーティションを書き込んだだけだったり、不適切なモジュールをインストールしてデバイスが起動しなくなったりした場合なら、適切な操作で復旧できます。このページでは、「文鎮化」状態になったデバイスを復旧させるための緊急手段を紹介します。

boot パーティションの書き込みによる文鎮化

KernelSU では、以下のような状況で boot パーティションを書き込んだときに文鎮化する場合があります:

  1. 間違った形式の boot イメージを書き込んでしまった場合。例えばお使いのデバイスのパーティション形式が gz なのに lz4 形式のイメージを書き込んでしまうと、起動しなくなります。
  2. お使いのデバイスが起動するために AVB 検証を無効にする必要がある場合(通常、無効にするにはすべてのデータを消去する必要があります)。
  3. カーネルにバグがある、または書き込みに適していない場合。

どのような状況であっても、純正の boot イメージを書き込むことで復旧できます。したがって、インストールする前にまずは純正の boot パーティションをバックアップすることを強くおすすめします。バックアップしていない場合は、あなたと同じデバイスを持つ他のユーザー、または公式ファームウェアから純正の boot イメージを入手できます。

モジュールによる文鎮化

モジュールのインストールはデバイスを文鎮化させる一般的な原因です。モジュールを未知のソースからインストールしないでください!モジュールは root 権限を持つため、あなたのデバイスに不可逆的なダメージを与える可能性があります!

通常のモジュール

安全であることが確認されているモジュールをインストールしてデバイスが起動しなくなった場合、KernelSU では心配することなく簡単に復旧できます。KernelSU には、以下のようなデバイスを救出するための仕組みが組み込まれています:

  1. AB アップデート
  2. 音量下ボタンでの復旧

AB アップデート

KernelSU のモジュール更新は、OTA アップデートで使用される Android システムの AB アップデート機構からヒントを得ています。新しいモジュールをインストールしたり、既存のモジュールを更新したりする場合、現在使用されているモジュールファイルを直接変更することはありません。代わりに、すべてのモジュールが別のアップデートイメージに組み込まれます。システムが再起動された後、このアップデートイメージの使用を開始しようとします。Android システムが正常に起動した場合、モジュールは本当に更新されます。

そのため、デバイスを復旧する最もシンプルで一般的な方法は、強制的に再起動することです。モジュールをインストールした後にシステムを起動できなくなった場合、電源ボタンを10秒以上長押しするとシステムが自動的に再起動します。再起動後はモジュールを更新する前の状態にロールバックされ、以前に更新したモジュールは自動的に無効化されます。

音量下ボタンでの復旧

AB アップデートでも解決しない場合は、セーフモードを使用してみてください。セーフモードでは、すべてのモジュールが無効化されます。

セーフモードに入るには、2つの方法があります:

  1. 一部のシステムの内蔵セーフモード:音量下ボタンの長押しでセーフモードに入れるシステムもあれば、リカバリーでセーフモードに入れるシステム(MIUI など)もあります。システムのセーフモードに入ると KernelSU もセーフモードに入り、自動的にモジュールを無効化します。
  2. KernelSU の内蔵セーフモード:最初の起動画面の後、音量下キーを3回以上連続して押すと入れます。なお、押す→離すを三回繰り返すのであって、長押しではありません。

セーフモードに入ると、KernelSU Manager のモジュールページにあるすべてのモジュールが無効になります。「アンインストール」操作を行うことで、問題を起こしている可能性のあるモジュールをアンインストールできます。

内蔵のセーフモードはカーネルに実装されているため、キーイベントを見逃す可能性はありません。ただし、GKI 以外のカーネルでは手動によるコードの統合が必要な場合があるため、公式ドキュメントを参考にしてください。

悪意のあるモジュール

上記の方法でデバイスを救出できない場合、インストールしたモジュールが悪意のある操作をしているか、他の手段でデバイスを損傷している可能性が高いです。この場合、2つの方法しかありません:

  1. データを消去して純正システムをインストールし直す
  2. アフターセールスサービスに問い合わせする
',23),i=[l];function n(s,d,c,h,p,b){return e(),a("div",null,i)}const m=o(r,[["render",n]]);export{u as __pageData,m as default}; diff --git a/assets/ja_JP_guide_rescue-from-bootloop.md.d26f95c4.lean.js b/assets/ja_JP_guide_rescue-from-bootloop.md.7bb5da8e.lean.js similarity index 72% rename from assets/ja_JP_guide_rescue-from-bootloop.md.d26f95c4.lean.js rename to assets/ja_JP_guide_rescue-from-bootloop.md.7bb5da8e.lean.js index 5381a142386a..7d06d238ddb5 100644 --- a/assets/ja_JP_guide_rescue-from-bootloop.md.d26f95c4.lean.js +++ b/assets/ja_JP_guide_rescue-from-bootloop.md.7bb5da8e.lean.js @@ -1 +1 @@ -import{_ as o,o as e,c as a,O as t}from"./chunks/framework.43781440.js";const u=JSON.parse('{"title":"ブートループからの復旧","description":"","frontmatter":{},"headers":[],"relativePath":"ja_JP/guide/rescue-from-bootloop.md","filePath":"ja_JP/guide/rescue-from-bootloop.md"}'),r={name:"ja_JP/guide/rescue-from-bootloop.md"},l=t("",23),i=[l];function n(s,d,c,h,p,b){return e(),a("div",null,i)}const m=o(r,[["render",n]]);export{u as __pageData,m as default}; +import{_ as o,o as e,c as a,Q as t}from"./chunks/framework.ec8f7e8e.js";const u=JSON.parse('{"title":"ブートループからの復旧","description":"","frontmatter":{},"headers":[],"relativePath":"ja_JP/guide/rescue-from-bootloop.md","filePath":"ja_JP/guide/rescue-from-bootloop.md"}'),r={name:"ja_JP/guide/rescue-from-bootloop.md"},l=t("",23),i=[l];function n(s,d,c,h,p,b){return e(),a("div",null,i)}const m=o(r,[["render",n]]);export{u as __pageData,m as default}; diff --git a/assets/ja_JP_guide_unofficially-support-devices.md.ca3f04b0.js b/assets/ja_JP_guide_unofficially-support-devices.md.e12c36fa.js similarity index 93% rename from assets/ja_JP_guide_unofficially-support-devices.md.ca3f04b0.js rename to assets/ja_JP_guide_unofficially-support-devices.md.e12c36fa.js index 2009af2f5a78..f5f9e1a4fac7 100644 --- a/assets/ja_JP_guide_unofficially-support-devices.md.ca3f04b0.js +++ b/assets/ja_JP_guide_unofficially-support-devices.md.e12c36fa.js @@ -1 +1 @@ -import{d as r}from"./chunks/repos.9a031e32.js";import{o as a,c as l,z as e,F as s,L as i,b as c,O as o,t as n}from"./chunks/framework.43781440.js";const _=o('

非公式の対応デバイス

警告

このページでは他の開発者が管理している、KernelSU をサポートする GKI 以外のデバイス用のカーネルを紹介しています。

警告

このページはあなたのデバイスに対応するソースコードを見つけるためのものであり、そのソースコードが KernelSU 開発者 によってレビューされたことを意味するものではありません。ご自身の責任においてご利用ください。

',3),d=e("thead",null,[e("tr",null,[e("th",null,"メンテナー"),e("th",null,"リポジトリ"),e("th",null,"対応デバイス")])],-1),u=["href"],m=["href"],v=JSON.parse('{"title":"非公式の対応デバイス","description":"","frontmatter":{},"headers":[],"relativePath":"ja_JP/guide/unofficially-support-devices.md","filePath":"ja_JP/guide/unofficially-support-devices.md"}'),f={name:"ja_JP/guide/unofficially-support-devices.md"},P=Object.assign(f,{setup(h){return(p,k)=>(a(),l("div",null,[_,e("table",null,[d,e("tbody",null,[(a(!0),l(s,null,i(c(r),t=>(a(),l("tr",{key:t.devices},[e("td",null,[e("a",{href:t.maintainer_link,target:"_blank",rel:"noreferrer"},n(t.maintainer),9,u)]),e("td",null,[e("a",{href:t.kernel_link,target:"_blank",rel:"noreferrer"},n(t.kernel_name),9,m)]),e("td",null,n(t.devices),1)]))),128))])])]))}});export{v as __pageData,P as default}; +import{d as r}from"./chunks/repos.9a031e32.js";import{o as a,c as l,k as e,F as s,D as i,l as c,Q as o,t as n}from"./chunks/framework.ec8f7e8e.js";const _=o('

非公式の対応デバイス

警告

このページでは他の開発者が管理している、KernelSU をサポートする GKI 以外のデバイス用のカーネルを紹介しています。

警告

このページはあなたのデバイスに対応するソースコードを見つけるためのものであり、そのソースコードが KernelSU 開発者 によってレビューされたことを意味するものではありません。ご自身の責任においてご利用ください。

',3),d=e("thead",null,[e("tr",null,[e("th",null,"メンテナー"),e("th",null,"リポジトリ"),e("th",null,"対応デバイス")])],-1),u=["href"],m=["href"],v=JSON.parse('{"title":"非公式の対応デバイス","description":"","frontmatter":{},"headers":[],"relativePath":"ja_JP/guide/unofficially-support-devices.md","filePath":"ja_JP/guide/unofficially-support-devices.md"}'),f={name:"ja_JP/guide/unofficially-support-devices.md"},P=Object.assign(f,{setup(h){return(p,k)=>(a(),l("div",null,[_,e("table",null,[d,e("tbody",null,[(a(!0),l(s,null,i(c(r),t=>(a(),l("tr",{key:t.devices},[e("td",null,[e("a",{href:t.maintainer_link,target:"_blank",rel:"noreferrer"},n(t.maintainer),9,u)]),e("td",null,[e("a",{href:t.kernel_link,target:"_blank",rel:"noreferrer"},n(t.kernel_name),9,m)]),e("td",null,n(t.devices),1)]))),128))])])]))}});export{v as __pageData,P as default}; diff --git a/assets/ja_JP_guide_unofficially-support-devices.md.ca3f04b0.lean.js b/assets/ja_JP_guide_unofficially-support-devices.md.e12c36fa.lean.js similarity index 87% rename from assets/ja_JP_guide_unofficially-support-devices.md.ca3f04b0.lean.js rename to assets/ja_JP_guide_unofficially-support-devices.md.e12c36fa.lean.js index d4fa42c95dd6..84c7f737ed53 100644 --- a/assets/ja_JP_guide_unofficially-support-devices.md.ca3f04b0.lean.js +++ b/assets/ja_JP_guide_unofficially-support-devices.md.e12c36fa.lean.js @@ -1 +1 @@ -import{d as r}from"./chunks/repos.9a031e32.js";import{o as a,c as l,z as e,F as s,L as i,b as c,O as o,t as n}from"./chunks/framework.43781440.js";const _=o("",3),d=e("thead",null,[e("tr",null,[e("th",null,"メンテナー"),e("th",null,"リポジトリ"),e("th",null,"対応デバイス")])],-1),u=["href"],m=["href"],v=JSON.parse('{"title":"非公式の対応デバイス","description":"","frontmatter":{},"headers":[],"relativePath":"ja_JP/guide/unofficially-support-devices.md","filePath":"ja_JP/guide/unofficially-support-devices.md"}'),f={name:"ja_JP/guide/unofficially-support-devices.md"},P=Object.assign(f,{setup(h){return(p,k)=>(a(),l("div",null,[_,e("table",null,[d,e("tbody",null,[(a(!0),l(s,null,i(c(r),t=>(a(),l("tr",{key:t.devices},[e("td",null,[e("a",{href:t.maintainer_link,target:"_blank",rel:"noreferrer"},n(t.maintainer),9,u)]),e("td",null,[e("a",{href:t.kernel_link,target:"_blank",rel:"noreferrer"},n(t.kernel_name),9,m)]),e("td",null,n(t.devices),1)]))),128))])])]))}});export{v as __pageData,P as default}; +import{d as r}from"./chunks/repos.9a031e32.js";import{o as a,c as l,k as e,F as s,D as i,l as c,Q as o,t as n}from"./chunks/framework.ec8f7e8e.js";const _=o("",3),d=e("thead",null,[e("tr",null,[e("th",null,"メンテナー"),e("th",null,"リポジトリ"),e("th",null,"対応デバイス")])],-1),u=["href"],m=["href"],v=JSON.parse('{"title":"非公式の対応デバイス","description":"","frontmatter":{},"headers":[],"relativePath":"ja_JP/guide/unofficially-support-devices.md","filePath":"ja_JP/guide/unofficially-support-devices.md"}'),f={name:"ja_JP/guide/unofficially-support-devices.md"},P=Object.assign(f,{setup(h){return(p,k)=>(a(),l("div",null,[_,e("table",null,[d,e("tbody",null,[(a(!0),l(s,null,i(c(r),t=>(a(),l("tr",{key:t.devices},[e("td",null,[e("a",{href:t.maintainer_link,target:"_blank",rel:"noreferrer"},n(t.maintainer),9,u)]),e("td",null,[e("a",{href:t.kernel_link,target:"_blank",rel:"noreferrer"},n(t.kernel_name),9,m)]),e("td",null,n(t.devices),1)]))),128))])])]))}});export{v as __pageData,P as default}; diff --git a/assets/ja_JP_guide_what-is-kernelsu.md.779775a2.js b/assets/ja_JP_guide_what-is-kernelsu.md.10b56327.js similarity index 95% rename from assets/ja_JP_guide_what-is-kernelsu.md.779775a2.js rename to assets/ja_JP_guide_what-is-kernelsu.md.10b56327.js index 82c5d582b913..29a57dd7ccb6 100644 --- a/assets/ja_JP_guide_what-is-kernelsu.md.779775a2.js +++ b/assets/ja_JP_guide_what-is-kernelsu.md.10b56327.js @@ -1 +1 @@ -import{_ as e,o as a,c as r,O as t}from"./chunks/framework.43781440.js";const m=JSON.parse('{"title":"KernelSU とは?","description":"","frontmatter":{},"headers":[],"relativePath":"ja_JP/guide/what-is-kernelsu.md","filePath":"ja_JP/guide/what-is-kernelsu.md"}'),l={name:"ja_JP/guide/what-is-kernelsu.md"},n=t('

KernelSU とは?

KernelSU は Android GKI デバイスのための root ソリューションです。カーネルモードで動作し、カーネル空間で直接ユーザー空間アプリに root 権限を付与します。

機能

KernelSU の最大の特徴は、カーネルベースであることです。KernelSU はカーネルモードで動作するため、今までにないカーネルインターフェイスを提供できます。例えば、カーネルモードで任意のプロセスにハードウェアブレークポイントを追加できる、誰にも気づかれずに任意のプロセスの物理メモリにアクセスできる、カーネル空間で任意のシステムコールを傍受できる、などです。

また、KernelSU は OverlayFS によるモジュールシステムを提供しており、カスタムプラグインをシステムに読み込めます。/system パーティションを変更する仕組みも提供しています。

使用方法

こちらをご覧ください: インストール方法

ビルド方法

ビルドするには

ディスカッション

',11),o=[n];function s(i,h,d,c,_,u){return a(),r("div",null,o)}const f=e(l,[["render",s]]);export{m as __pageData,f as default}; +import{_ as e,o as a,c as r,Q as t}from"./chunks/framework.ec8f7e8e.js";const m=JSON.parse('{"title":"KernelSU とは?","description":"","frontmatter":{},"headers":[],"relativePath":"ja_JP/guide/what-is-kernelsu.md","filePath":"ja_JP/guide/what-is-kernelsu.md"}'),l={name:"ja_JP/guide/what-is-kernelsu.md"},n=t('

KernelSU とは?

KernelSU は Android GKI デバイスのための root ソリューションです。カーネルモードで動作し、カーネル空間で直接ユーザー空間アプリに root 権限を付与します。

機能

KernelSU の最大の特徴は、カーネルベースであることです。KernelSU はカーネルモードで動作するため、今までにないカーネルインターフェイスを提供できます。例えば、カーネルモードで任意のプロセスにハードウェアブレークポイントを追加できる、誰にも気づかれずに任意のプロセスの物理メモリにアクセスできる、カーネル空間で任意のシステムコールを傍受できる、などです。

また、KernelSU は OverlayFS によるモジュールシステムを提供しており、カスタムプラグインをシステムに読み込めます。/system パーティションを変更する仕組みも提供しています。

使用方法

こちらをご覧ください: インストール方法

ビルド方法

ビルドするには

ディスカッション

',11),o=[n];function s(i,h,d,c,_,u){return a(),r("div",null,o)}const f=e(l,[["render",s]]);export{m as __pageData,f as default}; diff --git a/assets/ja_JP_guide_what-is-kernelsu.md.779775a2.lean.js b/assets/ja_JP_guide_what-is-kernelsu.md.10b56327.lean.js similarity index 70% rename from assets/ja_JP_guide_what-is-kernelsu.md.779775a2.lean.js rename to assets/ja_JP_guide_what-is-kernelsu.md.10b56327.lean.js index 40b7207c3824..ab56de3d88a4 100644 --- a/assets/ja_JP_guide_what-is-kernelsu.md.779775a2.lean.js +++ b/assets/ja_JP_guide_what-is-kernelsu.md.10b56327.lean.js @@ -1 +1 @@ -import{_ as e,o as a,c as r,O as t}from"./chunks/framework.43781440.js";const m=JSON.parse('{"title":"KernelSU とは?","description":"","frontmatter":{},"headers":[],"relativePath":"ja_JP/guide/what-is-kernelsu.md","filePath":"ja_JP/guide/what-is-kernelsu.md"}'),l={name:"ja_JP/guide/what-is-kernelsu.md"},n=t("",11),o=[n];function s(i,h,d,c,_,u){return a(),r("div",null,o)}const f=e(l,[["render",s]]);export{m as __pageData,f as default}; +import{_ as e,o as a,c as r,Q as t}from"./chunks/framework.ec8f7e8e.js";const m=JSON.parse('{"title":"KernelSU とは?","description":"","frontmatter":{},"headers":[],"relativePath":"ja_JP/guide/what-is-kernelsu.md","filePath":"ja_JP/guide/what-is-kernelsu.md"}'),l={name:"ja_JP/guide/what-is-kernelsu.md"},n=t("",11),o=[n];function s(i,h,d,c,_,u){return a(),r("div",null,o)}const f=e(l,[["render",s]]);export{m as __pageData,f as default}; diff --git a/assets/ja_JP_index.md.be3c7573.js b/assets/ja_JP_index.md.474a6e1c.js similarity index 95% rename from assets/ja_JP_index.md.be3c7573.js rename to assets/ja_JP_index.md.474a6e1c.js index 67b3c0c72409..5d29d6ba5f5e 100644 --- a/assets/ja_JP_index.md.be3c7573.js +++ b/assets/ja_JP_index.md.474a6e1c.js @@ -1 +1 @@ -import{_ as e,o as t,c as a}from"./chunks/framework.43781440.js";const _=JSON.parse('{"title":"Android 向けのカーネルベース root ソリューション","description":"","frontmatter":{"layout":"home","title":"Android 向けのカーネルベース root ソリューション","hero":{"name":"KernelSU","text":"Android 向けのカーネルベース root ソリューション","tagline":"","image":{"src":"/logo.png","alt":"KernelSU"},"actions":[{"theme":"brand","text":"はじめる","link":"/ja_JP/guide/what-is-kernelsu"},{"theme":"alt","text":"GitHub で表示","link":"https://github.com/tiann/KernelSU"}]},"features":[{"title":"カーネルベース","details":"KernelSU は Linux カーネルモードで動作し、ユーザー空間よりも高度な制御が可能です。"},{"title":"ホワイトリストの権限管理","details":"root 権限を許可したアプリのみが su にアクセスでき、他のアプリは su を見つけられません。"},{"title":"モジュール対応","details":"KernelSU は OverlayFS により実際のシステムを改変せずに /system を変更できます。書き込み可能にすることさえできます。"},{"title":"オープンソース","details":"KernelSU は GPL-3 でライセンスされたオープンソースプロジェクトです。"}]},"headers":[],"relativePath":"ja_JP/index.md","filePath":"ja_JP/index.md"}'),n={name:"ja_JP/index.md"};function i(r,o,l,s,d,c){return t(),a("div")}const p=e(n,[["render",i]]);export{_ as __pageData,p as default}; +import{_ as e,o as t,c as a}from"./chunks/framework.ec8f7e8e.js";const _=JSON.parse('{"title":"Android 向けのカーネルベース root ソリューション","description":"","frontmatter":{"layout":"home","title":"Android 向けのカーネルベース root ソリューション","hero":{"name":"KernelSU","text":"Android 向けのカーネルベース root ソリューション","tagline":"","image":{"src":"/logo.png","alt":"KernelSU"},"actions":[{"theme":"brand","text":"はじめる","link":"/ja_JP/guide/what-is-kernelsu"},{"theme":"alt","text":"GitHub で表示","link":"https://github.com/tiann/KernelSU"}]},"features":[{"title":"カーネルベース","details":"KernelSU は Linux カーネルモードで動作し、ユーザー空間よりも高度な制御が可能です。"},{"title":"ホワイトリストの権限管理","details":"root 権限を許可したアプリのみが su にアクセスでき、他のアプリは su を見つけられません。"},{"title":"モジュール対応","details":"KernelSU は OverlayFS により実際のシステムを改変せずに /system を変更できます。書き込み可能にすることさえできます。"},{"title":"オープンソース","details":"KernelSU は GPL-3 でライセンスされたオープンソースプロジェクトです。"}]},"headers":[],"relativePath":"ja_JP/index.md","filePath":"ja_JP/index.md"}'),n={name:"ja_JP/index.md"};function i(r,o,l,s,d,c){return t(),a("div")}const p=e(n,[["render",i]]);export{_ as __pageData,p as default}; diff --git a/assets/ja_JP_index.md.be3c7573.lean.js b/assets/ja_JP_index.md.474a6e1c.lean.js similarity index 95% rename from assets/ja_JP_index.md.be3c7573.lean.js rename to assets/ja_JP_index.md.474a6e1c.lean.js index 67b3c0c72409..5d29d6ba5f5e 100644 --- a/assets/ja_JP_index.md.be3c7573.lean.js +++ b/assets/ja_JP_index.md.474a6e1c.lean.js @@ -1 +1 @@ -import{_ as e,o as t,c as a}from"./chunks/framework.43781440.js";const _=JSON.parse('{"title":"Android 向けのカーネルベース root ソリューション","description":"","frontmatter":{"layout":"home","title":"Android 向けのカーネルベース root ソリューション","hero":{"name":"KernelSU","text":"Android 向けのカーネルベース root ソリューション","tagline":"","image":{"src":"/logo.png","alt":"KernelSU"},"actions":[{"theme":"brand","text":"はじめる","link":"/ja_JP/guide/what-is-kernelsu"},{"theme":"alt","text":"GitHub で表示","link":"https://github.com/tiann/KernelSU"}]},"features":[{"title":"カーネルベース","details":"KernelSU は Linux カーネルモードで動作し、ユーザー空間よりも高度な制御が可能です。"},{"title":"ホワイトリストの権限管理","details":"root 権限を許可したアプリのみが su にアクセスでき、他のアプリは su を見つけられません。"},{"title":"モジュール対応","details":"KernelSU は OverlayFS により実際のシステムを改変せずに /system を変更できます。書き込み可能にすることさえできます。"},{"title":"オープンソース","details":"KernelSU は GPL-3 でライセンスされたオープンソースプロジェクトです。"}]},"headers":[],"relativePath":"ja_JP/index.md","filePath":"ja_JP/index.md"}'),n={name:"ja_JP/index.md"};function i(r,o,l,s,d,c){return t(),a("div")}const p=e(n,[["render",i]]);export{_ as __pageData,p as default}; +import{_ as e,o as t,c as a}from"./chunks/framework.ec8f7e8e.js";const _=JSON.parse('{"title":"Android 向けのカーネルベース root ソリューション","description":"","frontmatter":{"layout":"home","title":"Android 向けのカーネルベース root ソリューション","hero":{"name":"KernelSU","text":"Android 向けのカーネルベース root ソリューション","tagline":"","image":{"src":"/logo.png","alt":"KernelSU"},"actions":[{"theme":"brand","text":"はじめる","link":"/ja_JP/guide/what-is-kernelsu"},{"theme":"alt","text":"GitHub で表示","link":"https://github.com/tiann/KernelSU"}]},"features":[{"title":"カーネルベース","details":"KernelSU は Linux カーネルモードで動作し、ユーザー空間よりも高度な制御が可能です。"},{"title":"ホワイトリストの権限管理","details":"root 権限を許可したアプリのみが su にアクセスでき、他のアプリは su を見つけられません。"},{"title":"モジュール対応","details":"KernelSU は OverlayFS により実際のシステムを改変せずに /system を変更できます。書き込み可能にすることさえできます。"},{"title":"オープンソース","details":"KernelSU は GPL-3 でライセンスされたオープンソースプロジェクトです。"}]},"headers":[],"relativePath":"ja_JP/index.md","filePath":"ja_JP/index.md"}'),n={name:"ja_JP/index.md"};function i(r,o,l,s,d,c){return t(),a("div")}const p=e(n,[["render",i]]);export{_ as __pageData,p as default}; diff --git a/assets/pt_BR_guide_app-profile.md.7a2ced3a.js b/assets/pt_BR_guide_app-profile.md.7a2ced3a.js deleted file mode 100644 index 569753209e9c..000000000000 --- a/assets/pt_BR_guide_app-profile.md.7a2ced3a.js +++ /dev/null @@ -1,5 +0,0 @@ -import{_ as o,o as e,c as s,O as a}from"./chunks/framework.43781440.js";const g=JSON.parse('{"title":"Perfil do Aplicativo","description":"","frontmatter":{},"headers":[],"relativePath":"pt_BR/guide/app-profile.md","filePath":"pt_BR/guide/app-profile.md"}'),r={name:"pt_BR/guide/app-profile.md"},p=a(`

Perfil do Aplicativo

O Perfil do Aplicativo é um mecanismo fornecido pelo KernelSU para personalizar a configuração de vários apps.

Para apps com permissões de root (ou seja, capazes de usar su), o Perfil do Aplicativo também pode ser chamado de Perfil Root. Ele permite a customização das regras uid, gid, groups, capabilities e SELinux do comando su, restringindo assim os privilégios do usuário root. Por exemplo, ele pode conceder permissões de rede apenas para apps de firewall enquanto nega permissões de acesso a arquivos, ou pode conceder permissões de shell em vez de acesso root completo para apps congelados: mantendo o poder confinado com o princípio do menor privilégio.

Para apps comuns sem permissões de root, o Perfil do Aplicativo pode controlar o comportamento do kernel e do sistema de módulos em relação a esses apps. Por exemplo, pode determinar se as modificações resultantes dos módulos devem ser abordadas. O kernel e o sistema de módulos podem tomar decisões com base nesta configuração, como realizar operações semelhantes a "ocultar"

Perfil Root

UID, GID e Grupos

Os sistemas Linux possuem dois conceitos: usuários e grupos. Cada usuário possui um ID de usuário (UID) e um usuário pode pertencer a vários grupos, cada um com seu próprio ID de grupo (GID). Esses IDs são usados ​​para identificar usuários no sistema e determinar quais recursos do sistema eles podem acessar.

Os usuários com UID 0 são conhecidos como usuários root e os grupos com GID 0 são conhecidos como grupos root. O grupo de usuários root normalmente possui os privilégios de sistema mais altos.

No caso do sistema Android, cada app é um usuário separado (excluindo cenários de UID compartilhados) com um UID exclusivo. Por exemplo, 0 representa o usuário root, 1000 representa system, 2000 representa o ADB shell e UIDs variando de 10.000 a 19.999 representam apps comuns.

INFORMAÇÕES

Aqui, o UID mencionado não é o mesmo que o conceito de múltiplos usuários ou perfis de trabalho no sistema Android. Os perfis de trabalho são, na verdade, implementados particionando o intervalo UID. Por exemplo, 10000-19999 representa o usuário principal, enquanto 110000-119999 representa um perfil de trabalho. Cada app comum entre eles possui seu próprio UID exclusivo.

Cada app pode ter vários grupos, com o GID representando o grupo principal, que geralmente corresponde ao UID. Outros grupos são conhecidos como grupos suplementares. Certas permissões são controladas por meio de grupos, como permissões de acesso à rede ou acesso Bluetooth.

Por exemplo, se executarmos o comando id no ADB shell, a saída pode ser semelhante a esta:

sh
oriole:/ $ id
-uid=2000(shell) gid=2000(shell) groups=2000(shell),1004(input),1007(log),1011(adb),1015(sdcard_rw),1028(sdcard_r),1078(ext_data_rw),1079(ext_obb_rw),3001(net_bt_admin),3002(net_bt),3003(inet),3006(net_bw_stats),3009(readproc),3011(uhid),3012(readtracefs) context=u:r:shell:s0

Aqui, o UID é 2000 e o GID (ID do grupo primário) também é 2000. Além disso, pertence a vários grupos suplementares, como inet (indicando a capacidade de criar soquetes AF_INET e AF_INET6) e sdcard_rw (indicando permissões de leitura/gravação para o cartão SD).

O Perfil Root do KernelSU permite a personalização do UID, GID e grupos para o processo root após a execução de su. Por exemplo, o Perfil Root de um app root pode definir seu UID como 2000, que significa que ao usar su, as permissões reais do app estão no nível do ADB shell. O grupo inet pode ser removido, evitando que o comando su acesse a rede.

OBSERVAÇÃO

O Perfil do Aplicativo controla apenas as permissões do processo root após usar su, e ele não controla as permissões do próprio app. Se um app solicitou permissão de acesso à rede, ele ainda poderá acessar a rede mesmo sem usar su. Remover o grupo inet de su apenas impede que su acesse a rede.

O Perfil Root é aplicado no kernel e não depende do comportamento voluntário de apps root, ao contrário da troca de usuários ou grupos por meio de su, a concessão da permissão su depende inteiramente do usuário e não do desenvolvedor.

Capacidades

As capacidades são um mecanismo para separação de privilégios no Linux.

Para realizar verificações de permissão, as implementações tradicionais do UNIX distinguem duas categorias de processos: processos privilegiados (cujo ID de usuário efetivo é 0, referido como superusuário ou root) e processos sem privilégios (cujo UID efetivo é diferente de zero). Os processos privilegiados ignoram todas as verificações de permissão do kernel, enquanto os processos não privilegiados estão sujeitos à verificação completa de permissão com base nas credenciais do processo (geralmente: UID efetivo, GID efetivo e lista de grupos suplementares).

A partir do Linux 2.2, o Linux divide os privilégios tradicionalmente associados ao superusuário em unidades distintas, conhecidas como capacidades, que podem ser ativadas e desativadas de forma independente.

Cada capacidade representa um ou mais privilégios. Por exemplo, CAP_DAC_READ_SEARCH representa a capacidade de ignorar verificações de permissão para leitura de arquivos, bem como permissões de leitura e execução de diretório. Se um usuário com um UID efetivo 0 (usuário root) não tiver recursos CAP_DAC_READ_SEARCH ou superiores, isso significa que mesmo sendo root, ele não pode ler arquivos à vontade.

O Perfil Root do KernelSU permite a personalização das capacidades do processo root após a execução de su, conseguindo assim conceder parcialmente "permissões de root". Ao contrário do UID e GID mencionados acima, certos apps root exigem um UID de 0 após usar su. Nesses casos, limitar as capacidades deste usuário root com UID 0 pode restringir suas operações permitidas.

FORTE RECOMENDAÇÃO

A documentação oficial da Capacidade do Linux fornece explicações detalhadas das habilidades representadas por cada Capacidade. Se você pretende customizar Capacidades, é altamente recomendável que você leia este documento primeiro.

SELinux

SELinux é um poderoso mecanismo do Controle de Acesso Obrigatório (MAC). Ele opera com base no princípio de negação padrão. Qualquer ação não explicitamente permitida é negada.

O SELinux pode ser executado em dois modos globais:

  1. Modo permissivo: Os eventos de negação são registrados, mas não aplicados.
  2. Modo de aplicação: Os eventos de negação são registrados e aplicados.

AVISO

Os sistemas Android modernos dependem fortemente do SELinux para garantir a segurança geral do sistema. É altamente recomendável não usar nenhum sistema personalizado executado em "Modo permissivo", pois não oferece vantagens significativas em relação a um sistema completamente aberto.

Explicar o conceito completo do SELinux é complexo e está além do objetivo deste documento. Recomenda-se primeiro entender seu funcionamento através dos seguintes recursos:

  1. Wikipédia
  2. Red Hat: O que é SELinux?
  3. ArchLinux: SELinux

O Perfil Root do KernelSU permite a personalização do contexto SELinux do processo root após a execução de su. Regras específicas de controle de acesso podem ser definidas para este contexto para permitir um controle refinado sobre as permissões de root.

Em cenários típicos, quando um app executa su, ele alterna o processo para um domínio SELinux com acesso irrestrito, como u:r:su:s0. Através do Perfil Root, este domínio pode ser mudado para um domínio personalizado, como u:r:app1:s0, e uma série de regras podem ser definidas para este domínio:

sh
type app1
-enforce app1
-typeattribute app1 mlstrustedsubject
-allow app1 * * *

Observe que a regra allow app1 * * * é usada apenas para fins de demonstração. Na prática, esta regra não deve ser utilizada extensivamente, pois não difere muito do Modo permissivo.

Escalação

Se a configuração do Perfil Root não estiver definida corretamente, poderá ocorrer um cenário de escalação. As restrições impostas pelo Perfil Root poderão falhar involuntariamente.

Por exemplo, se você conceder permissão root a um usuário ADB shell (que é um caso comum) e, em seguida, conceder permissão root a um app normal, mas configurar seu Perfil Root com UID 2000 (que é o UID do usuário ADB shell), o app pode obter acesso root completo executando o comando su duas vezes:

  1. A primeira execução su está sujeita à aplicação do Perfil do Aplicativo e mudará para UID 2000 (adb shell) em vez de 0 (root).
  2. A segunda execução su, como o UID é 2000 e você concedeu acesso root ao UID 2000 (adb shell) na configuração, o app obterá privilégio de root completo.

OBSERVAÇÃO

Este comportamento é totalmente esperado e não é um bug. Portanto, recomendamos o seguinte:

Se você realmente precisa conceder permissões de root ao ADB (por exemplo, como desenvolvedor), não é aconselhável alterar o UID para 2000 ao configurar o Perfil Root. Usar 1000 (system) seria uma melhor escolha.

Perfil não Root

Desmontar módulos

O KernelSU fornece um mecanismo sem sistema para modificar partições do sistema, obtido através da montagem de overlayfs. No entanto, alguns apps podem ser sensíveis a esse comportamento. Assim, podemos descarregar módulos montados nesses apps configurando a opção “desmontar módulos”.

Além disso, a interface de configurações do gerenciador KernelSU fornece uma opção para "desmontar módulos por padrão". Por padrão, essa opção está ativada, o que significa que o KernelSU ou alguns módulos descarregarão módulos para este app, a menos que configurações adicionais sejam aplicadas. Se você não preferir esta configuração ou se ela afetar determinados apps, você terá as seguintes opções:

  1. Mantenha a opção "desmontar módulos por padrão" e desative individualmente a opção "desmontar módulos" no Perfil do Aplicativo para apps que exigem carregamento do módulo (agindo como uma "lista de permissões").
  2. Desative a opção "desmontar módulos por padrão" e ative individualmente a opção "desmontar módulos" no Perfil do Aplicativo para apps que exigem descarregamento do módulo (agindo como uma "lista negra").

INFORMAÇÕES

Em dispositivos que utilizam kernel versão 5.10 e superior, o kernel realiza o descarregamento dos módulos. No entanto, para dispositivos que executam versões de kernel abaixo de 5.10, essa opção é apenas uma opção de configuração e o próprio KernelSU não executa nenhuma ação. Alguns módulos, como Zygisksu, podem usar essa opção para determinar se o descarregamento do módulo é necessário.

`,46),n=[p];function i(d,t,c,l,u,m){return e(),s("div",null,n)}const F=o(r,[["render",i]]);export{g as __pageData,F as default}; diff --git a/assets/pt_BR_guide_app-profile.md.7a2ced3a.lean.js b/assets/pt_BR_guide_app-profile.md.7a2ced3a.lean.js deleted file mode 100644 index d21fe62ab9ea..000000000000 --- a/assets/pt_BR_guide_app-profile.md.7a2ced3a.lean.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as o,o as e,c as s,O as a}from"./chunks/framework.43781440.js";const g=JSON.parse('{"title":"Perfil do Aplicativo","description":"","frontmatter":{},"headers":[],"relativePath":"pt_BR/guide/app-profile.md","filePath":"pt_BR/guide/app-profile.md"}'),r={name:"pt_BR/guide/app-profile.md"},p=a("",46),n=[p];function i(d,t,c,l,u,m){return e(),s("div",null,n)}const F=o(r,[["render",i]]);export{g as __pageData,F as default}; diff --git a/assets/pt_BR_guide_app-profile.md.dd333014.js b/assets/pt_BR_guide_app-profile.md.dd333014.js new file mode 100644 index 000000000000..2aa0b738dc45 --- /dev/null +++ b/assets/pt_BR_guide_app-profile.md.dd333014.js @@ -0,0 +1,9 @@ +import{_ as o,o as e,c as s,Q as a}from"./chunks/framework.ec8f7e8e.js";const y=JSON.parse('{"title":"Perfil do Aplicativo","description":"","frontmatter":{},"headers":[],"relativePath":"pt_BR/guide/app-profile.md","filePath":"pt_BR/guide/app-profile.md"}'),p={name:"pt_BR/guide/app-profile.md"},r=a(`

Perfil do Aplicativo

O Perfil do Aplicativo é um mecanismo fornecido pelo KernelSU para personalizar a configuração de vários apps.

Para apps com permissões de root (ou seja, capazes de usar su), o Perfil do Aplicativo também pode ser chamado de Perfil Root. Ele permite a customização das regras uid, gid, groups, capabilities e SELinux do comando su, restringindo assim os privilégios do usuário root. Por exemplo, ele pode conceder permissões de rede apenas para apps de firewall enquanto nega permissões de acesso a arquivos, ou pode conceder permissões de shell em vez de acesso root completo para apps congelados: mantendo o poder confinado com o princípio do menor privilégio.

Para apps comuns sem permissões de root, o Perfil do Aplicativo pode controlar o comportamento do kernel e do sistema de módulos em relação a esses apps. Por exemplo, pode determinar se as modificações resultantes dos módulos devem ser abordadas. O kernel e o sistema de módulos podem tomar decisões com base nesta configuração, como realizar operações semelhantes a "ocultar"

Perfil Root

UID, GID e Grupos

Os sistemas Linux possuem dois conceitos: usuários e grupos. Cada usuário possui um ID de usuário (UID) e um usuário pode pertencer a vários grupos, cada um com seu próprio ID de grupo (GID). Esses IDs são usados ​​para identificar usuários no sistema e determinar quais recursos do sistema eles podem acessar.

Os usuários com UID 0 são conhecidos como usuários root e os grupos com GID 0 são conhecidos como grupos root. O grupo de usuários root normalmente possui os privilégios de sistema mais altos.

No caso do sistema Android, cada app é um usuário separado (excluindo cenários de UID compartilhados) com um UID exclusivo. Por exemplo, 0 representa o usuário root, 1000 representa system, 2000 representa o ADB shell e UIDs variando de 10.000 a 19.999 representam apps comuns.

INFORMAÇÕES

Aqui, o UID mencionado não é o mesmo que o conceito de múltiplos usuários ou perfis de trabalho no sistema Android. Os perfis de trabalho são, na verdade, implementados particionando o intervalo UID. Por exemplo, 10000-19999 representa o usuário principal, enquanto 110000-119999 representa um perfil de trabalho. Cada app comum entre eles possui seu próprio UID exclusivo.

Cada app pode ter vários grupos, com o GID representando o grupo principal, que geralmente corresponde ao UID. Outros grupos são conhecidos como grupos suplementares. Certas permissões são controladas por meio de grupos, como permissões de acesso à rede ou acesso Bluetooth.

Por exemplo, se executarmos o comando id no ADB shell, a saída pode ser semelhante a esta:

sh
oriole:/ $ id
+uid=2000(shell) gid=2000(shell) groups=2000(shell),1004(input),1007(log),1011(adb),1015(sdcard_rw),1028(sdcard_r),1078(ext_data_rw),1079(ext_obb_rw),3001(net_bt_admin),3002(net_bt),3003(inet),3006(net_bw_stats),3009(readproc),3011(uhid),3012(readtracefs) context=u:r:shell:s0
oriole:/ $ id
+uid=2000(shell) gid=2000(shell) groups=2000(shell),1004(input),1007(log),1011(adb),1015(sdcard_rw),1028(sdcard_r),1078(ext_data_rw),1079(ext_obb_rw),3001(net_bt_admin),3002(net_bt),3003(inet),3006(net_bw_stats),3009(readproc),3011(uhid),3012(readtracefs) context=u:r:shell:s0

Aqui, o UID é 2000 e o GID (ID do grupo primário) também é 2000. Além disso, pertence a vários grupos suplementares, como inet (indicando a capacidade de criar soquetes AF_INET e AF_INET6) e sdcard_rw (indicando permissões de leitura/gravação para o cartão SD).

O Perfil Root do KernelSU permite a personalização do UID, GID e grupos para o processo root após a execução de su. Por exemplo, o Perfil Root de um app root pode definir seu UID como 2000, que significa que ao usar su, as permissões reais do app estão no nível do ADB shell. O grupo inet pode ser removido, evitando que o comando su acesse a rede.

OBSERVAÇÃO

O Perfil do Aplicativo controla apenas as permissões do processo root após usar su, e ele não controla as permissões do próprio app. Se um app solicitou permissão de acesso à rede, ele ainda poderá acessar a rede mesmo sem usar su. Remover o grupo inet de su apenas impede que su acesse a rede.

O Perfil Root é aplicado no kernel e não depende do comportamento voluntário de apps root, ao contrário da troca de usuários ou grupos por meio de su, a concessão da permissão su depende inteiramente do usuário e não do desenvolvedor.

Capacidades

As capacidades são um mecanismo para separação de privilégios no Linux.

Para realizar verificações de permissão, as implementações tradicionais do UNIX distinguem duas categorias de processos: processos privilegiados (cujo ID de usuário efetivo é 0, referido como superusuário ou root) e processos sem privilégios (cujo UID efetivo é diferente de zero). Os processos privilegiados ignoram todas as verificações de permissão do kernel, enquanto os processos não privilegiados estão sujeitos à verificação completa de permissão com base nas credenciais do processo (geralmente: UID efetivo, GID efetivo e lista de grupos suplementares).

A partir do Linux 2.2, o Linux divide os privilégios tradicionalmente associados ao superusuário em unidades distintas, conhecidas como capacidades, que podem ser ativadas e desativadas de forma independente.

Cada capacidade representa um ou mais privilégios. Por exemplo, CAP_DAC_READ_SEARCH representa a capacidade de ignorar verificações de permissão para leitura de arquivos, bem como permissões de leitura e execução de diretório. Se um usuário com um UID efetivo 0 (usuário root) não tiver recursos CAP_DAC_READ_SEARCH ou superiores, isso significa que mesmo sendo root, ele não pode ler arquivos à vontade.

O Perfil Root do KernelSU permite a personalização das capacidades do processo root após a execução de su, conseguindo assim conceder parcialmente "permissões de root". Ao contrário do UID e GID mencionados acima, certos apps root exigem um UID de 0 após usar su. Nesses casos, limitar as capacidades deste usuário root com UID 0 pode restringir suas operações permitidas.

FORTE RECOMENDAÇÃO

A documentação oficial da Capacidade do Linux fornece explicações detalhadas das habilidades representadas por cada Capacidade. Se você pretende customizar Capacidades, é altamente recomendável que você leia este documento primeiro.

SELinux

SELinux é um poderoso mecanismo do Controle de Acesso Obrigatório (MAC). Ele opera com base no princípio de negação padrão. Qualquer ação não explicitamente permitida é negada.

O SELinux pode ser executado em dois modos globais:

  1. Modo permissivo: Os eventos de negação são registrados, mas não aplicados.
  2. Modo de aplicação: Os eventos de negação são registrados e aplicados.

AVISO

Os sistemas Android modernos dependem fortemente do SELinux para garantir a segurança geral do sistema. É altamente recomendável não usar nenhum sistema personalizado executado em "Modo permissivo", pois não oferece vantagens significativas em relação a um sistema completamente aberto.

Explicar o conceito completo do SELinux é complexo e está além do objetivo deste documento. Recomenda-se primeiro entender seu funcionamento através dos seguintes recursos:

  1. Wikipédia
  2. Red Hat: O que é SELinux?
  3. ArchLinux: SELinux

O Perfil Root do KernelSU permite a personalização do contexto SELinux do processo root após a execução de su. Regras específicas de controle de acesso podem ser definidas para este contexto para permitir um controle refinado sobre as permissões de root.

Em cenários típicos, quando um app executa su, ele alterna o processo para um domínio SELinux com acesso irrestrito, como u:r:su:s0. Através do Perfil Root, este domínio pode ser mudado para um domínio personalizado, como u:r:app1:s0, e uma série de regras podem ser definidas para este domínio:

sh
type app1
+enforce app1
+typeattribute app1 mlstrustedsubject
+allow app1 * * *
type app1
+enforce app1
+typeattribute app1 mlstrustedsubject
+allow app1 * * *

Observe que a regra allow app1 * * * é usada apenas para fins de demonstração. Na prática, esta regra não deve ser utilizada extensivamente, pois não difere muito do Modo permissivo.

Escalação

Se a configuração do Perfil Root não estiver definida corretamente, poderá ocorrer um cenário de escalação. As restrições impostas pelo Perfil Root poderão falhar involuntariamente.

Por exemplo, se você conceder permissão root a um usuário ADB shell (que é um caso comum) e, em seguida, conceder permissão root a um app normal, mas configurar seu Perfil Root com UID 2000 (que é o UID do usuário ADB shell), o app pode obter acesso root completo executando o comando su duas vezes:

  1. A primeira execução su está sujeita à aplicação do Perfil do Aplicativo e mudará para UID 2000 (adb shell) em vez de 0 (root).
  2. A segunda execução su, como o UID é 2000 e você concedeu acesso root ao UID 2000 (adb shell) na configuração, o app obterá privilégio de root completo.

OBSERVAÇÃO

Este comportamento é totalmente esperado e não é um bug. Portanto, recomendamos o seguinte:

Se você realmente precisa conceder permissões de root ao ADB (por exemplo, como desenvolvedor), não é aconselhável alterar o UID para 2000 ao configurar o Perfil Root. Usar 1000 (system) seria uma melhor escolha.

Perfil não Root

Desmontar módulos

O KernelSU fornece um mecanismo sem sistema para modificar partições do sistema, obtido através da montagem de overlayfs. No entanto, alguns apps podem ser sensíveis a esse comportamento. Assim, podemos descarregar módulos montados nesses apps configurando a opção “desmontar módulos”.

Além disso, a interface de configurações do gerenciador KernelSU fornece uma opção para "desmontar módulos por padrão". Por padrão, essa opção está ativada, o que significa que o KernelSU ou alguns módulos descarregarão módulos para este app, a menos que configurações adicionais sejam aplicadas. Se você não preferir esta configuração ou se ela afetar determinados apps, você terá as seguintes opções:

  1. Mantenha a opção "desmontar módulos por padrão" e desative individualmente a opção "desmontar módulos" no Perfil do Aplicativo para apps que exigem carregamento do módulo (agindo como uma "lista de permissões").
  2. Desative a opção "desmontar módulos por padrão" e ative individualmente a opção "desmontar módulos" no Perfil do Aplicativo para apps que exigem descarregamento do módulo (agindo como uma "lista negra").

INFORMAÇÕES

Em dispositivos que utilizam kernel versão 5.10 e superior, o kernel realiza o descarregamento dos módulos. No entanto, para dispositivos que executam versões de kernel abaixo de 5.10, essa opção é apenas uma opção de configuração e o próprio KernelSU não executa nenhuma ação. Alguns módulos, como Zygisksu, podem usar essa opção para determinar se o descarregamento do módulo é necessário.

`,46),n=[r];function l(t,c,i,d,u,m){return e(),s("div",null,n)}const g=o(p,[["render",l]]);export{y as __pageData,g as default}; diff --git a/assets/pt_BR_guide_app-profile.md.dd333014.lean.js b/assets/pt_BR_guide_app-profile.md.dd333014.lean.js new file mode 100644 index 000000000000..329272a36eea --- /dev/null +++ b/assets/pt_BR_guide_app-profile.md.dd333014.lean.js @@ -0,0 +1 @@ +import{_ as o,o as e,c as s,Q as a}from"./chunks/framework.ec8f7e8e.js";const y=JSON.parse('{"title":"Perfil do Aplicativo","description":"","frontmatter":{},"headers":[],"relativePath":"pt_BR/guide/app-profile.md","filePath":"pt_BR/guide/app-profile.md"}'),p={name:"pt_BR/guide/app-profile.md"},r=a("",46),n=[r];function l(t,c,i,d,u,m){return e(),s("div",null,n)}const g=o(p,[["render",l]]);export{y as __pageData,g as default}; diff --git a/assets/pt_BR_guide_difference-with-magisk.md.319599e7.js b/assets/pt_BR_guide_difference-with-magisk.md.c0878d2e.js similarity index 98% rename from assets/pt_BR_guide_difference-with-magisk.md.319599e7.js rename to assets/pt_BR_guide_difference-with-magisk.md.c0878d2e.js index e9571092e913..b9c64eefdfd7 100644 --- a/assets/pt_BR_guide_difference-with-magisk.md.319599e7.js +++ b/assets/pt_BR_guide_difference-with-magisk.md.c0878d2e.js @@ -1 +1 @@ -import{_ as e,o,c as a,O as s}from"./chunks/framework.43781440.js";const h=JSON.parse('{"title":"Diferença com Magisk","description":"","frontmatter":{},"headers":[],"relativePath":"pt_BR/guide/difference-with-magisk.md","filePath":"pt_BR/guide/difference-with-magisk.md"}'),i={name:"pt_BR/guide/difference-with-magisk.md"},t=s('

Diferença com Magisk

Embora existam muitas semelhanças entre os módulos KernelSU e os módulos Magisk, existem inevitavelmente algumas diferenças devido aos seus mecanismos de implementação completamente diferentes. Se você deseja que seu módulo seja executado no Magisk e no KernelSU, você deve entender essas diferenças.

Semelhanças

  • Formato de arquivo do módulo: ambos usam o formato zip para organizar os módulos, e o formato dos módulos é quase o mesmo.
  • Diretório de instalação do módulo: ambos localizados em /data/adb/modules.
  • Sem sistema: ambos suportam a modificação de /system de maneira sem sistema por meio de módulos.
  • post-fs-data.sh: o tempo de execução e a semântica são exatamente os mesmos.
  • service.sh: o tempo de execução e a semântica são exatamente os mesmos.
  • system.prop: completamente o mesmo.
  • sepolicy.rule: completamente o mesmo.
  • BusyBox: os scripts são executados no BusyBox com o "Modo Autônomo" ativado em ambos os casos.

Diferenças

Antes de entender as diferenças, você precisa saber diferenciar se o seu módulo está rodando no KernelSU ou Magisk. Você pode usar a variável de ambiente KSU para diferenciá-la em todos os locais onde você pode executar os scripts do módulo (customize.sh, post-fs-data.sh, service.sh). No KernelSU, esta variável de ambiente será definida como true.

Aqui estão algumas diferenças:

  • Os módulos KernelSU não podem ser instalados no modo Recovery.
  • Os módulos KernelSU não têm suporte integrado para Zygisk (mas você pode usar módulos Zygisk através do ZygiskNext.
  • O método para substituir ou excluir arquivos nos módulos KernelSU é completamente diferente do Magisk. O KernelSU não suporta o método .replace. Em vez disso, você precisa criar um arquivo com o mesmo nome mknod filename c 0 0 para excluir o arquivo correspondente.
  • Os diretórios do BusyBox são diferentes. O BusyBox integrado no KernelSU está localizado em /data/adb/ksu/bin/busybox, enquanto no Magisk está em /data/adb/magisk/busybox. Observe que este é um comportamento interno do KernelSU e pode mudar no futuro!
  • O KernelSU não suporta arquivos .replace, entretanto, o KernelSU suporta as variáveis ​​REMOVE e REPLACE para remover ou substituir arquivos e pastas.
  • O KernelSU adiciona o estágio boot-completed para executar alguns scripts na inicialização concluída.
  • O KernelSU adiciona o estágio post-mount para executar alguns scripts após montar overlayfs.
',8),r=[t];function d(n,m,c,l,u,p){return o(),a("div",null,r)}const g=e(i,[["render",d]]);export{h as __pageData,g as default}; +import{_ as e,o,c as a,Q as s}from"./chunks/framework.ec8f7e8e.js";const h=JSON.parse('{"title":"Diferença com Magisk","description":"","frontmatter":{},"headers":[],"relativePath":"pt_BR/guide/difference-with-magisk.md","filePath":"pt_BR/guide/difference-with-magisk.md"}'),i={name:"pt_BR/guide/difference-with-magisk.md"},t=s('

Diferença com Magisk

Embora existam muitas semelhanças entre os módulos KernelSU e os módulos Magisk, existem inevitavelmente algumas diferenças devido aos seus mecanismos de implementação completamente diferentes. Se você deseja que seu módulo seja executado no Magisk e no KernelSU, você deve entender essas diferenças.

Semelhanças

  • Formato de arquivo do módulo: ambos usam o formato zip para organizar os módulos, e o formato dos módulos é quase o mesmo.
  • Diretório de instalação do módulo: ambos localizados em /data/adb/modules.
  • Sem sistema: ambos suportam a modificação de /system de maneira sem sistema por meio de módulos.
  • post-fs-data.sh: o tempo de execução e a semântica são exatamente os mesmos.
  • service.sh: o tempo de execução e a semântica são exatamente os mesmos.
  • system.prop: completamente o mesmo.
  • sepolicy.rule: completamente o mesmo.
  • BusyBox: os scripts são executados no BusyBox com o "Modo Autônomo" ativado em ambos os casos.

Diferenças

Antes de entender as diferenças, você precisa saber diferenciar se o seu módulo está rodando no KernelSU ou Magisk. Você pode usar a variável de ambiente KSU para diferenciá-la em todos os locais onde você pode executar os scripts do módulo (customize.sh, post-fs-data.sh, service.sh). No KernelSU, esta variável de ambiente será definida como true.

Aqui estão algumas diferenças:

  • Os módulos KernelSU não podem ser instalados no modo Recovery.
  • Os módulos KernelSU não têm suporte integrado para Zygisk (mas você pode usar módulos Zygisk através do ZygiskNext.
  • O método para substituir ou excluir arquivos nos módulos KernelSU é completamente diferente do Magisk. O KernelSU não suporta o método .replace. Em vez disso, você precisa criar um arquivo com o mesmo nome mknod filename c 0 0 para excluir o arquivo correspondente.
  • Os diretórios do BusyBox são diferentes. O BusyBox integrado no KernelSU está localizado em /data/adb/ksu/bin/busybox, enquanto no Magisk está em /data/adb/magisk/busybox. Observe que este é um comportamento interno do KernelSU e pode mudar no futuro!
  • O KernelSU não suporta arquivos .replace, entretanto, o KernelSU suporta as variáveis ​​REMOVE e REPLACE para remover ou substituir arquivos e pastas.
  • O KernelSU adiciona o estágio boot-completed para executar alguns scripts na inicialização concluída.
  • O KernelSU adiciona o estágio post-mount para executar alguns scripts após montar overlayfs.
',8),r=[t];function d(n,m,c,l,u,p){return o(),a("div",null,r)}const g=e(i,[["render",d]]);export{h as __pageData,g as default}; diff --git a/assets/pt_BR_guide_difference-with-magisk.md.319599e7.lean.js b/assets/pt_BR_guide_difference-with-magisk.md.c0878d2e.lean.js similarity index 86% rename from assets/pt_BR_guide_difference-with-magisk.md.319599e7.lean.js rename to assets/pt_BR_guide_difference-with-magisk.md.c0878d2e.lean.js index 5f9ba34a9693..33bd4c4106be 100644 --- a/assets/pt_BR_guide_difference-with-magisk.md.319599e7.lean.js +++ b/assets/pt_BR_guide_difference-with-magisk.md.c0878d2e.lean.js @@ -1 +1 @@ -import{_ as e,o,c as a,O as s}from"./chunks/framework.43781440.js";const h=JSON.parse('{"title":"Diferença com Magisk","description":"","frontmatter":{},"headers":[],"relativePath":"pt_BR/guide/difference-with-magisk.md","filePath":"pt_BR/guide/difference-with-magisk.md"}'),i={name:"pt_BR/guide/difference-with-magisk.md"},t=s("",8),r=[t];function d(n,m,c,l,u,p){return o(),a("div",null,r)}const g=e(i,[["render",d]]);export{h as __pageData,g as default}; +import{_ as e,o,c as a,Q as s}from"./chunks/framework.ec8f7e8e.js";const h=JSON.parse('{"title":"Diferença com Magisk","description":"","frontmatter":{},"headers":[],"relativePath":"pt_BR/guide/difference-with-magisk.md","filePath":"pt_BR/guide/difference-with-magisk.md"}'),i={name:"pt_BR/guide/difference-with-magisk.md"},t=s("",8),r=[t];function d(n,m,c,l,u,p){return o(),a("div",null,r)}const g=e(i,[["render",d]]);export{h as __pageData,g as default}; diff --git a/assets/pt_BR_guide_faq.md.e1ef8622.js b/assets/pt_BR_guide_faq.md.01a58b64.js similarity index 99% rename from assets/pt_BR_guide_faq.md.e1ef8622.js rename to assets/pt_BR_guide_faq.md.01a58b64.js index dc39bbd3155c..d9e754c4019a 100644 --- a/assets/pt_BR_guide_faq.md.e1ef8622.js +++ b/assets/pt_BR_guide_faq.md.01a58b64.js @@ -1 +1 @@ -import{_ as e,o,c as a,O as r}from"./chunks/framework.43781440.js";const k=JSON.parse('{"title":"FAQ","description":"","frontmatter":{},"headers":[],"relativePath":"pt_BR/guide/faq.md","filePath":"pt_BR/guide/faq.md"}'),s={name:"pt_BR/guide/faq.md"},i=r('

FAQ

KernelSU oferece suporte ao meu dispositivo?

Primeiro, seu dispositivo deve ser capaz de desbloquear o bootloader. Se não, então não há suporte.

Em seguida, instale o app gerenciador KernelSU em seu dispositivo e abra-o, se mostrar Sem suporte então seu dispositivo não pode ser suportado imediatamente, mas você pode construir a fonte do kernel e integrar o KernelSU para fazê-lo funcionar ou usar dispositivos com suporte não oficial.

KernelSU precisa desbloquear o Bootloader?

Certamente, sim.

KernelSU suporta módulos?

Sim, mas está na versão inicial, pode apresentar bugs. Por favor, aguarde até que fique estável.

KernelSU suporta Xposed?

Sim, Dreamland e TaiChi funcionam agora. Para o LSPosed, você pode fazer funcionar usando ZygiskNext.

KernelSU suporta Zygisk?

KernelSU não tem suporte integrado ao Zygisk, mas você pode usar ZygiskNext.

KernelSU é compatível com Magisk?

O sistema de módulos do KernelSU está em conflito com a montagem mágica do Magisk, se houver algum módulo habilitado no KernelSU, então todo o Magisk não funcionaria.

Mas se você usar apenas o su do KernelSU, então funcionará bem com o Magisk. KernelSU modifica o kernel e o Magisk modifica o ramdisk, eles podem trabalhar juntos.

KernelSU substituirá o Magisk?

Achamos que não e esse não é o nosso objetivo. O Magisk é bom o suficiente para solução root do espaço do usuário e terá uma longa vida. O objetivo do KernelSU é fornecer uma interface de kernel aos usuários, não substituindo o Magisk.

KernelSU pode oferecer suporte a dispositivos não GKI?

É possível. Mas você deve baixar o código-fonte do kernel e integrar o KernelSU à árvore do código-fonte e compilar o kernel você mesmo.

KernelSU pode oferecer suporte a dispositivos abaixo do Android 12?

É o kernel do dispositivo que afeta a compatibilidade do KernelSU e não tem nada a ver com a versão do Android. A única restrição é que os dispositivos lançados com Android 12 devem ser kernel 5.10+ (dispositivos GKI). Então:

  1. Os dispositivos lançados com Android 12 devem ser compatíveis.
  2. Dispositivos com kernel antigo (alguns dispositivos Android 12 também têm o kernel antigo) são compatíveis (você mesmo deve construir o kernel).

KernelSU pode suportar kernel antigo?

É possível, o KernelSU é portado para o kernel 4.14 agora, para o kernel mais antigo, você precisa fazer o backport manualmente e PRs são bem-vindos!

Como integrar o KernelSU para o kernel antigo?

Por favor, consulte a guia Como integrar o KernelSU para kernels não GKI

Por que minha versão do Android é 13 e o kernel mostra “android12-5.10”?

A versão do Kernel não tem nada a ver com a versão do Android, se você precisar fazer o flash do kernel, use sempre a versão do kernel, a versão do Android não é tão importante.

Existe algum namespace de montagem --mount-master/global no KernelSU?

Não existe agora (talvez no futuro), mas há muitas maneiras de mudar manualmente para o namespace de montagem global, como:

  1. nsenter -t 1 -m sh para obter um shell no namespace de montagem global.
  2. Adicione nsenter --mount=/proc/1/ns/mnt ao comando que você deseja executar, o comando será executado no namespace de montagem global. O KernelSU também está usando desta forma

Eu sou GKI1.0, posso usar isso?

GKI1 é completamente diferente do GKI2, você deve compilar o kernel sozinho.

',33),n=[i];function t(l,d,u,p,m,c){return o(),a("div",null,n)}const g=e(s,[["render",t]]);export{k as __pageData,g as default}; +import{_ as e,o,c as a,Q as r}from"./chunks/framework.ec8f7e8e.js";const k=JSON.parse('{"title":"FAQ","description":"","frontmatter":{},"headers":[],"relativePath":"pt_BR/guide/faq.md","filePath":"pt_BR/guide/faq.md"}'),s={name:"pt_BR/guide/faq.md"},i=r('

FAQ

KernelSU oferece suporte ao meu dispositivo?

Primeiro, seu dispositivo deve ser capaz de desbloquear o bootloader. Se não, então não há suporte.

Em seguida, instale o app gerenciador KernelSU em seu dispositivo e abra-o, se mostrar Sem suporte então seu dispositivo não pode ser suportado imediatamente, mas você pode construir a fonte do kernel e integrar o KernelSU para fazê-lo funcionar ou usar dispositivos com suporte não oficial.

KernelSU precisa desbloquear o Bootloader?

Certamente, sim.

KernelSU suporta módulos?

Sim, mas está na versão inicial, pode apresentar bugs. Por favor, aguarde até que fique estável.

KernelSU suporta Xposed?

Sim, Dreamland e TaiChi funcionam agora. Para o LSPosed, você pode fazer funcionar usando ZygiskNext.

KernelSU suporta Zygisk?

KernelSU não tem suporte integrado ao Zygisk, mas você pode usar ZygiskNext.

KernelSU é compatível com Magisk?

O sistema de módulos do KernelSU está em conflito com a montagem mágica do Magisk, se houver algum módulo habilitado no KernelSU, então todo o Magisk não funcionaria.

Mas se você usar apenas o su do KernelSU, então funcionará bem com o Magisk. KernelSU modifica o kernel e o Magisk modifica o ramdisk, eles podem trabalhar juntos.

KernelSU substituirá o Magisk?

Achamos que não e esse não é o nosso objetivo. O Magisk é bom o suficiente para solução root do espaço do usuário e terá uma longa vida. O objetivo do KernelSU é fornecer uma interface de kernel aos usuários, não substituindo o Magisk.

KernelSU pode oferecer suporte a dispositivos não GKI?

É possível. Mas você deve baixar o código-fonte do kernel e integrar o KernelSU à árvore do código-fonte e compilar o kernel você mesmo.

KernelSU pode oferecer suporte a dispositivos abaixo do Android 12?

É o kernel do dispositivo que afeta a compatibilidade do KernelSU e não tem nada a ver com a versão do Android. A única restrição é que os dispositivos lançados com Android 12 devem ser kernel 5.10+ (dispositivos GKI). Então:

  1. Os dispositivos lançados com Android 12 devem ser compatíveis.
  2. Dispositivos com kernel antigo (alguns dispositivos Android 12 também têm o kernel antigo) são compatíveis (você mesmo deve construir o kernel).

KernelSU pode suportar kernel antigo?

É possível, o KernelSU é portado para o kernel 4.14 agora, para o kernel mais antigo, você precisa fazer o backport manualmente e PRs são bem-vindos!

Como integrar o KernelSU para o kernel antigo?

Por favor, consulte a guia Como integrar o KernelSU para kernels não GKI

Por que minha versão do Android é 13 e o kernel mostra “android12-5.10”?

A versão do Kernel não tem nada a ver com a versão do Android, se você precisar fazer o flash do kernel, use sempre a versão do kernel, a versão do Android não é tão importante.

Existe algum namespace de montagem --mount-master/global no KernelSU?

Não existe agora (talvez no futuro), mas há muitas maneiras de mudar manualmente para o namespace de montagem global, como:

  1. nsenter -t 1 -m sh para obter um shell no namespace de montagem global.
  2. Adicione nsenter --mount=/proc/1/ns/mnt ao comando que você deseja executar, o comando será executado no namespace de montagem global. O KernelSU também está usando desta forma

Eu sou GKI1.0, posso usar isso?

GKI1 é completamente diferente do GKI2, você deve compilar o kernel sozinho.

',33),n=[i];function t(l,d,u,p,m,c){return o(),a("div",null,n)}const g=e(s,[["render",t]]);export{k as __pageData,g as default}; diff --git a/assets/pt_BR_guide_faq.md.e1ef8622.lean.js b/assets/pt_BR_guide_faq.md.01a58b64.lean.js similarity index 83% rename from assets/pt_BR_guide_faq.md.e1ef8622.lean.js rename to assets/pt_BR_guide_faq.md.01a58b64.lean.js index 5cf59bfe4540..55dee1d08899 100644 --- a/assets/pt_BR_guide_faq.md.e1ef8622.lean.js +++ b/assets/pt_BR_guide_faq.md.01a58b64.lean.js @@ -1 +1 @@ -import{_ as e,o,c as a,O as r}from"./chunks/framework.43781440.js";const k=JSON.parse('{"title":"FAQ","description":"","frontmatter":{},"headers":[],"relativePath":"pt_BR/guide/faq.md","filePath":"pt_BR/guide/faq.md"}'),s={name:"pt_BR/guide/faq.md"},i=r("",33),n=[i];function t(l,d,u,p,m,c){return o(),a("div",null,n)}const g=e(s,[["render",t]]);export{k as __pageData,g as default}; +import{_ as e,o,c as a,Q as r}from"./chunks/framework.ec8f7e8e.js";const k=JSON.parse('{"title":"FAQ","description":"","frontmatter":{},"headers":[],"relativePath":"pt_BR/guide/faq.md","filePath":"pt_BR/guide/faq.md"}'),s={name:"pt_BR/guide/faq.md"},i=r("",33),n=[i];function t(l,d,u,p,m,c){return o(),a("div",null,n)}const g=e(s,[["render",t]]);export{k as __pageData,g as default}; diff --git a/assets/pt_BR_guide_hidden-features.md.df3f1d4b.js b/assets/pt_BR_guide_hidden-features.md.817db7e8.js similarity index 86% rename from assets/pt_BR_guide_hidden-features.md.df3f1d4b.js rename to assets/pt_BR_guide_hidden-features.md.817db7e8.js index 1b03d7ac985c..ef01e982ab28 100644 --- a/assets/pt_BR_guide_hidden-features.md.df3f1d4b.js +++ b/assets/pt_BR_guide_hidden-features.md.817db7e8.js @@ -1 +1 @@ -import{_ as e,o as a,c as o,O as r}from"./chunks/framework.43781440.js";const m=JSON.parse('{"title":"Recursos ocultos","description":"","frontmatter":{},"headers":[],"relativePath":"pt_BR/guide/hidden-features.md","filePath":"pt_BR/guide/hidden-features.md"}'),s={name:"pt_BR/guide/hidden-features.md"},t=r('

Recursos ocultos

.ksurc

Por padrão, /system/bin/sh carrega /system/etc/mkshrc.

Você pode fazer su carregar um arquivo rc personalizado criando um arquivo /data/adb/ksu/.ksurc.

',4),c=[t];function d(u,i,n,_,l,h){return a(),o("div",null,c)}const f=e(s,[["render",d]]);export{m as __pageData,f as default}; +import{_ as e,o as a,c as o,Q as r}from"./chunks/framework.ec8f7e8e.js";const m=JSON.parse('{"title":"Recursos ocultos","description":"","frontmatter":{},"headers":[],"relativePath":"pt_BR/guide/hidden-features.md","filePath":"pt_BR/guide/hidden-features.md"}'),s={name:"pt_BR/guide/hidden-features.md"},t=r('

Recursos ocultos

.ksurc

Por padrão, /system/bin/sh carrega /system/etc/mkshrc.

Você pode fazer su carregar um arquivo rc personalizado criando um arquivo /data/adb/ksu/.ksurc.

',4),c=[t];function d(u,i,n,_,l,h){return a(),o("div",null,c)}const f=e(s,[["render",d]]);export{m as __pageData,f as default}; diff --git a/assets/pt_BR_guide_hidden-features.md.df3f1d4b.lean.js b/assets/pt_BR_guide_hidden-features.md.817db7e8.lean.js similarity index 70% rename from assets/pt_BR_guide_hidden-features.md.df3f1d4b.lean.js rename to assets/pt_BR_guide_hidden-features.md.817db7e8.lean.js index 3c845b8bfc06..f0fb9ed45199 100644 --- a/assets/pt_BR_guide_hidden-features.md.df3f1d4b.lean.js +++ b/assets/pt_BR_guide_hidden-features.md.817db7e8.lean.js @@ -1 +1 @@ -import{_ as e,o as a,c as o,O as r}from"./chunks/framework.43781440.js";const m=JSON.parse('{"title":"Recursos ocultos","description":"","frontmatter":{},"headers":[],"relativePath":"pt_BR/guide/hidden-features.md","filePath":"pt_BR/guide/hidden-features.md"}'),s={name:"pt_BR/guide/hidden-features.md"},t=r("",4),c=[t];function d(u,i,n,_,l,h){return a(),o("div",null,c)}const f=e(s,[["render",d]]);export{m as __pageData,f as default}; +import{_ as e,o as a,c as o,Q as r}from"./chunks/framework.ec8f7e8e.js";const m=JSON.parse('{"title":"Recursos ocultos","description":"","frontmatter":{},"headers":[],"relativePath":"pt_BR/guide/hidden-features.md","filePath":"pt_BR/guide/hidden-features.md"}'),s={name:"pt_BR/guide/hidden-features.md"},t=r("",4),c=[t];function d(u,i,n,_,l,h){return a(),o("div",null,c)}const f=e(s,[["render",d]]);export{m as __pageData,f as default}; diff --git a/assets/pt_BR_guide_how-to-build.md.50a0b380.js b/assets/pt_BR_guide_how-to-build.md.50a0b380.js deleted file mode 100644 index de210d4834f5..000000000000 --- a/assets/pt_BR_guide_how-to-build.md.50a0b380.js +++ /dev/null @@ -1,4 +0,0 @@ -import{_ as s,o,c as e,O as a}from"./chunks/framework.43781440.js";const h=JSON.parse('{"title":"Como construir o KernelSU?","description":"","frontmatter":{},"headers":[],"relativePath":"pt_BR/guide/how-to-build.md","filePath":"pt_BR/guide/how-to-build.md"}'),n={name:"pt_BR/guide/how-to-build.md"},l=a(`

Como construir o KernelSU?

Primeiro, você deve ler a documentação oficial do Android para construção do kernel:

  1. Como criar kernels
  2. Builds de versão de imagem genérica do kernel (GKI)

AVISO

Esta página é para dispositivos GKI, se você usa um kernel antigo, consulte Como integrar o KernelSU para kernels não GKI.

Construir o kernel

Sincronize o código-fonte do kernel

sh
repo init -u https://android.googlesource.com/kernel/manifest
-mv <kernel_manifest.xml> .repo/manifests
-repo init -m manifest.xml
-repo sync

O <kernel_manifest.xml> é um arquivo de manifesto que pode determinar uma construção exclusivamente, você pode usar o manifesto para fazer uma construção re-preduzível. Você deve baixar o arquivo de manifesto em compilações de lançamento do Google GKI.

Construir

Por favor, verifique Como criar kernels primeiro.

Por exemplo, precisamos construir a imagem do kernel aarch64:

sh
LTO=thin BUILD_CONFIG=common/build.config.gki.aarch64 build/build.sh

Não se esqueça de adicionar o sinalizador LTO=thin, caso contrário a compilação poderá falhar se a memória do seu computador for inferior a 24 GB.

A partir do Android 13, o kernel é construído pelo bazel:

sh
tools/bazel build --config=fast //common:kernel_aarch64_dist

Construir o kernel com KernelSU

Se você conseguir construir o kernel com sucesso, então construir o KernelSU é muito fácil. Selecione qualquer um executado no diretório raiz de origem do kernel:

  • Tag mais recente (estável)
sh
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -
  • branch principal (dev)
sh
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -s main
  • Selecione a tag (Como v0.5.2)
sh
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -s v0.5.2

E então reconstrua o kernel e você obterá uma imagem do kernel com KernelSU!

`,24),r=[l];function t(p,c,i,d,u,C){return o(),e("div",null,r)}const y=s(n,[["render",t]]);export{h as __pageData,y as default}; diff --git a/assets/pt_BR_guide_how-to-build.md.50a0b380.lean.js b/assets/pt_BR_guide_how-to-build.md.50a0b380.lean.js deleted file mode 100644 index ade354115b5a..000000000000 --- a/assets/pt_BR_guide_how-to-build.md.50a0b380.lean.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as s,o,c as e,O as a}from"./chunks/framework.43781440.js";const h=JSON.parse('{"title":"Como construir o KernelSU?","description":"","frontmatter":{},"headers":[],"relativePath":"pt_BR/guide/how-to-build.md","filePath":"pt_BR/guide/how-to-build.md"}'),n={name:"pt_BR/guide/how-to-build.md"},l=a("",24),r=[l];function t(p,c,i,d,u,C){return o(),e("div",null,r)}const y=s(n,[["render",t]]);export{h as __pageData,y as default}; diff --git a/assets/pt_BR_guide_how-to-build.md.df81a6ce.js b/assets/pt_BR_guide_how-to-build.md.df81a6ce.js new file mode 100644 index 000000000000..d7df6df341c4 --- /dev/null +++ b/assets/pt_BR_guide_how-to-build.md.df81a6ce.js @@ -0,0 +1,7 @@ +import{_ as s,o,c as a,Q as n}from"./chunks/framework.ec8f7e8e.js";const h=JSON.parse('{"title":"Como construir o KernelSU?","description":"","frontmatter":{},"headers":[],"relativePath":"pt_BR/guide/how-to-build.md","filePath":"pt_BR/guide/how-to-build.md"}'),e={name:"pt_BR/guide/how-to-build.md"},l=n(`

Como construir o KernelSU?

Primeiro, você deve ler a documentação oficial do Android para construção do kernel:

  1. Como criar kernels
  2. Builds de versão de imagem genérica do kernel (GKI)

AVISO

Esta página é para dispositivos GKI, se você usa um kernel antigo, consulte Como integrar o KernelSU para kernels não GKI.

Construir o kernel

Sincronize o código-fonte do kernel

sh
repo init -u https://android.googlesource.com/kernel/manifest
+mv <kernel_manifest.xml> .repo/manifests
+repo init -m manifest.xml
+repo sync
repo init -u https://android.googlesource.com/kernel/manifest
+mv <kernel_manifest.xml> .repo/manifests
+repo init -m manifest.xml
+repo sync

O <kernel_manifest.xml> é um arquivo de manifesto que pode determinar uma construção exclusivamente, você pode usar o manifesto para fazer uma construção re-preduzível. Você deve baixar o arquivo de manifesto em compilações de lançamento do Google GKI.

Construir

Por favor, verifique Como criar kernels primeiro.

Por exemplo, precisamos construir a imagem do kernel aarch64:

sh
LTO=thin BUILD_CONFIG=common/build.config.gki.aarch64 build/build.sh
LTO=thin BUILD_CONFIG=common/build.config.gki.aarch64 build/build.sh

Não se esqueça de adicionar o sinalizador LTO=thin, caso contrário a compilação poderá falhar se a memória do seu computador for inferior a 24 GB.

A partir do Android 13, o kernel é construído pelo bazel:

sh
tools/bazel build --config=fast //common:kernel_aarch64_dist
tools/bazel build --config=fast //common:kernel_aarch64_dist

Construir o kernel com KernelSU

Se você conseguir construir o kernel com sucesso, então construir o KernelSU é muito fácil. Selecione qualquer um executado no diretório raiz de origem do kernel:

  • Tag mais recente (estável)
sh
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -
  • branch principal (dev)
sh
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -s main
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -s main
  • Selecione a tag (Como v0.5.2)
sh
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -s v0.5.2
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -s v0.5.2

E então reconstrua o kernel e você obterá uma imagem do kernel com KernelSU!

`,24),p=[l];function r(t,c,i,d,u,y){return o(),a("div",null,p)}const m=s(e,[["render",r]]);export{h as __pageData,m as default}; diff --git a/assets/pt_BR_guide_how-to-build.md.df81a6ce.lean.js b/assets/pt_BR_guide_how-to-build.md.df81a6ce.lean.js new file mode 100644 index 000000000000..7d6a8ab706d9 --- /dev/null +++ b/assets/pt_BR_guide_how-to-build.md.df81a6ce.lean.js @@ -0,0 +1 @@ +import{_ as s,o,c as a,Q as n}from"./chunks/framework.ec8f7e8e.js";const h=JSON.parse('{"title":"Como construir o KernelSU?","description":"","frontmatter":{},"headers":[],"relativePath":"pt_BR/guide/how-to-build.md","filePath":"pt_BR/guide/how-to-build.md"}'),e={name:"pt_BR/guide/how-to-build.md"},l=n("",24),p=[l];function r(t,c,i,d,u,y){return o(),a("div",null,p)}const m=s(e,[["render",r]]);export{h as __pageData,m as default}; diff --git a/assets/pt_BR_guide_how-to-integrate-for-non-gki.md.6bd8e7c3.js b/assets/pt_BR_guide_how-to-integrate-for-non-gki.md.6bd8e7c3.js new file mode 100644 index 000000000000..8a121ec57bdb --- /dev/null +++ b/assets/pt_BR_guide_how-to-integrate-for-non-gki.md.6bd8e7c3.js @@ -0,0 +1,303 @@ +import{_ as s,o as n,c as a,Q as e}from"./chunks/framework.ec8f7e8e.js";const _=JSON.parse('{"title":"Como integrar o KernelSU para kernels não GKI?","description":"","frontmatter":{},"headers":[],"relativePath":"pt_BR/guide/how-to-integrate-for-non-gki.md","filePath":"pt_BR/guide/how-to-integrate-for-non-gki.md"}'),l={name:"pt_BR/guide/how-to-integrate-for-non-gki.md"},p=e(`

Como integrar o KernelSU para kernels não GKI?

O KernelSU pode ser integrado em kernels não GKI e foi portado para 4.14 e versões anteriores.

Devido à fragmentação de kernels não GKI, não temos uma maneira uniforme de construí-lo, portanto não podemos fornecer imagens boot não GKI. Mas você mesmo pode construir o kernel com o KernelSU integrado.

Primeiro, você deve ser capaz de construir um kernel inicializável a partir do código-fonte do kernel. Se o kernel não for de código aberto, será difícil executar o KernelSU no seu dispositivo.

Se você puder construir um kernel inicializável, existem duas maneiras de integrar o KernelSU ao código-fonte do kernel:

  1. Automaticamente com kprobe
  2. Manualmente

Integrar com kprobe

O KernelSU usa kprobe para fazer ganchos de kernel, se o kprobe funcionar bem em seu kernel, é recomendado usar desta forma.

Primeiro, adicione o KernelSU à árvore de origem do kernel:

sh
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -

Então, você deve verificar se o kprobe está ativado na configuração do seu kernel, se não estiver, adicione estas configurações a ele:

CONFIG_KPROBES=y
+CONFIG_HAVE_KPROBES=y
+CONFIG_KPROBE_EVENTS=y
CONFIG_KPROBES=y
+CONFIG_HAVE_KPROBES=y
+CONFIG_KPROBE_EVENTS=y

E construa seu kernel novamente, KernelSU deve funcionar bem.

Se você descobrir que o KPROBES ainda não está ativado, você pode tentar ativar CONFIG_MODULES. (Se ainda assim não surtir efeito, use make menuconfig para procurar outras dependências do KPROBES)

Mas se você entrar em um bootloop quando o KernelSU for integrado, talvez o kprobe esteja quebrado em seu kernel, você deve corrigir o bug do kprobe ou usar o segundo caminho.

COMO VERIFICAR SE O KPROBE ESTÁ QUEBRADO?

Comente ksu_enable_sucompat() e ksu_enable_ksud() em KernelSU/kernel/ksu.c, se o dispositivo inicializar normalmente, então o kprobe pode estar quebrado.

Modifique manualmente a fonte do kernel

Se o kprobe não funcionar no seu kernel (pode ser um bug do upstream ou do kernel abaixo de 4.8), então você pode tentar desta forma:

Primeiro, adicione o KernelSU à árvore de origem do kernel:

  • Tag mais recente (estável)
sh
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -
  • branch principal (dev)
sh
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -s main
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -s main
  • Selecione a tag (Como v0.5.2)
sh
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -s v0.5.2
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -s v0.5.2

Em seguida, adicione chamadas KernelSU à fonte do kernel. Aqui está um patch para referência:

diff
diff --git a/fs/exec.c b/fs/exec.c
+index ac59664eaecf..bdd585e1d2cc 100644
+--- a/fs/exec.c
++++ b/fs/exec.c
+@@ -1890,11 +1890,14 @@ static int __do_execve_file(int fd, struct filename *filename,
+ 	return retval;
+ }
+ 
++extern bool ksu_execveat_hook __read_mostly;
++extern int ksu_handle_execveat(int *fd, struct filename **filename_ptr, void *argv,
++			void *envp, int *flags);
++extern int ksu_handle_execveat_sucompat(int *fd, struct filename **filename_ptr,
++				 void *argv, void *envp, int *flags);
+ static int do_execveat_common(int fd, struct filename *filename,
+ 			      struct user_arg_ptr argv,
+ 			      struct user_arg_ptr envp,
+ 			      int flags)
+ {
++	if (unlikely(ksu_execveat_hook))
++		ksu_handle_execveat(&fd, &filename, &argv, &envp, &flags);
++	else
++		ksu_handle_execveat_sucompat(&fd, &filename, &argv, &envp, &flags);
+ 	return __do_execve_file(fd, filename, argv, envp, flags, NULL);
+ }
+ 
+diff --git a/fs/open.c b/fs/open.c
+index 05036d819197..965b84d486b8 100644
+--- a/fs/open.c
++++ b/fs/open.c
+@@ -348,6 +348,8 @@ SYSCALL_DEFINE4(fallocate, int, fd, int, mode, loff_t, offset, loff_t, len)
+ 	return ksys_fallocate(fd, mode, offset, len);
+ }
+ 
++extern int ksu_handle_faccessat(int *dfd, const char __user **filename_user, int *mode,
++			 int *flags);
+ /*
+  * access() needs to use the real uid/gid, not the effective uid/gid.
+  * We do this by temporarily clearing all FS-related capabilities and
+@@ -355,6 +357,7 @@ SYSCALL_DEFINE4(fallocate, int, fd, int, mode, loff_t, offset, loff_t, len)
+  */
+ long do_faccessat(int dfd, const char __user *filename, int mode)
+ {
++	ksu_handle_faccessat(&dfd, &filename, &mode, NULL);
+ 	const struct cred *old_cred;
+ 	struct cred *override_cred;
+ 	struct path path;
+diff --git a/fs/read_write.c b/fs/read_write.c
+index 650fc7e0f3a6..55be193913b6 100644
+--- a/fs/read_write.c
++++ b/fs/read_write.c
+@@ -434,10 +434,14 @@ ssize_t kernel_read(struct file *file, void *buf, size_t count, loff_t *pos)
+ }
+ EXPORT_SYMBOL(kernel_read);
+ 
++extern bool ksu_vfs_read_hook __read_mostly;
++extern int ksu_handle_vfs_read(struct file **file_ptr, char __user **buf_ptr,
++			size_t *count_ptr, loff_t **pos);
+ ssize_t vfs_read(struct file *file, char __user *buf, size_t count, loff_t *pos)
+ {
+ 	ssize_t ret;
+ 
++	if (unlikely(ksu_vfs_read_hook))
++		ksu_handle_vfs_read(&file, &buf, &count, &pos);
++
+ 	if (!(file->f_mode & FMODE_READ))
+ 		return -EBADF;
+ 	if (!(file->f_mode & FMODE_CAN_READ))
+diff --git a/fs/stat.c b/fs/stat.c
+index 376543199b5a..82adcef03ecc 100644
+--- a/fs/stat.c
++++ b/fs/stat.c
+@@ -148,6 +148,8 @@ int vfs_statx_fd(unsigned int fd, struct kstat *stat,
+ }
+ EXPORT_SYMBOL(vfs_statx_fd);
+ 
++extern int ksu_handle_stat(int *dfd, const char __user **filename_user, int *flags);
++
+ /**
+  * vfs_statx - Get basic and extra attributes by filename
+  * @dfd: A file descriptor representing the base dir for a relative filename
+@@ -170,6 +172,7 @@ int vfs_statx(int dfd, const char __user *filename, int flags,
+ 	int error = -EINVAL;
+ 	unsigned int lookup_flags = LOOKUP_FOLLOW | LOOKUP_AUTOMOUNT;
+ 
++	ksu_handle_stat(&dfd, &filename, &flags);
+ 	if ((flags & ~(AT_SYMLINK_NOFOLLOW | AT_NO_AUTOMOUNT |
+ 		       AT_EMPTY_PATH | KSTAT_QUERY_FLAGS)) != 0)
+ 		return -EINVAL;
diff --git a/fs/exec.c b/fs/exec.c
+index ac59664eaecf..bdd585e1d2cc 100644
+--- a/fs/exec.c
++++ b/fs/exec.c
+@@ -1890,11 +1890,14 @@ static int __do_execve_file(int fd, struct filename *filename,
+ 	return retval;
+ }
+ 
++extern bool ksu_execveat_hook __read_mostly;
++extern int ksu_handle_execveat(int *fd, struct filename **filename_ptr, void *argv,
++			void *envp, int *flags);
++extern int ksu_handle_execveat_sucompat(int *fd, struct filename **filename_ptr,
++				 void *argv, void *envp, int *flags);
+ static int do_execveat_common(int fd, struct filename *filename,
+ 			      struct user_arg_ptr argv,
+ 			      struct user_arg_ptr envp,
+ 			      int flags)
+ {
++	if (unlikely(ksu_execveat_hook))
++		ksu_handle_execveat(&fd, &filename, &argv, &envp, &flags);
++	else
++		ksu_handle_execveat_sucompat(&fd, &filename, &argv, &envp, &flags);
+ 	return __do_execve_file(fd, filename, argv, envp, flags, NULL);
+ }
+ 
+diff --git a/fs/open.c b/fs/open.c
+index 05036d819197..965b84d486b8 100644
+--- a/fs/open.c
++++ b/fs/open.c
+@@ -348,6 +348,8 @@ SYSCALL_DEFINE4(fallocate, int, fd, int, mode, loff_t, offset, loff_t, len)
+ 	return ksys_fallocate(fd, mode, offset, len);
+ }
+ 
++extern int ksu_handle_faccessat(int *dfd, const char __user **filename_user, int *mode,
++			 int *flags);
+ /*
+  * access() needs to use the real uid/gid, not the effective uid/gid.
+  * We do this by temporarily clearing all FS-related capabilities and
+@@ -355,6 +357,7 @@ SYSCALL_DEFINE4(fallocate, int, fd, int, mode, loff_t, offset, loff_t, len)
+  */
+ long do_faccessat(int dfd, const char __user *filename, int mode)
+ {
++	ksu_handle_faccessat(&dfd, &filename, &mode, NULL);
+ 	const struct cred *old_cred;
+ 	struct cred *override_cred;
+ 	struct path path;
+diff --git a/fs/read_write.c b/fs/read_write.c
+index 650fc7e0f3a6..55be193913b6 100644
+--- a/fs/read_write.c
++++ b/fs/read_write.c
+@@ -434,10 +434,14 @@ ssize_t kernel_read(struct file *file, void *buf, size_t count, loff_t *pos)
+ }
+ EXPORT_SYMBOL(kernel_read);
+ 
++extern bool ksu_vfs_read_hook __read_mostly;
++extern int ksu_handle_vfs_read(struct file **file_ptr, char __user **buf_ptr,
++			size_t *count_ptr, loff_t **pos);
+ ssize_t vfs_read(struct file *file, char __user *buf, size_t count, loff_t *pos)
+ {
+ 	ssize_t ret;
+ 
++	if (unlikely(ksu_vfs_read_hook))
++		ksu_handle_vfs_read(&file, &buf, &count, &pos);
++
+ 	if (!(file->f_mode & FMODE_READ))
+ 		return -EBADF;
+ 	if (!(file->f_mode & FMODE_CAN_READ))
+diff --git a/fs/stat.c b/fs/stat.c
+index 376543199b5a..82adcef03ecc 100644
+--- a/fs/stat.c
++++ b/fs/stat.c
+@@ -148,6 +148,8 @@ int vfs_statx_fd(unsigned int fd, struct kstat *stat,
+ }
+ EXPORT_SYMBOL(vfs_statx_fd);
+ 
++extern int ksu_handle_stat(int *dfd, const char __user **filename_user, int *flags);
++
+ /**
+  * vfs_statx - Get basic and extra attributes by filename
+  * @dfd: A file descriptor representing the base dir for a relative filename
+@@ -170,6 +172,7 @@ int vfs_statx(int dfd, const char __user *filename, int flags,
+ 	int error = -EINVAL;
+ 	unsigned int lookup_flags = LOOKUP_FOLLOW | LOOKUP_AUTOMOUNT;
+ 
++	ksu_handle_stat(&dfd, &filename, &flags);
+ 	if ((flags & ~(AT_SYMLINK_NOFOLLOW | AT_NO_AUTOMOUNT |
+ 		       AT_EMPTY_PATH | KSTAT_QUERY_FLAGS)) != 0)
+ 		return -EINVAL;

Você deve encontrar as quatro funções no código-fonte do kernel:

  1. do_faccessat, geralmente em fs/open.c
  2. do_execveat_common, geralmente em fs/exec.c
  3. vfs_read, geralmente em fs/read_write.c
  4. vfs_statx, geralmente em fs/stat.c

Se o seu kernel não tiver vfs_statx, use vfs_fstatat:

diff
diff --git a/fs/stat.c b/fs/stat.c
+index 068fdbcc9e26..5348b7bb9db2 100644
+--- a/fs/stat.c
++++ b/fs/stat.c
+@@ -87,6 +87,8 @@ int vfs_fstat(unsigned int fd, struct kstat *stat)
+ }
+ EXPORT_SYMBOL(vfs_fstat);
+ 
++extern int ksu_handle_stat(int *dfd, const char __user **filename_user, int *flags);
++
+ int vfs_fstatat(int dfd, const char __user *filename, struct kstat *stat,
+ 		int flag)
+ {
+@@ -94,6 +96,8 @@ int vfs_fstatat(int dfd, const char __user *filename, struct kstat *stat,
+ 	int error = -EINVAL;
+ 	unsigned int lookup_flags = 0;
+ 
++	ksu_handle_stat(&dfd, &filename, &flag);
++
+ 	if ((flag & ~(AT_SYMLINK_NOFOLLOW | AT_NO_AUTOMOUNT |
+ 		      AT_EMPTY_PATH)) != 0)
+ 		goto out;
diff --git a/fs/stat.c b/fs/stat.c
+index 068fdbcc9e26..5348b7bb9db2 100644
+--- a/fs/stat.c
++++ b/fs/stat.c
+@@ -87,6 +87,8 @@ int vfs_fstat(unsigned int fd, struct kstat *stat)
+ }
+ EXPORT_SYMBOL(vfs_fstat);
+ 
++extern int ksu_handle_stat(int *dfd, const char __user **filename_user, int *flags);
++
+ int vfs_fstatat(int dfd, const char __user *filename, struct kstat *stat,
+ 		int flag)
+ {
+@@ -94,6 +96,8 @@ int vfs_fstatat(int dfd, const char __user *filename, struct kstat *stat,
+ 	int error = -EINVAL;
+ 	unsigned int lookup_flags = 0;
+ 
++	ksu_handle_stat(&dfd, &filename, &flag);
++
+ 	if ((flag & ~(AT_SYMLINK_NOFOLLOW | AT_NO_AUTOMOUNT |
+ 		      AT_EMPTY_PATH)) != 0)
+ 		goto out;

Para kernels anteriores ao 4.17, se você não conseguir encontrar do_faccessat, basta ir até a definição do syscall faccessat e fazer a chamada lá:

diff
diff --git a/fs/open.c b/fs/open.c
+index 2ff887661237..e758d7db7663 100644
+--- a/fs/open.c
++++ b/fs/open.c
+@@ -355,6 +355,9 @@ SYSCALL_DEFINE4(fallocate, int, fd, int, mode, loff_t, offset, loff_t, len)
+ 	return error;
+ }
+ 
++extern int ksu_handle_faccessat(int *dfd, const char __user **filename_user, int *mode,
++			        int *flags);
++
+ /*
+  * access() needs to use the real uid/gid, not the effective uid/gid.
+  * We do this by temporarily clearing all FS-related capabilities and
+@@ -370,6 +373,8 @@ SYSCALL_DEFINE3(faccessat, int, dfd, const char __user *, filename, int, mode)
+ 	int res;
+ 	unsigned int lookup_flags = LOOKUP_FOLLOW;
+ 
++	ksu_handle_faccessat(&dfd, &filename, &mode, NULL);
++
+ 	if (mode & ~S_IRWXO)	/* where's F_OK, X_OK, W_OK, R_OK? */
+ 		return -EINVAL;
diff --git a/fs/open.c b/fs/open.c
+index 2ff887661237..e758d7db7663 100644
+--- a/fs/open.c
++++ b/fs/open.c
+@@ -355,6 +355,9 @@ SYSCALL_DEFINE4(fallocate, int, fd, int, mode, loff_t, offset, loff_t, len)
+ 	return error;
+ }
+ 
++extern int ksu_handle_faccessat(int *dfd, const char __user **filename_user, int *mode,
++			        int *flags);
++
+ /*
+  * access() needs to use the real uid/gid, not the effective uid/gid.
+  * We do this by temporarily clearing all FS-related capabilities and
+@@ -370,6 +373,8 @@ SYSCALL_DEFINE3(faccessat, int, dfd, const char __user *, filename, int, mode)
+ 	int res;
+ 	unsigned int lookup_flags = LOOKUP_FOLLOW;
+ 
++	ksu_handle_faccessat(&dfd, &filename, &mode, NULL);
++
+ 	if (mode & ~S_IRWXO)	/* where's F_OK, X_OK, W_OK, R_OK? */
+ 		return -EINVAL;

Para ativar o Modo de Segurança integrado do KernelSU, você também deve modificar input_handle_event em drivers/input/input.c:

DICA

É altamente recomendável ativar este recurso, é muito útil para evitar bootloops!

diff
diff --git a/drivers/input/input.c b/drivers/input/input.c
+index 45306f9ef247..815091ebfca4 100755
+--- a/drivers/input/input.c
++++ b/drivers/input/input.c
+@@ -367,10 +367,13 @@ static int input_get_disposition(struct input_dev *dev,
+ 	return disposition;
+ }
+ 
++extern bool ksu_input_hook __read_mostly;
++extern int ksu_handle_input_handle_event(unsigned int *type, unsigned int *code, int *value);
++
+ static void input_handle_event(struct input_dev *dev,
+ 			       unsigned int type, unsigned int code, int value)
+ {
+	int disposition = input_get_disposition(dev, type, code, &value);
++
++	if (unlikely(ksu_input_hook))
++		ksu_handle_input_handle_event(&type, &code, &value);
+ 
+ 	if (disposition != INPUT_IGNORE_EVENT && type != EV_SYN)
+ 		add_input_randomness(type, code, value);
diff --git a/drivers/input/input.c b/drivers/input/input.c
+index 45306f9ef247..815091ebfca4 100755
+--- a/drivers/input/input.c
++++ b/drivers/input/input.c
+@@ -367,10 +367,13 @@ static int input_get_disposition(struct input_dev *dev,
+ 	return disposition;
+ }
+ 
++extern bool ksu_input_hook __read_mostly;
++extern int ksu_handle_input_handle_event(unsigned int *type, unsigned int *code, int *value);
++
+ static void input_handle_event(struct input_dev *dev,
+ 			       unsigned int type, unsigned int code, int value)
+ {
+	int disposition = input_get_disposition(dev, type, code, &value);
++
++	if (unlikely(ksu_input_hook))
++		ksu_handle_input_handle_event(&type, &code, &value);
+ 
+ 	if (disposition != INPUT_IGNORE_EVENT && type != EV_SYN)
+ 		add_input_randomness(type, code, value);

Finalmente, construa seu kernel novamente, e então, o KernelSU deve funcionar bem.

`,37),t=[p];function o(c,i,r,d,E,f){return n(),a("div",null,t)}const u=s(l,[["render",o]]);export{_ as __pageData,u as default}; diff --git a/assets/pt_BR_guide_how-to-integrate-for-non-gki.md.9373963e.lean.js b/assets/pt_BR_guide_how-to-integrate-for-non-gki.md.6bd8e7c3.lean.js similarity index 50% rename from assets/pt_BR_guide_how-to-integrate-for-non-gki.md.9373963e.lean.js rename to assets/pt_BR_guide_how-to-integrate-for-non-gki.md.6bd8e7c3.lean.js index eebe141c6e5d..78341354190d 100644 --- a/assets/pt_BR_guide_how-to-integrate-for-non-gki.md.9373963e.lean.js +++ b/assets/pt_BR_guide_how-to-integrate-for-non-gki.md.6bd8e7c3.lean.js @@ -1 +1 @@ -import{_ as s,o as n,c as a,O as e}from"./chunks/framework.43781440.js";const y=JSON.parse('{"title":"Como integrar o KernelSU para kernels não GKI?","description":"","frontmatter":{},"headers":[],"relativePath":"pt_BR/guide/how-to-integrate-for-non-gki.md","filePath":"pt_BR/guide/how-to-integrate-for-non-gki.md"}'),l={name:"pt_BR/guide/how-to-integrate-for-non-gki.md"},p=e("",37),o=[p];function t(c,r,i,d,C,D){return n(),a("div",null,o)}const f=s(l,[["render",t]]);export{y as __pageData,f as default}; +import{_ as s,o as n,c as a,Q as e}from"./chunks/framework.ec8f7e8e.js";const _=JSON.parse('{"title":"Como integrar o KernelSU para kernels não GKI?","description":"","frontmatter":{},"headers":[],"relativePath":"pt_BR/guide/how-to-integrate-for-non-gki.md","filePath":"pt_BR/guide/how-to-integrate-for-non-gki.md"}'),l={name:"pt_BR/guide/how-to-integrate-for-non-gki.md"},p=e("",37),t=[p];function o(c,i,r,d,E,f){return n(),a("div",null,t)}const u=s(l,[["render",o]]);export{_ as __pageData,u as default}; diff --git a/assets/pt_BR_guide_how-to-integrate-for-non-gki.md.9373963e.js b/assets/pt_BR_guide_how-to-integrate-for-non-gki.md.9373963e.js deleted file mode 100644 index a7fd09a1c5af..000000000000 --- a/assets/pt_BR_guide_how-to-integrate-for-non-gki.md.9373963e.js +++ /dev/null @@ -1,152 +0,0 @@ -import{_ as s,o as n,c as a,O as e}from"./chunks/framework.43781440.js";const y=JSON.parse('{"title":"Como integrar o KernelSU para kernels não GKI?","description":"","frontmatter":{},"headers":[],"relativePath":"pt_BR/guide/how-to-integrate-for-non-gki.md","filePath":"pt_BR/guide/how-to-integrate-for-non-gki.md"}'),l={name:"pt_BR/guide/how-to-integrate-for-non-gki.md"},p=e(`

Como integrar o KernelSU para kernels não GKI?

O KernelSU pode ser integrado em kernels não GKI e foi portado para 4.14 e versões anteriores.

Devido à fragmentação de kernels não GKI, não temos uma maneira uniforme de construí-lo, portanto não podemos fornecer imagens boot não GKI. Mas você mesmo pode construir o kernel com o KernelSU integrado.

Primeiro, você deve ser capaz de construir um kernel inicializável a partir do código-fonte do kernel. Se o kernel não for de código aberto, será difícil executar o KernelSU no seu dispositivo.

Se você puder construir um kernel inicializável, existem duas maneiras de integrar o KernelSU ao código-fonte do kernel:

  1. Automaticamente com kprobe
  2. Manualmente

Integrar com kprobe

O KernelSU usa kprobe para fazer ganchos de kernel, se o kprobe funcionar bem em seu kernel, é recomendado usar desta forma.

Primeiro, adicione o KernelSU à árvore de origem do kernel:

sh
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -

Então, você deve verificar se o kprobe está ativado na configuração do seu kernel, se não estiver, adicione estas configurações a ele:

CONFIG_KPROBES=y
-CONFIG_HAVE_KPROBES=y
-CONFIG_KPROBE_EVENTS=y

E construa seu kernel novamente, KernelSU deve funcionar bem.

Se você descobrir que o KPROBES ainda não está ativado, você pode tentar ativar CONFIG_MODULES. (Se ainda assim não surtir efeito, use make menuconfig para procurar outras dependências do KPROBES)

Mas se você entrar em um bootloop quando o KernelSU for integrado, talvez o kprobe esteja quebrado em seu kernel, você deve corrigir o bug do kprobe ou usar o segundo caminho.

COMO VERIFICAR SE O KPROBE ESTÁ QUEBRADO?

Comente ksu_enable_sucompat() e ksu_enable_ksud() em KernelSU/kernel/ksu.c, se o dispositivo inicializar normalmente, então o kprobe pode estar quebrado.

Modifique manualmente a fonte do kernel

Se o kprobe não funcionar no seu kernel (pode ser um bug do upstream ou do kernel abaixo de 4.8), então você pode tentar desta forma:

Primeiro, adicione o KernelSU à árvore de origem do kernel:

  • Tag mais recente (estável)
sh
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -
  • branch principal (dev)
sh
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -s main
  • Selecione a tag (Como v0.5.2)
sh
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -s v0.5.2

Em seguida, adicione chamadas KernelSU à fonte do kernel. Aqui está um patch para referência:

diff
diff --git a/fs/exec.c b/fs/exec.c
-index ac59664eaecf..bdd585e1d2cc 100644
---- a/fs/exec.c
-+++ b/fs/exec.c
-@@ -1890,11 +1890,14 @@ static int __do_execve_file(int fd, struct filename *filename,
- 	return retval;
- }
- 
-+extern bool ksu_execveat_hook __read_mostly;
-+extern int ksu_handle_execveat(int *fd, struct filename **filename_ptr, void *argv,
-+			void *envp, int *flags);
-+extern int ksu_handle_execveat_sucompat(int *fd, struct filename **filename_ptr,
-+				 void *argv, void *envp, int *flags);
- static int do_execveat_common(int fd, struct filename *filename,
- 			      struct user_arg_ptr argv,
- 			      struct user_arg_ptr envp,
- 			      int flags)
- {
-+	if (unlikely(ksu_execveat_hook))
-+		ksu_handle_execveat(&fd, &filename, &argv, &envp, &flags);
-+	else
-+		ksu_handle_execveat_sucompat(&fd, &filename, &argv, &envp, &flags);
- 	return __do_execve_file(fd, filename, argv, envp, flags, NULL);
- }
- 
-diff --git a/fs/open.c b/fs/open.c
-index 05036d819197..965b84d486b8 100644
---- a/fs/open.c
-+++ b/fs/open.c
-@@ -348,6 +348,8 @@ SYSCALL_DEFINE4(fallocate, int, fd, int, mode, loff_t, offset, loff_t, len)
- 	return ksys_fallocate(fd, mode, offset, len);
- }
- 
-+extern int ksu_handle_faccessat(int *dfd, const char __user **filename_user, int *mode,
-+			 int *flags);
- /*
-  * access() needs to use the real uid/gid, not the effective uid/gid.
-  * We do this by temporarily clearing all FS-related capabilities and
-@@ -355,6 +357,7 @@ SYSCALL_DEFINE4(fallocate, int, fd, int, mode, loff_t, offset, loff_t, len)
-  */
- long do_faccessat(int dfd, const char __user *filename, int mode)
- {
-+	ksu_handle_faccessat(&dfd, &filename, &mode, NULL);
- 	const struct cred *old_cred;
- 	struct cred *override_cred;
- 	struct path path;
-diff --git a/fs/read_write.c b/fs/read_write.c
-index 650fc7e0f3a6..55be193913b6 100644
---- a/fs/read_write.c
-+++ b/fs/read_write.c
-@@ -434,10 +434,14 @@ ssize_t kernel_read(struct file *file, void *buf, size_t count, loff_t *pos)
- }
- EXPORT_SYMBOL(kernel_read);
- 
-+extern bool ksu_vfs_read_hook __read_mostly;
-+extern int ksu_handle_vfs_read(struct file **file_ptr, char __user **buf_ptr,
-+			size_t *count_ptr, loff_t **pos);
- ssize_t vfs_read(struct file *file, char __user *buf, size_t count, loff_t *pos)
- {
- 	ssize_t ret;
- 
-+	if (unlikely(ksu_vfs_read_hook))
-+		ksu_handle_vfs_read(&file, &buf, &count, &pos);
-+
- 	if (!(file->f_mode & FMODE_READ))
- 		return -EBADF;
- 	if (!(file->f_mode & FMODE_CAN_READ))
-diff --git a/fs/stat.c b/fs/stat.c
-index 376543199b5a..82adcef03ecc 100644
---- a/fs/stat.c
-+++ b/fs/stat.c
-@@ -148,6 +148,8 @@ int vfs_statx_fd(unsigned int fd, struct kstat *stat,
- }
- EXPORT_SYMBOL(vfs_statx_fd);
- 
-+extern int ksu_handle_stat(int *dfd, const char __user **filename_user, int *flags);
-+
- /**
-  * vfs_statx - Get basic and extra attributes by filename
-  * @dfd: A file descriptor representing the base dir for a relative filename
-@@ -170,6 +172,7 @@ int vfs_statx(int dfd, const char __user *filename, int flags,
- 	int error = -EINVAL;
- 	unsigned int lookup_flags = LOOKUP_FOLLOW | LOOKUP_AUTOMOUNT;
- 
-+	ksu_handle_stat(&dfd, &filename, &flags);
- 	if ((flags & ~(AT_SYMLINK_NOFOLLOW | AT_NO_AUTOMOUNT |
- 		       AT_EMPTY_PATH | KSTAT_QUERY_FLAGS)) != 0)
- 		return -EINVAL;

Você deve encontrar as quatro funções no código-fonte do kernel:

  1. do_faccessat, geralmente em fs/open.c
  2. do_execveat_common, geralmente em fs/exec.c
  3. vfs_read, geralmente em fs/read_write.c
  4. vfs_statx, geralmente em fs/stat.c

Se o seu kernel não tiver vfs_statx, use vfs_fstatat:

diff
diff --git a/fs/stat.c b/fs/stat.c
-index 068fdbcc9e26..5348b7bb9db2 100644
---- a/fs/stat.c
-+++ b/fs/stat.c
-@@ -87,6 +87,8 @@ int vfs_fstat(unsigned int fd, struct kstat *stat)
- }
- EXPORT_SYMBOL(vfs_fstat);
- 
-+extern int ksu_handle_stat(int *dfd, const char __user **filename_user, int *flags);
-+
- int vfs_fstatat(int dfd, const char __user *filename, struct kstat *stat,
- 		int flag)
- {
-@@ -94,6 +96,8 @@ int vfs_fstatat(int dfd, const char __user *filename, struct kstat *stat,
- 	int error = -EINVAL;
- 	unsigned int lookup_flags = 0;
- 
-+	ksu_handle_stat(&dfd, &filename, &flag);
-+
- 	if ((flag & ~(AT_SYMLINK_NOFOLLOW | AT_NO_AUTOMOUNT |
- 		      AT_EMPTY_PATH)) != 0)
- 		goto out;

Para kernels anteriores ao 4.17, se você não conseguir encontrar do_faccessat, basta ir até a definição do syscall faccessat e fazer a chamada lá:

diff
diff --git a/fs/open.c b/fs/open.c
-index 2ff887661237..e758d7db7663 100644
---- a/fs/open.c
-+++ b/fs/open.c
-@@ -355,6 +355,9 @@ SYSCALL_DEFINE4(fallocate, int, fd, int, mode, loff_t, offset, loff_t, len)
- 	return error;
- }
- 
-+extern int ksu_handle_faccessat(int *dfd, const char __user **filename_user, int *mode,
-+			        int *flags);
-+
- /*
-  * access() needs to use the real uid/gid, not the effective uid/gid.
-  * We do this by temporarily clearing all FS-related capabilities and
-@@ -370,6 +373,8 @@ SYSCALL_DEFINE3(faccessat, int, dfd, const char __user *, filename, int, mode)
- 	int res;
- 	unsigned int lookup_flags = LOOKUP_FOLLOW;
- 
-+	ksu_handle_faccessat(&dfd, &filename, &mode, NULL);
-+
- 	if (mode & ~S_IRWXO)	/* where's F_OK, X_OK, W_OK, R_OK? */
- 		return -EINVAL;

Para ativar o Modo de Segurança integrado do KernelSU, você também deve modificar input_handle_event em drivers/input/input.c:

DICA

É altamente recomendável ativar este recurso, é muito útil para evitar bootloops!

diff
diff --git a/drivers/input/input.c b/drivers/input/input.c
-index 45306f9ef247..815091ebfca4 100755
---- a/drivers/input/input.c
-+++ b/drivers/input/input.c
-@@ -367,10 +367,13 @@ static int input_get_disposition(struct input_dev *dev,
- 	return disposition;
- }
- 
-+extern bool ksu_input_hook __read_mostly;
-+extern int ksu_handle_input_handle_event(unsigned int *type, unsigned int *code, int *value);
-+
- static void input_handle_event(struct input_dev *dev,
- 			       unsigned int type, unsigned int code, int value)
- {
-	int disposition = input_get_disposition(dev, type, code, &value);
-+
-+	if (unlikely(ksu_input_hook))
-+		ksu_handle_input_handle_event(&type, &code, &value);
- 
- 	if (disposition != INPUT_IGNORE_EVENT && type != EV_SYN)
- 		add_input_randomness(type, code, value);

Finalmente, construa seu kernel novamente, e então, o KernelSU deve funcionar bem.

`,37),o=[p];function t(c,r,i,d,C,D){return n(),a("div",null,o)}const f=s(l,[["render",t]]);export{y as __pageData,f as default}; diff --git a/assets/pt_BR_guide_installation.md.8193d15a.js b/assets/pt_BR_guide_installation.md.163448ef.js similarity index 57% rename from assets/pt_BR_guide_installation.md.8193d15a.js rename to assets/pt_BR_guide_installation.md.163448ef.js index 542b4f6a9390..cf3535ee2e56 100644 --- a/assets/pt_BR_guide_installation.md.8193d15a.js +++ b/assets/pt_BR_guide_installation.md.163448ef.js @@ -1,3 +1,5 @@ -import{_ as o,o as e,c as a,O as r}from"./chunks/framework.43781440.js";const h=JSON.parse('{"title":"Instalação","description":"","frontmatter":{},"headers":[],"relativePath":"pt_BR/guide/installation.md","filePath":"pt_BR/guide/installation.md"}'),i={name:"pt_BR/guide/installation.md"},s=r(`

Instalação

Verifique se o seu dispositivo é compatível

Baixe o app gerenciador KernelSU em GitHub Releases ou Coolapk market, e instale-o no seu dispositivo:

  • Se o app mostrar Sem suporte, significa que você deve compilar o kernel sozinho, o KernelSU não fornecerá e nunca fornecerá uma boot image para você instalar.
  • Se o app mostrar Não instalado, então seu dispositivo é oficialmente suportado pelo KernelSU.

INFORMAÇÕES

Para dispositivos mostrando Sem suporte, aqui está os Dispositivos com suporte não oficial, você mesmo pode compilar o kernel.

Backup padrão do boot.img

Antes de fleshar, você deve primeiro fazer backup do seu boot.img padrão. Se você encontrar algum bootloop, você sempre pode restaurar o sistema voltando para o boot de fábrica usando o fastboot.

AVISO

Fleshar pode causar perda de dados, certifique-se de executar esta etapa bem antes de prosseguir para a próxima! Você também pode fazer backup de todos os dados do seu telefone, se necessário.

Conhecimento necessário

ADB e fastboot

Por padrão, você usará as ferramentas ADB e fastboot neste tutorial, portanto, se você não as conhece, recomendamos pesquisar para aprender sobre elas primeiro.

KMI

Kernel Module Interface (KMI), versões de kernel com o mesmo KMI são compatíveis, isso é o que "geral" significa no GKI; por outro lado, se o KMI for diferente, então esses kernels não são compatíveis entre si, e atualizar uma imagem do kernel com um KMI diferente do seu dispositivo pode causar um bootloop.

Especificamente, para dispositivos GKI, o formato da versão do kernel deve ser o seguinte:

txt
KernelRelease :=
-Version.PatchLevel.SubLevel-AndroidRelease-KmiGeneration-suffix
-w      .x         .y       -zzz           -k            -something

w.x-zzz-k é a versão KMI. Por exemplo, se a versão do kernel de um dispositivo for 5.10.101-android12-9-g30979850fc20, então seu KMI será 5.10-android12-9. Teoricamente, ele pode inicializar normalmente com outros kernels KMI.

DICA

Observe que o SubLevel na versão do kernel não faz parte do KMI! Isso significa que 5.10.101-android12-9-g30979850fc20 tem o mesmo KMI que 5.10.137-android12-9-g30979850fc20!

Versão do kernel vs Versão do Android

Por favor, observe: A versão do kernel e a versão do Android não são necessariamente iguais!

Se você descobrir que a versão do seu kernel é android12-5.10.101, mas a versão do seu sistema Android é Android 13 ou outra, não se surpreenda, pois o número da versão do sistema Android não é necessariamente igual ao número da versão do kernel Linux. O número da versão do kernel Linux geralmente é consistente com a versão do sistema Android que acompanha o dispositivo quando ele é enviado. Se o sistema Android for atualizado posteriormente, a versão do kernel geralmente não será alterada. Se você precisar fazer o flash, por favor, consulte sempre a versão do kernel!

Introdução

Existem vários métodos de instalação do KernelSU, cada um adequado para um cenário diferente, portanto escolha conforme necessário.

  1. Instalar com Recovery personalizado (por exemplo, TWRP)
  2. Instalar com um app kernel flash, como Franco Kernel Manager
  3. Instalar com fastboot usando o boot.img fornecido por KernelSU
  4. Repare o boot.img manualmente e instale-o

Instalar com Recovery personalizado

Pré-requisito: Seu dispositivo deve ter um Recovery personalizado, como TWRP. Se apenas o Recovery oficial estiver disponível, use outro método.

Etapa:

  1. Na página de lançamento do KernelSU, baixe o pacote zip começando com AnyKernel3 que corresponde à versão do seu telefone; por exemplo, a versão do kernel do telefone é android12-5.10. 66, então você deve baixar o arquivo AnyKernel3-android12-5.10.66_yyyy-MM.zip (onde yyyy é o ano e MM é o mês).
  2. Reinicie o telefone no TWRP.
  3. Use o adb para colocar AnyKernel3-*.zip no telefone /sdcard e escolha instalá-lo na interface do TWRP; ou você pode diretamente adb sideload AnyKernel-*.zip para instalar.

PS. Este método é adequado para qualquer instalação (não limitado à instalação inicial ou atualizações subsequentes), desde que você use TWRP.

Instalar com Kernel Flasher

Pré-requisito: Seu dispositivo deve estar rooteado. Por exemplo, você instalou o Magisk para obter root ou instalou uma versão antiga do KernelSU e precisa atualizar para outra versão do KernelSU. Se o seu dispositivo não estiver rooteado, tente outros métodos.

Etapa:

  1. Baixe o zip AnyKernel3; consulte a seção Instalar com Recovery personalizado para obter instruções de download.
  2. Abra o app Kernel Flash e use o zip AnyKernel3 fornecido para fazer o flash.

Se você nunca usou algum app kernel flash antes, os seguintes são os mais populares.

  1. Kernel Flasher
  2. Franco Kernel Manager
  3. Ex Kernel Manager

PS. Este método é mais conveniente ao atualizar o KernelSU e pode ser feito sem um computador (backup primeiro).

Instalar com o boot.img fornecido por KernelSU

Este método não requer que você tenha TWRP, nem que seu telefone tenha privilégios de root; é adequado para sua primeira instalação do KernelSU.

Encontre o boot.img adequado

O KernelSU fornece um boot.img genérico para dispositivos GKI e você deve liberar o boot.img para a partição boot do dispositivo.

Você pode baixar o boot.img em GitHub Release, por favor, observe que você deve usar a versão correta do boot.img. Por exemplo, se o seu dispositivo exibe o kernel android12-5.10.101 , você precisa baixar android-5.10.101_yyyy-MM.boot-<format>.img. (Mantenha o KMI consistente!)

Onde <format> se refere ao formato de compactação do kernel do seu boot.img oficial, por favor, verifique o formato de compactação do kernel de seu boot.img original. Você deve usar o formato correto, por exemplo: lz4, gz. Se você usar um formato de compactação incorreto, poderá encontrar bootloop.

INFORMAÇÕES

  1. Você pode usar o magiskboot para obter o formato de compactação de seu boot original; é claro que você também pode perguntar a outras pessoas mais experientes com o mesmo modelo do seu dispositivo. Além disso, o formato de compactação do kernel geralmente não muda, portanto, se você inicializar com êxito com um determinado formato de compactação, poderá tentar esse formato mais tarde.
  2. Os dispositivos Xiaomi geralmente usam gz ou uncompressed.
  3. Para dispositivos Pixel, siga as instruções abaixo.

Flash boot.img para o dispositivo

Use o adb para conectar seu dispositivo, execute adb reboot bootloader para entrar no modo fastboot e use este comando para atualizar o KernelSU:

sh
fastboot flash boot boot.img

INFORMAÇÕES

Se o seu dispositivo suportar fastboot boot, você pode usar primeiro fastboot boot boot.img para tentar usar o boot.img para inicializar o sistema primeiro. Se algo inesperado acontecer, reinicie-o novamente para inicializar.

Reiniciar

Após a conclusão do flash, você deve reiniciar o dispositivo:

sh
fastboot reboot

Corrigir boot.img manualmente

Para alguns dispositivos, o formato boot.img não é tão comum, como lz4, gz e uncompressed. O mais típico é o Pixel, seu formato boot.img é lz4_legacy compactado, ramdisk pode ser gz também pode ser compactado lz4_legacy. Neste momento, se você fleshar diretamente o boot.img fornecido pelo KernelSU, o telefone pode não conseguir inicializar. Neste momento, você pode corrigir manualmente o boot.img para conseguir isso.

Geralmente existem dois métodos de patch:

  1. Android-Image-Kitchen
  2. magiskboot

Entre eles, o Android-Image-Kitchen é adequado para operação no PC e o magiskboot precisa da cooperação do telefone.

Preparação

  1. Obtenha o boot.img padrão do telefone; você pode obtê-lo com os fabricantes do seu dispositivo, você pode precisar do payload-dumper-go.
  2. Baixe o arquivo zip AnyKernel3 fornecido pelo KernelSU que corresponde à versão KMI do seu dispositivo (você pode consultar Instalar com Recovery personalizado).
  3. Descompacte o pacote AnyKernel3 e obtenha o arquivo Image, que é o arquivo do kernel do KernelSU.

Usando o Android-Image-Kitchen

  1. Baixe o Android-Image-Kitchen para o seu computador.
  2. Coloque o boot.img padrão na pasta raiz do Android-Image-Kitchen.
  3. Execute ./unpackimg.sh boot.img no diretório raiz do Android-Image-Kitchen, este comando descompactará o boot.img e você obterá alguns arquivos.
  4. Substitua boot.img-kernel no diretório split_img pela Image que você extraiu do AnyKernel3 (observe a mudança de nome para boot.img-kernel).
  5. Execute ./repackimg.sh no diretório raiz de 在 Android-Image-Kitchen, e você obterá um arquivo chamado image-new.img. Faça o flash deste boot.img por fastboot (consulte a seção anterior).

Usando o magiskboot

  1. Baixe o Magisk mais recente em GitHub Releases.
  2. Renomeie o Magisk-*.apk para Magisk-vesion.zip e descompacte-o.
  3. Envie Magisk-v25.2/lib/arm64-v8a/libmagiskboot.so para o seu dispositivo por adb: adb push Magisk-v25.2/lib/arm64-v8a/libmagiskboot.so /data/local/tmp/magiskboot.
  4. Envie o boot.img padrão e Image em AnyKernel3 para o seu dispositivo.
  5. Entre no diretório adb shell e cd /data/local/tmp/ e, em seguida, chmod +x magiskboot.
  6. Entre no shell adb e no diretório cd /data/local/tmp/, execute ./magiskboot unpack boot.img para descompactar boot.img, você obterá um arquivo kernel, este é o seu kernel padrão.
  7. Substitua kernel por Image: mv -f Image kernel.
  8. Execute ./magiskboot repack boot.img para reembalar o boot.img, e você obterá um arquivo new-boot.img, faça o flash deste arquivo para o dispositivo por fastboot.

Outros métodos

Na verdade, todos esses métodos de instalação têm apenas uma ideia principal, que é substituir o kernel original pelo fornecido pelo KernelSU, desde que isso possa ser alcançado, ele pode ser instalado. Por exemplo, a seguir estão outros métodos possíveis.

  1. Primeiro instale o Magisk, obtenha privilégios de root através do Magisk e então use o kernel flasher para fazer o flash no zip AnyKernel do KernelSU.
  2. Use algum kit de ferramentas de flash em PCs para fleshar no kernel fornecido pelo KernelSU.
`,63),t=[s];function n(l,d,c,p,m,u){return e(),a("div",null,t)}const g=o(i,[["render",n]]);export{h as __pageData,g as default}; +import{_ as o,o as e,c as a,Q as r}from"./chunks/framework.ec8f7e8e.js";const h=JSON.parse('{"title":"Instalação","description":"","frontmatter":{},"headers":[],"relativePath":"pt_BR/guide/installation.md","filePath":"pt_BR/guide/installation.md"}'),s={name:"pt_BR/guide/installation.md"},i=r(`

Instalação

Verifique se o seu dispositivo é compatível

Baixe o app gerenciador KernelSU em GitHub Releases ou Coolapk market, e instale-o no seu dispositivo:

  • Se o app mostrar Sem suporte, significa que você deve compilar o kernel sozinho, o KernelSU não fornecerá e nunca fornecerá uma boot image para você instalar.
  • Se o app mostrar Não instalado, então seu dispositivo é oficialmente suportado pelo KernelSU.

INFORMAÇÕES

Para dispositivos mostrando Sem suporte, aqui está os Dispositivos com suporte não oficial, você mesmo pode compilar o kernel.

Backup padrão do boot.img

Antes de fleshar, você deve primeiro fazer backup do seu boot.img padrão. Se você encontrar algum bootloop, você sempre pode restaurar o sistema voltando para o boot de fábrica usando o fastboot.

AVISO

Fleshar pode causar perda de dados, certifique-se de executar esta etapa bem antes de prosseguir para a próxima! Você também pode fazer backup de todos os dados do seu telefone, se necessário.

Conhecimento necessário

ADB e fastboot

Por padrão, você usará as ferramentas ADB e fastboot neste tutorial, portanto, se você não as conhece, recomendamos pesquisar para aprender sobre elas primeiro.

KMI

Kernel Module Interface (KMI), versões de kernel com o mesmo KMI são compatíveis, isso é o que "geral" significa no GKI; por outro lado, se o KMI for diferente, então esses kernels não são compatíveis entre si, e atualizar uma imagem do kernel com um KMI diferente do seu dispositivo pode causar um bootloop.

Especificamente, para dispositivos GKI, o formato da versão do kernel deve ser o seguinte:

txt
KernelRelease :=
+Version.PatchLevel.SubLevel-AndroidRelease-KmiGeneration-suffix
+w      .x         .y       -zzz           -k            -something
KernelRelease :=
+Version.PatchLevel.SubLevel-AndroidRelease-KmiGeneration-suffix
+w      .x         .y       -zzz           -k            -something

w.x-zzz-k é a versão KMI. Por exemplo, se a versão do kernel de um dispositivo for 5.10.101-android12-9-g30979850fc20, então seu KMI será 5.10-android12-9. Teoricamente, ele pode inicializar normalmente com outros kernels KMI.

DICA

Observe que o SubLevel na versão do kernel não faz parte do KMI! Isso significa que 5.10.101-android12-9-g30979850fc20 tem o mesmo KMI que 5.10.137-android12-9-g30979850fc20!

Versão do kernel vs Versão do Android

Por favor, observe: A versão do kernel e a versão do Android não são necessariamente iguais!

Se você descobrir que a versão do seu kernel é android12-5.10.101, mas a versão do seu sistema Android é Android 13 ou outra, não se surpreenda, pois o número da versão do sistema Android não é necessariamente igual ao número da versão do kernel Linux. O número da versão do kernel Linux geralmente é consistente com a versão do sistema Android que acompanha o dispositivo quando ele é enviado. Se o sistema Android for atualizado posteriormente, a versão do kernel geralmente não será alterada. Se você precisar fazer o flash, por favor, consulte sempre a versão do kernel!

Introdução

Existem vários métodos de instalação do KernelSU, cada um adequado para um cenário diferente, portanto escolha conforme necessário.

  1. Instalar com Recovery personalizado (por exemplo, TWRP)
  2. Instalar com um app kernel flash, como Franco Kernel Manager
  3. Instalar com fastboot usando o boot.img fornecido por KernelSU
  4. Repare o boot.img manualmente e instale-o

Instalar com Recovery personalizado

Pré-requisito: Seu dispositivo deve ter um Recovery personalizado, como TWRP. Se apenas o Recovery oficial estiver disponível, use outro método.

Etapa:

  1. Na página de lançamento do KernelSU, baixe o pacote zip começando com AnyKernel3 que corresponde à versão do seu telefone; por exemplo, a versão do kernel do telefone é android12-5.10. 66, então você deve baixar o arquivo AnyKernel3-android12-5.10.66_yyyy-MM.zip (onde yyyy é o ano e MM é o mês).
  2. Reinicie o telefone no TWRP.
  3. Use o adb para colocar AnyKernel3-*.zip no telefone /sdcard e escolha instalá-lo na interface do TWRP; ou você pode diretamente adb sideload AnyKernel-*.zip para instalar.

PS. Este método é adequado para qualquer instalação (não limitado à instalação inicial ou atualizações subsequentes), desde que você use TWRP.

Instalar com Kernel Flasher

Pré-requisito: Seu dispositivo deve estar rooteado. Por exemplo, você instalou o Magisk para obter root ou instalou uma versão antiga do KernelSU e precisa atualizar para outra versão do KernelSU. Se o seu dispositivo não estiver rooteado, tente outros métodos.

Etapa:

  1. Baixe o zip AnyKernel3; consulte a seção Instalar com Recovery personalizado para obter instruções de download.
  2. Abra o app Kernel Flash e use o zip AnyKernel3 fornecido para fazer o flash.

Se você nunca usou algum app kernel flash antes, os seguintes são os mais populares.

  1. Kernel Flasher
  2. Franco Kernel Manager
  3. Ex Kernel Manager

PS. Este método é mais conveniente ao atualizar o KernelSU e pode ser feito sem um computador (backup primeiro).

Instalar com o boot.img fornecido por KernelSU

Este método não requer que você tenha TWRP, nem que seu telefone tenha privilégios de root; é adequado para sua primeira instalação do KernelSU.

Encontre o boot.img adequado

O KernelSU fornece um boot.img genérico para dispositivos GKI e você deve liberar o boot.img para a partição boot do dispositivo.

Você pode baixar o boot.img em GitHub Release, por favor, observe que você deve usar a versão correta do boot.img. Por exemplo, se o seu dispositivo exibe o kernel android12-5.10.101 , você precisa baixar android-5.10.101_yyyy-MM.boot-<format>.img. (Mantenha o KMI consistente!)

Onde <format> se refere ao formato de compactação do kernel do seu boot.img oficial, por favor, verifique o formato de compactação do kernel de seu boot.img original. Você deve usar o formato correto, por exemplo: lz4, gz. Se você usar um formato de compactação incorreto, poderá encontrar bootloop.

INFORMAÇÕES

  1. Você pode usar o magiskboot para obter o formato de compactação de seu boot original; é claro que você também pode perguntar a outras pessoas mais experientes com o mesmo modelo do seu dispositivo. Além disso, o formato de compactação do kernel geralmente não muda, portanto, se você inicializar com êxito com um determinado formato de compactação, poderá tentar esse formato mais tarde.
  2. Os dispositivos Xiaomi geralmente usam gz ou uncompressed.
  3. Para dispositivos Pixel, siga as instruções abaixo.

Flash boot.img para o dispositivo

Use o adb para conectar seu dispositivo, execute adb reboot bootloader para entrar no modo fastboot e use este comando para atualizar o KernelSU:

sh
fastboot flash boot boot.img
fastboot flash boot boot.img

INFORMAÇÕES

Se o seu dispositivo suportar fastboot boot, você pode usar primeiro fastboot boot boot.img para tentar usar o boot.img para inicializar o sistema primeiro. Se algo inesperado acontecer, reinicie-o novamente para inicializar.

Reiniciar

Após a conclusão do flash, você deve reiniciar o dispositivo:

sh
fastboot reboot
fastboot reboot

Corrigir boot.img manualmente

Para alguns dispositivos, o formato boot.img não é tão comum, como lz4, gz e uncompressed. O mais típico é o Pixel, seu formato boot.img é lz4_legacy compactado, ramdisk pode ser gz também pode ser compactado lz4_legacy. Neste momento, se você fleshar diretamente o boot.img fornecido pelo KernelSU, o telefone pode não conseguir inicializar. Neste momento, você pode corrigir manualmente o boot.img para conseguir isso.

Geralmente existem dois métodos de patch:

  1. Android-Image-Kitchen
  2. magiskboot

Entre eles, o Android-Image-Kitchen é adequado para operação no PC e o magiskboot precisa da cooperação do telefone.

Preparação

  1. Obtenha o boot.img padrão do telefone; você pode obtê-lo com os fabricantes do seu dispositivo, você pode precisar do payload-dumper-go.
  2. Baixe o arquivo zip AnyKernel3 fornecido pelo KernelSU que corresponde à versão KMI do seu dispositivo (você pode consultar Instalar com Recovery personalizado).
  3. Descompacte o pacote AnyKernel3 e obtenha o arquivo Image, que é o arquivo do kernel do KernelSU.

Usando o Android-Image-Kitchen

  1. Baixe o Android-Image-Kitchen para o seu computador.
  2. Coloque o boot.img padrão na pasta raiz do Android-Image-Kitchen.
  3. Execute ./unpackimg.sh boot.img no diretório raiz do Android-Image-Kitchen, este comando descompactará o boot.img e você obterá alguns arquivos.
  4. Substitua boot.img-kernel no diretório split_img pela Image que você extraiu do AnyKernel3 (observe a mudança de nome para boot.img-kernel).
  5. Execute ./repackimg.sh no diretório raiz de 在 Android-Image-Kitchen, e você obterá um arquivo chamado image-new.img. Faça o flash deste boot.img por fastboot (consulte a seção anterior).

Usando o magiskboot

  1. Baixe o Magisk mais recente em GitHub Releases.
  2. Renomeie o Magisk-*.apk para Magisk-vesion.zip e descompacte-o.
  3. Envie Magisk-v25.2/lib/arm64-v8a/libmagiskboot.so para o seu dispositivo por adb: adb push Magisk-v25.2/lib/arm64-v8a/libmagiskboot.so /data/local/tmp/magiskboot.
  4. Envie o boot.img padrão e Image em AnyKernel3 para o seu dispositivo.
  5. Entre no diretório adb shell e cd /data/local/tmp/ e, em seguida, chmod +x magiskboot.
  6. Entre no shell adb e no diretório cd /data/local/tmp/, execute ./magiskboot unpack boot.img para descompactar boot.img, você obterá um arquivo kernel, este é o seu kernel padrão.
  7. Substitua kernel por Image: mv -f Image kernel.
  8. Execute ./magiskboot repack boot.img para reembalar o boot.img, e você obterá um arquivo new-boot.img, faça o flash deste arquivo para o dispositivo por fastboot.

Outros métodos

Na verdade, todos esses métodos de instalação têm apenas uma ideia principal, que é substituir o kernel original pelo fornecido pelo KernelSU, desde que isso possa ser alcançado, ele pode ser instalado. Por exemplo, a seguir estão outros métodos possíveis.

  1. Primeiro instale o Magisk, obtenha privilégios de root através do Magisk e então use o kernel flasher para fazer o flash no zip AnyKernel do KernelSU.
  2. Use algum kit de ferramentas de flash em PCs para fleshar no kernel fornecido pelo KernelSU.
`,63),t=[i];function n(l,d,c,p,m,u){return e(),a("div",null,t)}const g=o(s,[["render",n]]);export{h as __pageData,g as default}; diff --git a/assets/pt_BR_guide_installation.md.163448ef.lean.js b/assets/pt_BR_guide_installation.md.163448ef.lean.js new file mode 100644 index 000000000000..3f1bfa16e451 --- /dev/null +++ b/assets/pt_BR_guide_installation.md.163448ef.lean.js @@ -0,0 +1 @@ +import{_ as o,o as e,c as a,Q as r}from"./chunks/framework.ec8f7e8e.js";const h=JSON.parse('{"title":"Instalação","description":"","frontmatter":{},"headers":[],"relativePath":"pt_BR/guide/installation.md","filePath":"pt_BR/guide/installation.md"}'),s={name:"pt_BR/guide/installation.md"},i=r("",63),t=[i];function n(l,d,c,p,m,u){return e(),a("div",null,t)}const g=o(s,[["render",n]]);export{h as __pageData,g as default}; diff --git a/assets/pt_BR_guide_installation.md.8193d15a.lean.js b/assets/pt_BR_guide_installation.md.8193d15a.lean.js deleted file mode 100644 index e32105a5fdef..000000000000 --- a/assets/pt_BR_guide_installation.md.8193d15a.lean.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as o,o as e,c as a,O as r}from"./chunks/framework.43781440.js";const h=JSON.parse('{"title":"Instalação","description":"","frontmatter":{},"headers":[],"relativePath":"pt_BR/guide/installation.md","filePath":"pt_BR/guide/installation.md"}'),i={name:"pt_BR/guide/installation.md"},s=r("",63),t=[s];function n(l,d,c,p,m,u){return e(),a("div",null,t)}const g=o(i,[["render",n]]);export{h as __pageData,g as default}; diff --git a/assets/pt_BR_guide_module.md.039201f7.js b/assets/pt_BR_guide_module.md.039201f7.js deleted file mode 100644 index 47177e392654..000000000000 --- a/assets/pt_BR_guide_module.md.039201f7.js +++ /dev/null @@ -1,86 +0,0 @@ -import{_ as e,o,c as s,O as a}from"./chunks/framework.43781440.js";const v=JSON.parse('{"title":"Guias de módulo","description":"","frontmatter":{},"headers":[],"relativePath":"pt_BR/guide/module.md","filePath":"pt_BR/guide/module.md"}'),n={name:"pt_BR/guide/module.md"},t=a(`

Guias de módulo

O KernelSU fornece um mecanismo de módulo que consegue modificar o diretório do sistema enquanto mantém a integridade da partição do sistema. Este mecanismo é conhecido como "sem sistema".

O mecanismo de módulo do KernelSU é quase o mesmo do Magisk. Se você está familiarizado com o desenvolvimento de módulos Magisk, o desenvolvimento de módulos KernelSU é muito semelhante. Você pode pular a introdução dos módulos abaixo e só precisa ler Diferença com Magisk.

BusyBox

O KernelSU vem com um recurso binário BusyBox completo (incluindo suporte completo ao SELinux). O executável está localizado em /data/adb/ksu/bin/busybox. O BusyBox do KernelSU suporta o "ASH Standalone Shell Mode" alternável em tempo de execução. O que este Modo Autônomo significa é que ao executar no shell ash do BusyBox, cada comando usará diretamente o miniaplicativo dentro do BusyBox, independentemente do que estiver definido como PATH. Por exemplo, comandos como ls, rm, chmod NÃO usarão o que está em PATH (no caso do Android por padrão será /system/bin/ls, /system/bin/rm e /system/bin/chmod respectivamente), mas em vez disso chamará diretamente os miniaplicativos internos do BusyBox. Isso garante que os scripts sempre sejam executados em um ambiente previsível e sempre tenham o conjunto completo de comandos, independentemente da versão do Android em que estão sendo executados. Para forçar um comando a NÃO usar o BusyBox, você deve chamar o executável com caminhos completos.

Cada script shell executado no contexto do KernelSU será executado no shell ash do BusyBox com o Modo Autônomo ativado. Para o que é relevante para desenvolvedores terceirizados, isso inclui todos os scripts de inicialização e scripts de instalação de módulos.

Para aqueles que desejam usar o recurso “Modo Autônomo” fora do KernelSU, existem 2 maneiras de ativá-los:

  1. Defina a variável de ambiente ASH_STANDALONE como 1
    Exemplo: ASH_STANDALONE=1 /data/adb/ksu/bin/busybox sh <script>
  2. Alternar com opções de linha de comando:
    /data/adb/ksu/bin/busybox sh -o standalone <script>

Para garantir que todos os shells sh subsequentes executados também sejam executados no Modo Autônomo, a opção 1 é o método preferido (e é isso que o KernelSU e o gerenciador KernelSU usam internamente), pois as variáveis ​​de ambiente são herdadas para os subprocesso.

DIFERENÇA COM MAGISK

O BusyBox do KernelSU agora está usando o arquivo binário compilado diretamente do projeto Magisk. Obrigado ao Magisk! Portanto, você não precisa se preocupar com problemas de compatibilidade entre scripts BusyBox no Magisk e KernelSU porque eles são exatamente iguais!

Módulos KernelSU

Um módulo KernelSU é uma pasta colocada em /data/adb/modules com a estrutura abaixo:

txt
/data/adb/modules
-├── .
-├── .
-|
-├── $MODID                  <--- A pasta é nomeada com o ID do módulo
-│   │
-│   │      *** Identidade do Módulo ***
-│   │
-│   ├── module.prop         <--- Este arquivo armazena os metadados do módulo
-│   │
-│   │      *** Conteúdo Principal ***
-│   │
-│   ├── system              <--- Esta pasta será montada se skip_mount não existir
-│   │   ├── ...
-│   │   ├── ...
-│   │   └── ...
-│   │
-│   │      *** Sinalizadores de Status ***
-│   │
-│   ├── skip_mount          <--- Se existir, o KernelSU NÃO montará sua pasta de sistema
-│   ├── disable             <--- Se existir, o módulo será desabilitado
-│   ├── remove              <--- Se existir, o módulo será removido na próxima reinicialização
-│   │
-│   │      *** Arquivos Opcionais ***
-│   │
-│   ├── post-fs-data.sh     <--- Este script será executado em post-fs-data
-│   ├── post-mount.sh       <--- Este script será executado em post-mount
-│   ├── service.sh          <--- Este script será executado no late_start service
-│   ├── boot-completed.sh   <--- Este script será executado na inicialização concluída
-|   ├── uninstall.sh        <--- Este script será executado quando o KernelSU remover seu módulo
-│   ├── system.prop         <--- As propriedades neste arquivo serão carregadas como propriedades do sistema por resetprop
-│   ├── sepolicy.rule       <--- Regras adicionais de sepolicy personalizadas
-│   │
-│   │      *** Gerado Automaticamente, NÃO CRIE OU MODIFIQUE MANUALMENTE ***
-│   │
-│   ├── vendor              <--- Um link simbólico para $MODID/system/vendor
-│   ├── product             <--- Um link simbólico para $MODID/system/product
-│   ├── system_ext          <--- Um link simbólico para $MODID/system/system_ext
-│   │
-│   │      *** Quaisquer arquivos/pastas adicionais são permitidos ***
-│   │
-│   ├── ...
-│   └── ...
-|
-├── another_module
-│   ├── .
-│   └── .
-├── .
-├── .

DIFERENÇA COM MAGISK

O KernelSU não possui suporte integrado para o Zygisk, portanto não há conteúdo relacionado ao Zygisk no módulo. No entanto, você pode usar ZygiskNext para suportar módulos Zygisk. Neste caso, o conteúdo do módulo Zygisk é idêntico ao suportado pelo Magisk.

module.prop

module.prop é um arquivo de configuração para um módulo. No KernelSU, se um módulo não contiver este arquivo, ele não será reconhecido como um módulo. O formato deste arquivo é o seguinte:

txt
id=<string>
-name=<string>
-version=<string>
-versionCode=<int>
-author=<string>
-description=<string>
  • id deve corresponder a esta expressão regular: ^[a-zA-Z][a-zA-Z0-9._-]+$
    Exemplo: ✓ a_module, ✓ a.module, ✓ module-101, ✗ a module, ✗ 1_module, ✗ -a-module
    Este é o identificador exclusivo do seu módulo. Você não deve alterá-lo depois de publicado.
  • versionCode deve ser um inteiro. Isso é usado para comparar versões
  • Outros que não foram mencionados acima podem ser qualquer string de linha única.
  • Certifique-se de usar o tipo de quebra de linha UNIX (LF) e não o Windows (CR+LF) ou Macintosh (CR).

Shell scripts

Por favor, leia a seção Scripts de inicialização para entender a diferença entre post-fs-data.sh e service.sh. Para a maioria dos desenvolvedores de módulos, service.sh deve ser bom o suficiente se você precisar apenas executar um script de inicialização. Se precisar executar o script após a inicialização ser concluída, use boot-completed.sh. Se você quiser fazer algo após montar overlayfs, use post-mount.sh.

Em todos os scripts do seu módulo, use MODDIR=\${0%/*} para obter o caminho do diretório base do seu módulo, NÃO codifique o caminho do seu módulo em scripts.

DIFERENÇA COM MAGISK

Você pode usar a variável de ambiente KSU para determinar se um script está sendo executado no KernelSU ou Magisk. Se estiver executando no KernelSU, esse valor será definido como true.

Diretório system

O conteúdo deste diretório será sobreposto à partição /system do sistema usando overlayfs após a inicialização do sistema. Isso significa que:

  1. Arquivos com o mesmo nome daqueles no diretório correspondente no sistema serão substituídos pelos arquivos deste diretório.
  2. Pastas com o mesmo nome daquelas no diretório correspondente no sistema serão mescladas com as pastas neste diretório.

Se você deseja excluir um arquivo ou pasta no diretório original do sistema, você precisa criar um arquivo com o mesmo nome do arquivo/pasta no diretório do módulo usando mknod filename c 0 0. Dessa forma, o sistema overlayfs irá automaticamente "branquear" este arquivo como se ele tivesse sido excluído (a partição /system não foi realmente alterada).

Você também pode declarar uma variável chamada REMOVE contendo uma lista de diretórios em customize.sh para executar operações de remoção, e o KernelSU executará automaticamente mknod <TARGET> c 0 0 nos diretórios correspondentes do módulo. Por exemplo:

sh
REMOVE="
-/system/app/YouTube
-/system/app/Bloatware
-"

A lista acima irá executar mknod $MODPATH/system/app/YouTube c 0 0 e mknod $MODPATH/system/app/Bloatware c 0 0; e /system/app/YouTube e /system/app/Bloatware serão removidos após o módulo entrar em vigor.

Se você deseja substituir um diretório no sistema, você precisa criar um diretório com o mesmo caminho no diretório do módulo e, em seguida, definir o atributo setfattr -n trusted.overlay.opaque -v y <TARGET> para este diretório. Desta forma, o sistema overlayfs substituirá automaticamente o diretório correspondente no sistema (sem alterar a partição /system).

Você pode declarar uma variável chamada REPLACE em seu arquivo customize.sh, que inclui uma lista de diretórios a serem substituídos, e o KernelSU executará automaticamente as operações correspondentes em seu diretório de módulo. Por exemplo:

sh
REPLACE="
-/system/app/YouTube
-/system/app/Bloatware
-"

Esta lista criará automaticamente os diretórios $MODPATH/system/app/YouTube e $MODPATH/system/app/Bloatware e, em seguida, executará setfattr -n trusted.overlay.opaque -v y $MODPATH/system/app/YouTube e setfattr -n trusted.overlay.opaque -v y $MODPATH/system/app/Bloatware. Após o módulo entrar em vigor, /system/app/YouTube e /system/app/Bloatware serão substituídos por diretórios vazios.

DIFERENÇA COM MAGISK

O mecanismo sem sistema do KernelSU é implementado através do overlayfs do kernel, enquanto o Magisk atualmente usa montagem mágica (montagem de ligação). Os dois métodos de implementação têm diferenças significativas, mas o objetivo final é o mesmo: modificar os arquivos /system sem modificar fisicamente a partição /system.

Se você estiver interessado em overlayfs, é recomendável ler a documentação sobre overlayfs do Kernel Linux.

system.prop

Este arquivo segue o mesmo formato de build.prop. Cada linha é composta por [key]=[value].

sepolicy.rule

Se o seu módulo exigir alguns patches adicionais de sepolicy, adicione essas regras a este arquivo. Cada linha neste arquivo será tratada como uma declaração de política.

Instalador do módulo

Um instalador do módulo KernelSU é um módulo KernelSU empacotado em um arquivo zip que pode ser atualizado no app gerenciador KernelSU. O instalador do módulo KernelSU mais simples é apenas um módulo KernelSU compactado como um arquivo zip.

txt
module.zip
-
-├── customize.sh                       <--- (Opcional, mais detalhes posteriormente)
-│                                           Este script será fornecido por update-binary
-├── ...
-├── ...  /* O resto dos arquivos do módulo */
-

AVISO

O módulo KernelSU NÃO é compatível para instalação no Recovery personalizado!

Personalização

Se você precisar personalizar o processo de instalação do módulo, opcionalmente você pode criar um script no instalador chamado customize.sh. Este script será sourced (não executado!) pelo script do instalador do módulo depois que todos os arquivos forem extraídos e as permissões padrão e o contexto secundário forem aplicados. Isso é muito útil se o seu módulo exigir configuração adicional com base na API do dispositivo ou se você precisar definir permissões/secontext especiais para alguns dos arquivos do seu módulo.

Se você quiser controlar e personalizar totalmente o processo de instalação, declare SKIPUNZIP=1 em customize.sh para pular todas as etapas de instalação padrão. Ao fazer isso, seu customize.sh será responsável por instalar tudo sozinho.

O script customize.sh é executado no shell BusyBox ash do KernelSU com o "Modo Autônomo" ativado. As seguintes variáveis ​​e funções estão disponíveis:

Variáveis

  • KSU (bool): uma variável para marcar que o script está sendo executado no ambiente KernelSU, e o valor desta variável sempre será true. Você pode usá-lo para distinguir entre KernelSU e Magisk.
  • KSU_VER (string): a string da versão do KernelSU atualmente instalado (por exemplo, v0.4.0).
  • KSU_VER_CODE (int): o código da versão do KernelSU atualmente instalado no espaço do usuário (por exemplo: 10672).
  • KSU_KERNEL_VER_CODE (int): o código da versão do KernelSU atualmente instalado no espaço do kernel (por exemplo: 10672).
  • BOOTMODE (bool): sempre seja true no KernelSU.
  • MODPATH (path): o caminho onde os arquivos do seu módulo devem ser instalados.
  • TMPDIR (path): um lugar onde você pode armazenar arquivos temporariamente.
  • ZIPFILE (path): zip de instalação do seu módulo.
  • ARCH (string): a arquitetura da CPU do dispositivo. O valor é arm, arm64, x86 ou x64.
  • IS64BIT (bool): true se $ARCH for arm64 ou x64.
  • API (int): o nível da API (versão do Android) do dispositivo (por exemplo: 23 para Android 6.0).

AVISO

No KernelSU, MAGISK_VER_CODE é sempre 25200 e MAGISK_VER é sempre v25.2. Por favor, não use essas duas variáveis ​​para determinar se ele está sendo executado no KernelSU ou não.

Funções

txt
ui_print <msg>
-    imprima <msg> no console
-    Evite usar 'echo', pois ele não será exibido no console de recuperação personalizado
-
-abort <msg>
-    imprima mensagem de erro <msg> para consolar e encerrar a instalação
-    Evite usar 'exit', pois isso irá pular as etapas de limpeza de encerramento
-
-set_perm <target> <owner> <group> <permission> [context]
-    se [context] não estiver definido, o padrão é "u:object_r:system_file:s0"
-    esta função é uma abreviação para os seguintes comandos:
-       chown owner.group target
-       chmod permission target
-       chcon context target
-
-set_perm_recursive <directory> <owner> <group> <dirpermission> <filepermission> [context]
-    se [context] não está definido, o padrão é "u:object_r:system_file:s0"
-    para todos os arquivos em <directory>, ele chamará:
-       contexto de permissão de arquivo do grupo proprietário do arquivo set_perm
-    para todos os diretórios em <directory> (including itself), ele vai ligar:
-       set_perm dir owner group dirpermission context

Scripts de inicialização

No KernelSU, os scripts são divididos em dois tipos com base em seu modo de execução: modo post-fs-data e modo de serviço late_start:

  • modo post-fs-data
    • Esta etapa está BLOQUEANDO. O processo de inicialização é pausado antes da execução ser concluída ou 10 segundos se passaram.
    • Os scripts são executados antes de qualquer módulo ser montado. Isso permite que um desenvolvedor de módulo ajuste dinamicamente seus módulos antes de serem montados.
    • Este estágio acontece antes do início do Zygote, o que significa praticamente tudo no Android.
    • AVISO: Usar setprop irá bloquear o processo de inicialização! Por favor, use resetprop -n <prop_name> <prop_value> em vez disso.
    • Execute scripts neste modo apenas se necessário.
  • modo de serviço late_start
    • Esta etapa é SEM BLOQUEIO. Seu script é executado em paralelo com o restante do processo de inicialização.
    • Este é o estágio recomendado para executar a maioria dos scripts.

No KernelSU, os scripts de inicialização são divididos em dois tipos com base no local de armazenamento: scripts gerais e scripts de módulo:

  • Scripts gerais
    • Colocado em /data/adb/post-fs-data.d, /data/adb/service.d, /data/adb/post-mount.d ou /data/adb/boot-completed.d.
    • Somente executado se o script estiver definido como executável (chmod +x script.sh).
    • Os scripts em post-fs-data.d são executados no modo post-fs-data e os scripts em service.d são executados no modo de serviço late_start.
    • Os módulos NÃO devem adicionar scripts gerais durante a instalação.
  • Scripts de módulo
    • Colocado na própria pasta do módulo.
    • Executado apenas se o módulo estiver ativado.
    • post-fs-data.sh é executado no modo post-fs-data, service.sh é executado no modo de serviço late_start, boot-completed.sh é executado na inicialização concluída e post-mount.sh é executado em overlayfs montado.

Todos os scripts de inicialização serão executados no shell BusyBox ash do KernelSU com o "Modo Autônomo" ativado.

`,58),l=[t];function i(r,d,c,p,m,u){return o(),s("div",null,l)}const C=e(n,[["render",i]]);export{v as __pageData,C as default}; diff --git a/assets/pt_BR_guide_module.md.039201f7.lean.js b/assets/pt_BR_guide_module.md.039201f7.lean.js deleted file mode 100644 index 0143bbaef869..000000000000 --- a/assets/pt_BR_guide_module.md.039201f7.lean.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as e,o,c as s,O as a}from"./chunks/framework.43781440.js";const v=JSON.parse('{"title":"Guias de módulo","description":"","frontmatter":{},"headers":[],"relativePath":"pt_BR/guide/module.md","filePath":"pt_BR/guide/module.md"}'),n={name:"pt_BR/guide/module.md"},t=a("",58),l=[t];function i(r,d,c,p,m,u){return o(),s("div",null,l)}const C=e(n,[["render",i]]);export{v as __pageData,C as default}; diff --git a/assets/pt_BR_guide_module.md.dc3df57b.js b/assets/pt_BR_guide_module.md.dc3df57b.js new file mode 100644 index 000000000000..b9fb260b458b --- /dev/null +++ b/assets/pt_BR_guide_module.md.dc3df57b.js @@ -0,0 +1,171 @@ +import{_ as e,o as s,c as a,Q as o}from"./chunks/framework.ec8f7e8e.js";const v=JSON.parse('{"title":"Guias de módulo","description":"","frontmatter":{},"headers":[],"relativePath":"pt_BR/guide/module.md","filePath":"pt_BR/guide/module.md"}'),n={name:"pt_BR/guide/module.md"},l=o(`

Guias de módulo

O KernelSU fornece um mecanismo de módulo que consegue modificar o diretório do sistema enquanto mantém a integridade da partição do sistema. Este mecanismo é conhecido como "sem sistema".

O mecanismo de módulo do KernelSU é quase o mesmo do Magisk. Se você está familiarizado com o desenvolvimento de módulos Magisk, o desenvolvimento de módulos KernelSU é muito semelhante. Você pode pular a introdução dos módulos abaixo e só precisa ler Diferença com Magisk.

BusyBox

O KernelSU vem com um recurso binário BusyBox completo (incluindo suporte completo ao SELinux). O executável está localizado em /data/adb/ksu/bin/busybox. O BusyBox do KernelSU suporta o "ASH Standalone Shell Mode" alternável em tempo de execução. O que este Modo Autônomo significa é que ao executar no shell ash do BusyBox, cada comando usará diretamente o miniaplicativo dentro do BusyBox, independentemente do que estiver definido como PATH. Por exemplo, comandos como ls, rm, chmod NÃO usarão o que está em PATH (no caso do Android por padrão será /system/bin/ls, /system/bin/rm e /system/bin/chmod respectivamente), mas em vez disso chamará diretamente os miniaplicativos internos do BusyBox. Isso garante que os scripts sempre sejam executados em um ambiente previsível e sempre tenham o conjunto completo de comandos, independentemente da versão do Android em que estão sendo executados. Para forçar um comando a NÃO usar o BusyBox, você deve chamar o executável com caminhos completos.

Cada script shell executado no contexto do KernelSU será executado no shell ash do BusyBox com o Modo Autônomo ativado. Para o que é relevante para desenvolvedores terceirizados, isso inclui todos os scripts de inicialização e scripts de instalação de módulos.

Para aqueles que desejam usar o recurso “Modo Autônomo” fora do KernelSU, existem 2 maneiras de ativá-los:

  1. Defina a variável de ambiente ASH_STANDALONE como 1
    Exemplo: ASH_STANDALONE=1 /data/adb/ksu/bin/busybox sh <script>
  2. Alternar com opções de linha de comando:
    /data/adb/ksu/bin/busybox sh -o standalone <script>

Para garantir que todos os shells sh subsequentes executados também sejam executados no Modo Autônomo, a opção 1 é o método preferido (e é isso que o KernelSU e o gerenciador KernelSU usam internamente), pois as variáveis ​​de ambiente são herdadas para os subprocesso.

DIFERENÇA COM MAGISK

O BusyBox do KernelSU agora está usando o arquivo binário compilado diretamente do projeto Magisk. Obrigado ao Magisk! Portanto, você não precisa se preocupar com problemas de compatibilidade entre scripts BusyBox no Magisk e KernelSU porque eles são exatamente iguais!

Módulos KernelSU

Um módulo KernelSU é uma pasta colocada em /data/adb/modules com a estrutura abaixo:

txt
/data/adb/modules
+├── .
+├── .
+|
+├── $MODID                  <--- A pasta é nomeada com o ID do módulo
+│   │
+│   │      *** Identidade do Módulo ***
+│   │
+│   ├── module.prop         <--- Este arquivo armazena os metadados do módulo
+│   │
+│   │      *** Conteúdo Principal ***
+│   │
+│   ├── system              <--- Esta pasta será montada se skip_mount não existir
+│   │   ├── ...
+│   │   ├── ...
+│   │   └── ...
+│   │
+│   │      *** Sinalizadores de Status ***
+│   │
+│   ├── skip_mount          <--- Se existir, o KernelSU NÃO montará sua pasta de sistema
+│   ├── disable             <--- Se existir, o módulo será desabilitado
+│   ├── remove              <--- Se existir, o módulo será removido na próxima reinicialização
+│   │
+│   │      *** Arquivos Opcionais ***
+│   │
+│   ├── post-fs-data.sh     <--- Este script será executado em post-fs-data
+│   ├── post-mount.sh       <--- Este script será executado em post-mount
+│   ├── service.sh          <--- Este script será executado no late_start service
+│   ├── boot-completed.sh   <--- Este script será executado na inicialização concluída
+|   ├── uninstall.sh        <--- Este script será executado quando o KernelSU remover seu módulo
+│   ├── system.prop         <--- As propriedades neste arquivo serão carregadas como propriedades do sistema por resetprop
+│   ├── sepolicy.rule       <--- Regras adicionais de sepolicy personalizadas
+│   │
+│   │      *** Gerado Automaticamente, NÃO CRIE OU MODIFIQUE MANUALMENTE ***
+│   │
+│   ├── vendor              <--- Um link simbólico para $MODID/system/vendor
+│   ├── product             <--- Um link simbólico para $MODID/system/product
+│   ├── system_ext          <--- Um link simbólico para $MODID/system/system_ext
+│   │
+│   │      *** Quaisquer arquivos/pastas adicionais são permitidos ***
+│   │
+│   ├── ...
+│   └── ...
+|
+├── another_module
+│   ├── .
+│   └── .
+├── .
+├── .
/data/adb/modules
+├── .
+├── .
+|
+├── $MODID                  <--- A pasta é nomeada com o ID do módulo
+│   │
+│   │      *** Identidade do Módulo ***
+│   │
+│   ├── module.prop         <--- Este arquivo armazena os metadados do módulo
+│   │
+│   │      *** Conteúdo Principal ***
+│   │
+│   ├── system              <--- Esta pasta será montada se skip_mount não existir
+│   │   ├── ...
+│   │   ├── ...
+│   │   └── ...
+│   │
+│   │      *** Sinalizadores de Status ***
+│   │
+│   ├── skip_mount          <--- Se existir, o KernelSU NÃO montará sua pasta de sistema
+│   ├── disable             <--- Se existir, o módulo será desabilitado
+│   ├── remove              <--- Se existir, o módulo será removido na próxima reinicialização
+│   │
+│   │      *** Arquivos Opcionais ***
+│   │
+│   ├── post-fs-data.sh     <--- Este script será executado em post-fs-data
+│   ├── post-mount.sh       <--- Este script será executado em post-mount
+│   ├── service.sh          <--- Este script será executado no late_start service
+│   ├── boot-completed.sh   <--- Este script será executado na inicialização concluída
+|   ├── uninstall.sh        <--- Este script será executado quando o KernelSU remover seu módulo
+│   ├── system.prop         <--- As propriedades neste arquivo serão carregadas como propriedades do sistema por resetprop
+│   ├── sepolicy.rule       <--- Regras adicionais de sepolicy personalizadas
+│   │
+│   │      *** Gerado Automaticamente, NÃO CRIE OU MODIFIQUE MANUALMENTE ***
+│   │
+│   ├── vendor              <--- Um link simbólico para $MODID/system/vendor
+│   ├── product             <--- Um link simbólico para $MODID/system/product
+│   ├── system_ext          <--- Um link simbólico para $MODID/system/system_ext
+│   │
+│   │      *** Quaisquer arquivos/pastas adicionais são permitidos ***
+│   │
+│   ├── ...
+│   └── ...
+|
+├── another_module
+│   ├── .
+│   └── .
+├── .
+├── .

DIFERENÇA COM MAGISK

O KernelSU não possui suporte integrado para o Zygisk, portanto não há conteúdo relacionado ao Zygisk no módulo. No entanto, você pode usar ZygiskNext para suportar módulos Zygisk. Neste caso, o conteúdo do módulo Zygisk é idêntico ao suportado pelo Magisk.

module.prop

module.prop é um arquivo de configuração para um módulo. No KernelSU, se um módulo não contiver este arquivo, ele não será reconhecido como um módulo. O formato deste arquivo é o seguinte:

txt
id=<string>
+name=<string>
+version=<string>
+versionCode=<int>
+author=<string>
+description=<string>
id=<string>
+name=<string>
+version=<string>
+versionCode=<int>
+author=<string>
+description=<string>
  • id deve corresponder a esta expressão regular: ^[a-zA-Z][a-zA-Z0-9._-]+$
    Exemplo: ✓ a_module, ✓ a.module, ✓ module-101, ✗ a module, ✗ 1_module, ✗ -a-module
    Este é o identificador exclusivo do seu módulo. Você não deve alterá-lo depois de publicado.
  • versionCode deve ser um inteiro. Isso é usado para comparar versões
  • Outros que não foram mencionados acima podem ser qualquer string de linha única.
  • Certifique-se de usar o tipo de quebra de linha UNIX (LF) e não o Windows (CR+LF) ou Macintosh (CR).

Shell scripts

Por favor, leia a seção Scripts de inicialização para entender a diferença entre post-fs-data.sh e service.sh. Para a maioria dos desenvolvedores de módulos, service.sh deve ser bom o suficiente se você precisar apenas executar um script de inicialização. Se precisar executar o script após a inicialização ser concluída, use boot-completed.sh. Se você quiser fazer algo após montar overlayfs, use post-mount.sh.

Em todos os scripts do seu módulo, use MODDIR=\${0%/*} para obter o caminho do diretório base do seu módulo, NÃO codifique o caminho do seu módulo em scripts.

DIFERENÇA COM MAGISK

Você pode usar a variável de ambiente KSU para determinar se um script está sendo executado no KernelSU ou Magisk. Se estiver executando no KernelSU, esse valor será definido como true.

Diretório system

O conteúdo deste diretório será sobreposto à partição /system do sistema usando overlayfs após a inicialização do sistema. Isso significa que:

  1. Arquivos com o mesmo nome daqueles no diretório correspondente no sistema serão substituídos pelos arquivos deste diretório.
  2. Pastas com o mesmo nome daquelas no diretório correspondente no sistema serão mescladas com as pastas neste diretório.

Se você deseja excluir um arquivo ou pasta no diretório original do sistema, você precisa criar um arquivo com o mesmo nome do arquivo/pasta no diretório do módulo usando mknod filename c 0 0. Dessa forma, o sistema overlayfs irá automaticamente "branquear" este arquivo como se ele tivesse sido excluído (a partição /system não foi realmente alterada).

Você também pode declarar uma variável chamada REMOVE contendo uma lista de diretórios em customize.sh para executar operações de remoção, e o KernelSU executará automaticamente mknod <TARGET> c 0 0 nos diretórios correspondentes do módulo. Por exemplo:

sh
REMOVE="
+/system/app/YouTube
+/system/app/Bloatware
+"
REMOVE="
+/system/app/YouTube
+/system/app/Bloatware
+"

A lista acima irá executar mknod $MODPATH/system/app/YouTube c 0 0 e mknod $MODPATH/system/app/Bloatware c 0 0; e /system/app/YouTube e /system/app/Bloatware serão removidos após o módulo entrar em vigor.

Se você deseja substituir um diretório no sistema, você precisa criar um diretório com o mesmo caminho no diretório do módulo e, em seguida, definir o atributo setfattr -n trusted.overlay.opaque -v y <TARGET> para este diretório. Desta forma, o sistema overlayfs substituirá automaticamente o diretório correspondente no sistema (sem alterar a partição /system).

Você pode declarar uma variável chamada REPLACE em seu arquivo customize.sh, que inclui uma lista de diretórios a serem substituídos, e o KernelSU executará automaticamente as operações correspondentes em seu diretório de módulo. Por exemplo:

sh
REPLACE="
+/system/app/YouTube
+/system/app/Bloatware
+"
REPLACE="
+/system/app/YouTube
+/system/app/Bloatware
+"

Esta lista criará automaticamente os diretórios $MODPATH/system/app/YouTube e $MODPATH/system/app/Bloatware e, em seguida, executará setfattr -n trusted.overlay.opaque -v y $MODPATH/system/app/YouTube e setfattr -n trusted.overlay.opaque -v y $MODPATH/system/app/Bloatware. Após o módulo entrar em vigor, /system/app/YouTube e /system/app/Bloatware serão substituídos por diretórios vazios.

DIFERENÇA COM MAGISK

O mecanismo sem sistema do KernelSU é implementado através do overlayfs do kernel, enquanto o Magisk atualmente usa montagem mágica (montagem de ligação). Os dois métodos de implementação têm diferenças significativas, mas o objetivo final é o mesmo: modificar os arquivos /system sem modificar fisicamente a partição /system.

Se você estiver interessado em overlayfs, é recomendável ler a documentação sobre overlayfs do Kernel Linux.

system.prop

Este arquivo segue o mesmo formato de build.prop. Cada linha é composta por [key]=[value].

sepolicy.rule

Se o seu módulo exigir alguns patches adicionais de sepolicy, adicione essas regras a este arquivo. Cada linha neste arquivo será tratada como uma declaração de política.

Instalador do módulo

Um instalador do módulo KernelSU é um módulo KernelSU empacotado em um arquivo zip que pode ser atualizado no app gerenciador KernelSU. O instalador do módulo KernelSU mais simples é apenas um módulo KernelSU compactado como um arquivo zip.

txt
module.zip
+
+├── customize.sh                       <--- (Opcional, mais detalhes posteriormente)
+│                                           Este script será fornecido por update-binary
+├── ...
+├── ...  /* O resto dos arquivos do módulo */
+
module.zip
+
+├── customize.sh                       <--- (Opcional, mais detalhes posteriormente)
+│                                           Este script será fornecido por update-binary
+├── ...
+├── ...  /* O resto dos arquivos do módulo */
+

AVISO

O módulo KernelSU NÃO é compatível para instalação no Recovery personalizado!

Personalização

Se você precisar personalizar o processo de instalação do módulo, opcionalmente você pode criar um script no instalador chamado customize.sh. Este script será sourced (não executado!) pelo script do instalador do módulo depois que todos os arquivos forem extraídos e as permissões padrão e o contexto secundário forem aplicados. Isso é muito útil se o seu módulo exigir configuração adicional com base na API do dispositivo ou se você precisar definir permissões/secontext especiais para alguns dos arquivos do seu módulo.

Se você quiser controlar e personalizar totalmente o processo de instalação, declare SKIPUNZIP=1 em customize.sh para pular todas as etapas de instalação padrão. Ao fazer isso, seu customize.sh será responsável por instalar tudo sozinho.

O script customize.sh é executado no shell BusyBox ash do KernelSU com o "Modo Autônomo" ativado. As seguintes variáveis ​​e funções estão disponíveis:

Variáveis

  • KSU (bool): uma variável para marcar que o script está sendo executado no ambiente KernelSU, e o valor desta variável sempre será true. Você pode usá-lo para distinguir entre KernelSU e Magisk.
  • KSU_VER (string): a string da versão do KernelSU atualmente instalado (por exemplo, v0.4.0).
  • KSU_VER_CODE (int): o código da versão do KernelSU atualmente instalado no espaço do usuário (por exemplo: 10672).
  • KSU_KERNEL_VER_CODE (int): o código da versão do KernelSU atualmente instalado no espaço do kernel (por exemplo: 10672).
  • BOOTMODE (bool): sempre seja true no KernelSU.
  • MODPATH (path): o caminho onde os arquivos do seu módulo devem ser instalados.
  • TMPDIR (path): um lugar onde você pode armazenar arquivos temporariamente.
  • ZIPFILE (path): zip de instalação do seu módulo.
  • ARCH (string): a arquitetura da CPU do dispositivo. O valor é arm, arm64, x86 ou x64.
  • IS64BIT (bool): true se $ARCH for arm64 ou x64.
  • API (int): o nível da API (versão do Android) do dispositivo (por exemplo: 23 para Android 6.0).

AVISO

No KernelSU, MAGISK_VER_CODE é sempre 25200 e MAGISK_VER é sempre v25.2. Por favor, não use essas duas variáveis ​​para determinar se ele está sendo executado no KernelSU ou não.

Funções

txt
ui_print <msg>
+    imprima <msg> no console
+    Evite usar 'echo', pois ele não será exibido no console de recuperação personalizado
+
+abort <msg>
+    imprima mensagem de erro <msg> para consolar e encerrar a instalação
+    Evite usar 'exit', pois isso irá pular as etapas de limpeza de encerramento
+
+set_perm <target> <owner> <group> <permission> [context]
+    se [context] não estiver definido, o padrão é "u:object_r:system_file:s0"
+    esta função é uma abreviação para os seguintes comandos:
+       chown owner.group target
+       chmod permission target
+       chcon context target
+
+set_perm_recursive <directory> <owner> <group> <dirpermission> <filepermission> [context]
+    se [context] não está definido, o padrão é "u:object_r:system_file:s0"
+    para todos os arquivos em <directory>, ele chamará:
+       contexto de permissão de arquivo do grupo proprietário do arquivo set_perm
+    para todos os diretórios em <directory> (including itself), ele vai ligar:
+       set_perm dir owner group dirpermission context
ui_print <msg>
+    imprima <msg> no console
+    Evite usar 'echo', pois ele não será exibido no console de recuperação personalizado
+
+abort <msg>
+    imprima mensagem de erro <msg> para consolar e encerrar a instalação
+    Evite usar 'exit', pois isso irá pular as etapas de limpeza de encerramento
+
+set_perm <target> <owner> <group> <permission> [context]
+    se [context] não estiver definido, o padrão é "u:object_r:system_file:s0"
+    esta função é uma abreviação para os seguintes comandos:
+       chown owner.group target
+       chmod permission target
+       chcon context target
+
+set_perm_recursive <directory> <owner> <group> <dirpermission> <filepermission> [context]
+    se [context] não está definido, o padrão é "u:object_r:system_file:s0"
+    para todos os arquivos em <directory>, ele chamará:
+       contexto de permissão de arquivo do grupo proprietário do arquivo set_perm
+    para todos os diretórios em <directory> (including itself), ele vai ligar:
+       set_perm dir owner group dirpermission context

Scripts de inicialização

No KernelSU, os scripts são divididos em dois tipos com base em seu modo de execução: modo post-fs-data e modo de serviço late_start:

  • modo post-fs-data
    • Esta etapa está BLOQUEANDO. O processo de inicialização é pausado antes da execução ser concluída ou 10 segundos se passaram.
    • Os scripts são executados antes de qualquer módulo ser montado. Isso permite que um desenvolvedor de módulo ajuste dinamicamente seus módulos antes de serem montados.
    • Este estágio acontece antes do início do Zygote, o que significa praticamente tudo no Android.
    • AVISO: Usar setprop irá bloquear o processo de inicialização! Por favor, use resetprop -n <prop_name> <prop_value> em vez disso.
    • Execute scripts neste modo apenas se necessário.
  • modo de serviço late_start
    • Esta etapa é SEM BLOQUEIO. Seu script é executado em paralelo com o restante do processo de inicialização.
    • Este é o estágio recomendado para executar a maioria dos scripts.

No KernelSU, os scripts de inicialização são divididos em dois tipos com base no local de armazenamento: scripts gerais e scripts de módulo:

  • Scripts gerais
    • Colocado em /data/adb/post-fs-data.d, /data/adb/service.d, /data/adb/post-mount.d ou /data/adb/boot-completed.d.
    • Somente executado se o script estiver definido como executável (chmod +x script.sh).
    • Os scripts em post-fs-data.d são executados no modo post-fs-data e os scripts em service.d são executados no modo de serviço late_start.
    • Os módulos NÃO devem adicionar scripts gerais durante a instalação.
  • Scripts de módulo
    • Colocado na própria pasta do módulo.
    • Executado apenas se o módulo estiver ativado.
    • post-fs-data.sh é executado no modo post-fs-data, service.sh é executado no modo de serviço late_start, boot-completed.sh é executado na inicialização concluída e post-mount.sh é executado em overlayfs montado.

Todos os scripts de inicialização serão executados no shell BusyBox ash do KernelSU com o "Modo Autônomo" ativado.

`,58),t=[l];function p(i,r,c,d,u,m){return s(),a("div",null,t)}const g=e(n,[["render",p]]);export{v as __pageData,g as default}; diff --git a/assets/pt_BR_guide_module.md.dc3df57b.lean.js b/assets/pt_BR_guide_module.md.dc3df57b.lean.js new file mode 100644 index 000000000000..d42ceb9d94f4 --- /dev/null +++ b/assets/pt_BR_guide_module.md.dc3df57b.lean.js @@ -0,0 +1 @@ +import{_ as e,o as s,c as a,Q as o}from"./chunks/framework.ec8f7e8e.js";const v=JSON.parse('{"title":"Guias de módulo","description":"","frontmatter":{},"headers":[],"relativePath":"pt_BR/guide/module.md","filePath":"pt_BR/guide/module.md"}'),n={name:"pt_BR/guide/module.md"},l=o("",58),t=[l];function p(i,r,c,d,u,m){return s(),a("div",null,t)}const g=e(n,[["render",p]]);export{v as __pageData,g as default}; diff --git a/assets/pt_BR_guide_rescue-from-bootloop.md.f4a13bcd.js b/assets/pt_BR_guide_rescue-from-bootloop.md.ee15e0d3.js similarity index 98% rename from assets/pt_BR_guide_rescue-from-bootloop.md.f4a13bcd.js rename to assets/pt_BR_guide_rescue-from-bootloop.md.ee15e0d3.js index 1329152b8d9b..17c4ea19d453 100644 --- a/assets/pt_BR_guide_rescue-from-bootloop.md.f4a13bcd.js +++ b/assets/pt_BR_guide_rescue-from-bootloop.md.ee15e0d3.js @@ -1 +1 @@ -import{_ as o,o as e,c as a,O as i}from"./chunks/framework.43781440.js";const b=JSON.parse('{"title":"Resgate do bootloop","description":"","frontmatter":{},"headers":[],"relativePath":"pt_BR/guide/rescue-from-bootloop.md","filePath":"pt_BR/guide/rescue-from-bootloop.md"}'),s={name:"pt_BR/guide/rescue-from-bootloop.md"},r=i('

Resgate do bootloop

Ao atualizar um dispositivo, podemos encontrar situações em que o dispositivo fica "bloqueado". Em teoria, se você usar o fastboot apenas para atualizar a partição boot ou instalar módulos inadequados que causam falha na inicialização do dispositivo, isso poderá ser restaurado por meio de operações apropriadas. Este documento tem como objetivo fornecer alguns métodos de emergência para ajudá-lo a se recuperar de um dispositivo "bloqueado".

Bloqueio por fleshear partição boot

No KernelSU, as seguintes situações podem causar bloqueio de inicialização ao fleshear a partição boot:

  1. Você atualizou uma imagem boot no formato errado. Por exemplo, se o formato de boot do seu telefone for gz, mas você atualizou uma imagem no formato lz4, o telefone não será capaz de inicializar.
  2. Seu telefone precisa desativar a verificação AVB para inicializar corretamente (geralmente exigindo a limpeza de todos os dados do telefone).
  3. Seu kernel tem alguns bugs ou não é adequado para o flash do seu telefone.

Não importa qual seja a situação, você pode recuperar fleshando a imagem de boot padrão. Portanto, no início do tutorial de instalação, recomendamos fortemente que você faça backup de seu boot padrão antes de fleshar. Se você não fez backup, poderá obter o boot original de fábrica de outros usuários com o mesmo dispositivo que você ou do firmware oficial.

Bloqueio por módulos

A instalação de módulos pode ser uma causa mais comum de bloqueio do seu dispositivo, mas devemos avisá-lo seriamente: NÃO INSTALE MÓDULOS DE FONTES DESCONHECIDAS! Como os módulos têm privilégios root, eles podem causar danos irreversíveis ao seu dispositivo!

Módulos normais

Se você instalou um módulo que foi comprovadamente seguro, mas faz com que seu dispositivo não inicialize, então esta situação é facilmente recuperável no KernelSU sem qualquer preocupação. O KernelSU possui mecanismos integrados para recuperar seu dispositivo, incluindo o seguinte:

  1. Atualização AB
  2. Recupere pressionando o botão de diminuir volume

Atualização AB

As atualizações do módulo KernelSU inspiram-se no mecanismo de atualização AB do sistema Android usado em atualizações OTA. Se você instalar um novo módulo ou atualizar um existente, isso não modificará diretamente o arquivo do módulo usado atualmente. Em vez disso, todos os módulos serão integrados em outra imagem de atualização. Depois que o sistema for reiniciado, ele tentará começar a usar esta imagem de atualização. Se o sistema Android inicializar com sucesso, os módulos serão realmente atualizados.

Portanto, o método mais simples e comumente usado para recuperar seu dispositivo é forçar uma reinicialização. Se você não conseguir iniciar o sistema após instalar um módulo, você pode pressionar e segurar o botão liga/desliga por mais de 10 segundos e o sistema será reinicializado automaticamente. Após a reinicialização, ele retornará ao estado anterior à atualização do módulo e os módulos atualizados anteriormente serão desativados automaticamente.

Recupere pressionando o botão de diminuir volume

Se as atualizações AB ainda não resolverem o problema, você pode tentar usar o Modo de Segurança. No Modo de Segurança, todos os módulos estão desabilitados.

Existem duas maneiras de entrar no Modo de Segurança:

  1. O Modo de Segurança integrado de alguns sistemas. Alguns sistemas possuem um Modo de Segurança integrado que pode ser acessado pressionando longamente o botão de diminuir volume, enquanto outros (como a MIUI) podem ativar o Modo de Segurança no Recovery. Ao entrar no Modo de Segurança do sistema, o KernelSU também entrará no Modo de Segurança e desativará automaticamente os módulos.
  2. O Modo de Segurança integrado do KernelSU. O método de operação é pressionar a tecla de diminuir volume continuamente por mais de três vezes após a primeira tela de inicialização.

Após entrar no Modo de Segurança, todos os módulos na página de módulos do gerenciador KernelSU são desabilitados, mas você pode executar operações de "desinstalação" para desinstalar quaisquer módulos que possam estar causando problemas.

O Modo de Segurança integrado é implementado no kernel, portanto não há possibilidade de perder eventos importantes devido à interceptação. No entanto, para kernels não GKI, a integração manual do código pode ser necessária e você pode consultar a documentação oficial para obter orientação.

Módulos maliciosos

Se os métodos acima não conseguirem recuperar seu dispositivo, é altamente provável que o módulo que você instalou tenha operações maliciosas ou tenha danificado seu dispositivo por outros meios. Neste caso, existem apenas duas sugestões:

  1. Limpe os dados e instale o sistema oficial.
  2. Consulte o serviço pós-venda.
',23),t=[r];function d(n,u,l,m,p,c){return e(),a("div",null,t)}const v=o(s,[["render",d]]);export{b as __pageData,v as default}; +import{_ as o,o as e,c as a,Q as i}from"./chunks/framework.ec8f7e8e.js";const b=JSON.parse('{"title":"Resgate do bootloop","description":"","frontmatter":{},"headers":[],"relativePath":"pt_BR/guide/rescue-from-bootloop.md","filePath":"pt_BR/guide/rescue-from-bootloop.md"}'),s={name:"pt_BR/guide/rescue-from-bootloop.md"},r=i('

Resgate do bootloop

Ao atualizar um dispositivo, podemos encontrar situações em que o dispositivo fica "bloqueado". Em teoria, se você usar o fastboot apenas para atualizar a partição boot ou instalar módulos inadequados que causam falha na inicialização do dispositivo, isso poderá ser restaurado por meio de operações apropriadas. Este documento tem como objetivo fornecer alguns métodos de emergência para ajudá-lo a se recuperar de um dispositivo "bloqueado".

Bloqueio por fleshear partição boot

No KernelSU, as seguintes situações podem causar bloqueio de inicialização ao fleshear a partição boot:

  1. Você atualizou uma imagem boot no formato errado. Por exemplo, se o formato de boot do seu telefone for gz, mas você atualizou uma imagem no formato lz4, o telefone não será capaz de inicializar.
  2. Seu telefone precisa desativar a verificação AVB para inicializar corretamente (geralmente exigindo a limpeza de todos os dados do telefone).
  3. Seu kernel tem alguns bugs ou não é adequado para o flash do seu telefone.

Não importa qual seja a situação, você pode recuperar fleshando a imagem de boot padrão. Portanto, no início do tutorial de instalação, recomendamos fortemente que você faça backup de seu boot padrão antes de fleshar. Se você não fez backup, poderá obter o boot original de fábrica de outros usuários com o mesmo dispositivo que você ou do firmware oficial.

Bloqueio por módulos

A instalação de módulos pode ser uma causa mais comum de bloqueio do seu dispositivo, mas devemos avisá-lo seriamente: NÃO INSTALE MÓDULOS DE FONTES DESCONHECIDAS! Como os módulos têm privilégios root, eles podem causar danos irreversíveis ao seu dispositivo!

Módulos normais

Se você instalou um módulo que foi comprovadamente seguro, mas faz com que seu dispositivo não inicialize, então esta situação é facilmente recuperável no KernelSU sem qualquer preocupação. O KernelSU possui mecanismos integrados para recuperar seu dispositivo, incluindo o seguinte:

  1. Atualização AB
  2. Recupere pressionando o botão de diminuir volume

Atualização AB

As atualizações do módulo KernelSU inspiram-se no mecanismo de atualização AB do sistema Android usado em atualizações OTA. Se você instalar um novo módulo ou atualizar um existente, isso não modificará diretamente o arquivo do módulo usado atualmente. Em vez disso, todos os módulos serão integrados em outra imagem de atualização. Depois que o sistema for reiniciado, ele tentará começar a usar esta imagem de atualização. Se o sistema Android inicializar com sucesso, os módulos serão realmente atualizados.

Portanto, o método mais simples e comumente usado para recuperar seu dispositivo é forçar uma reinicialização. Se você não conseguir iniciar o sistema após instalar um módulo, você pode pressionar e segurar o botão liga/desliga por mais de 10 segundos e o sistema será reinicializado automaticamente. Após a reinicialização, ele retornará ao estado anterior à atualização do módulo e os módulos atualizados anteriormente serão desativados automaticamente.

Recupere pressionando o botão de diminuir volume

Se as atualizações AB ainda não resolverem o problema, você pode tentar usar o Modo de Segurança. No Modo de Segurança, todos os módulos estão desabilitados.

Existem duas maneiras de entrar no Modo de Segurança:

  1. O Modo de Segurança integrado de alguns sistemas. Alguns sistemas possuem um Modo de Segurança integrado que pode ser acessado pressionando longamente o botão de diminuir volume, enquanto outros (como a MIUI) podem ativar o Modo de Segurança no Recovery. Ao entrar no Modo de Segurança do sistema, o KernelSU também entrará no Modo de Segurança e desativará automaticamente os módulos.
  2. O Modo de Segurança integrado do KernelSU. O método de operação é pressionar a tecla de diminuir volume continuamente por mais de três vezes após a primeira tela de inicialização.

Após entrar no Modo de Segurança, todos os módulos na página de módulos do gerenciador KernelSU são desabilitados, mas você pode executar operações de "desinstalação" para desinstalar quaisquer módulos que possam estar causando problemas.

O Modo de Segurança integrado é implementado no kernel, portanto não há possibilidade de perder eventos importantes devido à interceptação. No entanto, para kernels não GKI, a integração manual do código pode ser necessária e você pode consultar a documentação oficial para obter orientação.

Módulos maliciosos

Se os métodos acima não conseguirem recuperar seu dispositivo, é altamente provável que o módulo que você instalou tenha operações maliciosas ou tenha danificado seu dispositivo por outros meios. Neste caso, existem apenas duas sugestões:

  1. Limpe os dados e instale o sistema oficial.
  2. Consulte o serviço pós-venda.
',23),t=[r];function d(n,u,l,m,p,c){return e(),a("div",null,t)}const v=o(s,[["render",d]]);export{b as __pageData,v as default}; diff --git a/assets/pt_BR_guide_rescue-from-bootloop.md.f4a13bcd.lean.js b/assets/pt_BR_guide_rescue-from-bootloop.md.ee15e0d3.lean.js similarity index 71% rename from assets/pt_BR_guide_rescue-from-bootloop.md.f4a13bcd.lean.js rename to assets/pt_BR_guide_rescue-from-bootloop.md.ee15e0d3.lean.js index 9c127e1e12f5..f97985de9eaa 100644 --- a/assets/pt_BR_guide_rescue-from-bootloop.md.f4a13bcd.lean.js +++ b/assets/pt_BR_guide_rescue-from-bootloop.md.ee15e0d3.lean.js @@ -1 +1 @@ -import{_ as o,o as e,c as a,O as i}from"./chunks/framework.43781440.js";const b=JSON.parse('{"title":"Resgate do bootloop","description":"","frontmatter":{},"headers":[],"relativePath":"pt_BR/guide/rescue-from-bootloop.md","filePath":"pt_BR/guide/rescue-from-bootloop.md"}'),s={name:"pt_BR/guide/rescue-from-bootloop.md"},r=i("",23),t=[r];function d(n,u,l,m,p,c){return e(),a("div",null,t)}const v=o(s,[["render",d]]);export{b as __pageData,v as default}; +import{_ as o,o as e,c as a,Q as i}from"./chunks/framework.ec8f7e8e.js";const b=JSON.parse('{"title":"Resgate do bootloop","description":"","frontmatter":{},"headers":[],"relativePath":"pt_BR/guide/rescue-from-bootloop.md","filePath":"pt_BR/guide/rescue-from-bootloop.md"}'),s={name:"pt_BR/guide/rescue-from-bootloop.md"},r=i("",23),t=[r];function d(n,u,l,m,p,c){return e(),a("div",null,t)}const v=o(s,[["render",d]]);export{b as __pageData,v as default}; diff --git a/assets/pt_BR_guide_unofficially-support-devices.md.007edca2.js b/assets/pt_BR_guide_unofficially-support-devices.md.007edca2.js deleted file mode 100644 index 27febb38953c..000000000000 --- a/assets/pt_BR_guide_unofficially-support-devices.md.007edca2.js +++ /dev/null @@ -1 +0,0 @@ -import{d as a}from"./chunks/repos.9a031e32.js";import{o as t,c as s,z as e,F as n,L as r,b as l,O as c,t as i}from"./chunks/framework.43781440.js";const d=c('

Dispositivos com suporte não oficial

AVISO

Nesta página, existem kernels para dispositivos não GKI que suportam o KernelSU mantidos por outros desenvolvedores.

AVISO

Esta página é apenas para você encontrar o código-fonte correspondente ao seu dispositivo, NÃO significa que o código-fonte foi revisado pelos desenvolvedores do KernelSU. Você deve usá-lo por sua própria conta e risco.

',3),p=e("thead",null,[e("tr",null,[e("th",null,"Mantenedor"),e("th",null,"Repositório"),e("th",null,"Dispositivos suportados")])],-1),u=["href"],_=["href"],b=JSON.parse('{"title":"Dispositivos com suporte não oficial","description":"","frontmatter":{},"headers":[],"relativePath":"pt_BR/guide/unofficially-support-devices.md","filePath":"pt_BR/guide/unofficially-support-devices.md"}'),f={name:"pt_BR/guide/unofficially-support-devices.md"},S=Object.assign(f,{setup(m){return(v,h)=>(t(),s("div",null,[d,e("table",null,[p,e("tbody",null,[(t(!0),s(n,null,r(l(a),o=>(t(),s("tr",{key:o.devices},[e("td",null,[e("a",{href:o.maintainer_link,target:"_blank",rel:"noreferrer"},i(o.maintainer),9,u)]),e("td",null,[e("a",{href:o.kernel_link,target:"_blank",rel:"noreferrer"},i(o.kernel_name),9,_)]),e("td",null,i(o.devices),1)]))),128))])])]))}});export{b as __pageData,S as default}; diff --git a/assets/pt_BR_guide_unofficially-support-devices.md.007edca2.lean.js b/assets/pt_BR_guide_unofficially-support-devices.md.007edca2.lean.js deleted file mode 100644 index 58686e2b8324..000000000000 --- a/assets/pt_BR_guide_unofficially-support-devices.md.007edca2.lean.js +++ /dev/null @@ -1 +0,0 @@ -import{d as a}from"./chunks/repos.9a031e32.js";import{o as t,c as s,z as e,F as n,L as r,b as l,O as c,t as i}from"./chunks/framework.43781440.js";const d=c("",3),p=e("thead",null,[e("tr",null,[e("th",null,"Mantenedor"),e("th",null,"Repositório"),e("th",null,"Dispositivos suportados")])],-1),u=["href"],_=["href"],b=JSON.parse('{"title":"Dispositivos com suporte não oficial","description":"","frontmatter":{},"headers":[],"relativePath":"pt_BR/guide/unofficially-support-devices.md","filePath":"pt_BR/guide/unofficially-support-devices.md"}'),f={name:"pt_BR/guide/unofficially-support-devices.md"},S=Object.assign(f,{setup(m){return(v,h)=>(t(),s("div",null,[d,e("table",null,[p,e("tbody",null,[(t(!0),s(n,null,r(l(a),o=>(t(),s("tr",{key:o.devices},[e("td",null,[e("a",{href:o.maintainer_link,target:"_blank",rel:"noreferrer"},i(o.maintainer),9,u)]),e("td",null,[e("a",{href:o.kernel_link,target:"_blank",rel:"noreferrer"},i(o.kernel_name),9,_)]),e("td",null,i(o.devices),1)]))),128))])])]))}});export{b as __pageData,S as default}; diff --git a/assets/pt_BR_guide_unofficially-support-devices.md.9d8e434d.js b/assets/pt_BR_guide_unofficially-support-devices.md.9d8e434d.js new file mode 100644 index 000000000000..bda479cfd15e --- /dev/null +++ b/assets/pt_BR_guide_unofficially-support-devices.md.9d8e434d.js @@ -0,0 +1 @@ +import{d as a}from"./chunks/repos.9a031e32.js";import{o as t,c as s,k as e,F as n,D as r,l,Q as c,t as i}from"./chunks/framework.ec8f7e8e.js";const d=c('

Dispositivos com suporte não oficial

AVISO

Nesta página, existem kernels para dispositivos não GKI que suportam o KernelSU mantidos por outros desenvolvedores.

AVISO

Esta página é apenas para você encontrar o código-fonte correspondente ao seu dispositivo, NÃO significa que o código-fonte foi revisado pelos desenvolvedores do KernelSU. Você deve usá-lo por sua própria conta e risco.

',3),p=e("thead",null,[e("tr",null,[e("th",null,"Mantenedor"),e("th",null,"Repositório"),e("th",null,"Dispositivos suportados")])],-1),u=["href"],_=["href"],b=JSON.parse('{"title":"Dispositivos com suporte não oficial","description":"","frontmatter":{},"headers":[],"relativePath":"pt_BR/guide/unofficially-support-devices.md","filePath":"pt_BR/guide/unofficially-support-devices.md"}'),f={name:"pt_BR/guide/unofficially-support-devices.md"},S=Object.assign(f,{setup(m){return(v,h)=>(t(),s("div",null,[d,e("table",null,[p,e("tbody",null,[(t(!0),s(n,null,r(l(a),o=>(t(),s("tr",{key:o.devices},[e("td",null,[e("a",{href:o.maintainer_link,target:"_blank",rel:"noreferrer"},i(o.maintainer),9,u)]),e("td",null,[e("a",{href:o.kernel_link,target:"_blank",rel:"noreferrer"},i(o.kernel_name),9,_)]),e("td",null,i(o.devices),1)]))),128))])])]))}});export{b as __pageData,S as default}; diff --git a/assets/pt_BR_guide_unofficially-support-devices.md.9d8e434d.lean.js b/assets/pt_BR_guide_unofficially-support-devices.md.9d8e434d.lean.js new file mode 100644 index 000000000000..e82ac901cc2e --- /dev/null +++ b/assets/pt_BR_guide_unofficially-support-devices.md.9d8e434d.lean.js @@ -0,0 +1 @@ +import{d as a}from"./chunks/repos.9a031e32.js";import{o as t,c as s,k as e,F as n,D as r,l,Q as c,t as i}from"./chunks/framework.ec8f7e8e.js";const d=c("",3),p=e("thead",null,[e("tr",null,[e("th",null,"Mantenedor"),e("th",null,"Repositório"),e("th",null,"Dispositivos suportados")])],-1),u=["href"],_=["href"],b=JSON.parse('{"title":"Dispositivos com suporte não oficial","description":"","frontmatter":{},"headers":[],"relativePath":"pt_BR/guide/unofficially-support-devices.md","filePath":"pt_BR/guide/unofficially-support-devices.md"}'),f={name:"pt_BR/guide/unofficially-support-devices.md"},S=Object.assign(f,{setup(m){return(v,h)=>(t(),s("div",null,[d,e("table",null,[p,e("tbody",null,[(t(!0),s(n,null,r(l(a),o=>(t(),s("tr",{key:o.devices},[e("td",null,[e("a",{href:o.maintainer_link,target:"_blank",rel:"noreferrer"},i(o.maintainer),9,u)]),e("td",null,[e("a",{href:o.kernel_link,target:"_blank",rel:"noreferrer"},i(o.kernel_name),9,_)]),e("td",null,i(o.devices),1)]))),128))])])]))}});export{b as __pageData,S as default}; diff --git a/assets/pt_BR_guide_what-is-kernelsu.md.b42bbc04.js b/assets/pt_BR_guide_what-is-kernelsu.md.e7123691.js similarity index 94% rename from assets/pt_BR_guide_what-is-kernelsu.md.b42bbc04.js rename to assets/pt_BR_guide_what-is-kernelsu.md.e7123691.js index 20299aeea11d..aa57d1c37606 100644 --- a/assets/pt_BR_guide_what-is-kernelsu.md.b42bbc04.js +++ b/assets/pt_BR_guide_what-is-kernelsu.md.e7123691.js @@ -1 +1 @@ -import{_ as e,o as a,c as o,O as r}from"./chunks/framework.43781440.js";const _=JSON.parse('{"title":"O que é KernelSU?","description":"","frontmatter":{},"headers":[],"relativePath":"pt_BR/guide/what-is-kernelsu.md","filePath":"pt_BR/guide/what-is-kernelsu.md"}'),s={name:"pt_BR/guide/what-is-kernelsu.md"},t=r('

O que é KernelSU?

O KernelSU é uma solução root para dispositivos Android GKI, funciona no modo kernel e concede permissão root ao app do espaço do usuário diretamente no espaço do kernel.

Características

A principal característica do KernelSU é que ele é baseado em kernel. O KernelSU funciona no modo kernel, portanto pode fornecer uma interface de kernel que nunca tivemos antes. Por exemplo, podemos adicionar um ponto de interrupção de hardware a qualquer processo no modo kernel; Podemos acessar a memória física de qualquer processo sem que ninguém perceba; Podemos interceptar qualquer syscall no espaço do kernel; etc.

E também, o KernelSU fornece um sistema de módulos via overlayfs, que permite carregar seu plugin personalizado no sistema. Ele também fornece um mecanismo para modificar arquivos na partição /system.

Como usar

Por favor, consulte: Instalação

Como construir

Como construir o KernelSU?

Discussão

',11),n=[t];function i(c,l,u,d,m,p){return a(),o("div",null,n)}const f=e(s,[["render",i]]);export{_ as __pageData,f as default}; +import{_ as e,o as a,c as o,Q as r}from"./chunks/framework.ec8f7e8e.js";const _=JSON.parse('{"title":"O que é KernelSU?","description":"","frontmatter":{},"headers":[],"relativePath":"pt_BR/guide/what-is-kernelsu.md","filePath":"pt_BR/guide/what-is-kernelsu.md"}'),s={name:"pt_BR/guide/what-is-kernelsu.md"},t=r('

O que é KernelSU?

O KernelSU é uma solução root para dispositivos Android GKI, funciona no modo kernel e concede permissão root ao app do espaço do usuário diretamente no espaço do kernel.

Características

A principal característica do KernelSU é que ele é baseado em kernel. O KernelSU funciona no modo kernel, portanto pode fornecer uma interface de kernel que nunca tivemos antes. Por exemplo, podemos adicionar um ponto de interrupção de hardware a qualquer processo no modo kernel; Podemos acessar a memória física de qualquer processo sem que ninguém perceba; Podemos interceptar qualquer syscall no espaço do kernel; etc.

E também, o KernelSU fornece um sistema de módulos via overlayfs, que permite carregar seu plugin personalizado no sistema. Ele também fornece um mecanismo para modificar arquivos na partição /system.

Como usar

Por favor, consulte: Instalação

Como construir

Como construir o KernelSU?

Discussão

',11),n=[t];function i(c,l,u,d,m,p){return a(),o("div",null,n)}const f=e(s,[["render",i]]);export{_ as __pageData,f as default}; diff --git a/assets/pt_BR_guide_what-is-kernelsu.md.b42bbc04.lean.js b/assets/pt_BR_guide_what-is-kernelsu.md.e7123691.lean.js similarity index 71% rename from assets/pt_BR_guide_what-is-kernelsu.md.b42bbc04.lean.js rename to assets/pt_BR_guide_what-is-kernelsu.md.e7123691.lean.js index c35fb4a6d616..ef9541937968 100644 --- a/assets/pt_BR_guide_what-is-kernelsu.md.b42bbc04.lean.js +++ b/assets/pt_BR_guide_what-is-kernelsu.md.e7123691.lean.js @@ -1 +1 @@ -import{_ as e,o as a,c as o,O as r}from"./chunks/framework.43781440.js";const _=JSON.parse('{"title":"O que é KernelSU?","description":"","frontmatter":{},"headers":[],"relativePath":"pt_BR/guide/what-is-kernelsu.md","filePath":"pt_BR/guide/what-is-kernelsu.md"}'),s={name:"pt_BR/guide/what-is-kernelsu.md"},t=r("",11),n=[t];function i(c,l,u,d,m,p){return a(),o("div",null,n)}const f=e(s,[["render",i]]);export{_ as __pageData,f as default}; +import{_ as e,o as a,c as o,Q as r}from"./chunks/framework.ec8f7e8e.js";const _=JSON.parse('{"title":"O que é KernelSU?","description":"","frontmatter":{},"headers":[],"relativePath":"pt_BR/guide/what-is-kernelsu.md","filePath":"pt_BR/guide/what-is-kernelsu.md"}'),s={name:"pt_BR/guide/what-is-kernelsu.md"},t=r("",11),n=[t];function i(c,l,u,d,m,p){return a(),o("div",null,n)}const f=e(s,[["render",i]]);export{_ as __pageData,f as default}; diff --git a/assets/pt_BR_index.md.a1524471.js b/assets/pt_BR_index.md.14860a0b.js similarity index 95% rename from assets/pt_BR_index.md.a1524471.js rename to assets/pt_BR_index.md.14860a0b.js index 6b5c06986c9a..1efc532408d5 100644 --- a/assets/pt_BR_index.md.a1524471.js +++ b/assets/pt_BR_index.md.14860a0b.js @@ -1 +1 @@ -import{_ as e,o,c as t}from"./chunks/framework.43781440.js";const u=JSON.parse('{"title":"Uma solução root baseada em kernel para Android","description":"","frontmatter":{"layout":"home","title":"Uma solução root baseada em kernel para Android","hero":{"name":"KernelSU","text":"Uma solução root baseada em kernel para Android","tagline":"","image":{"src":"/logo.png","alt":"KernelSU"},"actions":[{"theme":"brand","text":"Iniciar","link":"/pt_BR/guide/what-is-kernelsu"},{"theme":"alt","text":"Ver no GitHub","link":"https://github.com/tiann/KernelSU"}]},"features":[{"title":"Baseado em kernel","details":"Como o nome sugere, KernelSU funciona no kernel Linux, dando-lhe mais controle sobre os apps do espaço do usuário."},{"title":"Controle de acesso root","details":"Somente apps permitidos podem acessar ou ver su, todos os outros apps não estão cientes disso."},{"title":"Privilégios de root personalizáveis","details":"KernelSU permite a personalização de uid, gid, grupos, capacidades e regras SELinux do su, bloqueando privilégios de root."},{"title":"Módulo","details":"Os módulos podem modificar /system sem sistema usando overlayfs permitindo grande potência."}]},"headers":[],"relativePath":"pt_BR/index.md","filePath":"pt_BR/index.md"}'),a={name:"pt_BR/index.md"};function s(r,i,n,d,l,p){return o(),t("div")}const c=e(a,[["render",s]]);export{u as __pageData,c as default}; +import{_ as e,o,c as t}from"./chunks/framework.ec8f7e8e.js";const u=JSON.parse('{"title":"Uma solução root baseada em kernel para Android","description":"","frontmatter":{"layout":"home","title":"Uma solução root baseada em kernel para Android","hero":{"name":"KernelSU","text":"Uma solução root baseada em kernel para Android","tagline":"","image":{"src":"/logo.png","alt":"KernelSU"},"actions":[{"theme":"brand","text":"Iniciar","link":"/pt_BR/guide/what-is-kernelsu"},{"theme":"alt","text":"Ver no GitHub","link":"https://github.com/tiann/KernelSU"}]},"features":[{"title":"Baseado em kernel","details":"Como o nome sugere, KernelSU funciona no kernel Linux, dando-lhe mais controle sobre os apps do espaço do usuário."},{"title":"Controle de acesso root","details":"Somente apps permitidos podem acessar ou ver su, todos os outros apps não estão cientes disso."},{"title":"Privilégios de root personalizáveis","details":"KernelSU permite a personalização de uid, gid, grupos, capacidades e regras SELinux do su, bloqueando privilégios de root."},{"title":"Módulo","details":"Os módulos podem modificar /system sem sistema usando overlayfs permitindo grande potência."}]},"headers":[],"relativePath":"pt_BR/index.md","filePath":"pt_BR/index.md"}'),a={name:"pt_BR/index.md"};function s(r,i,n,d,l,p){return o(),t("div")}const c=e(a,[["render",s]]);export{u as __pageData,c as default}; diff --git a/assets/pt_BR_index.md.a1524471.lean.js b/assets/pt_BR_index.md.14860a0b.lean.js similarity index 95% rename from assets/pt_BR_index.md.a1524471.lean.js rename to assets/pt_BR_index.md.14860a0b.lean.js index 6b5c06986c9a..1efc532408d5 100644 --- a/assets/pt_BR_index.md.a1524471.lean.js +++ b/assets/pt_BR_index.md.14860a0b.lean.js @@ -1 +1 @@ -import{_ as e,o,c as t}from"./chunks/framework.43781440.js";const u=JSON.parse('{"title":"Uma solução root baseada em kernel para Android","description":"","frontmatter":{"layout":"home","title":"Uma solução root baseada em kernel para Android","hero":{"name":"KernelSU","text":"Uma solução root baseada em kernel para Android","tagline":"","image":{"src":"/logo.png","alt":"KernelSU"},"actions":[{"theme":"brand","text":"Iniciar","link":"/pt_BR/guide/what-is-kernelsu"},{"theme":"alt","text":"Ver no GitHub","link":"https://github.com/tiann/KernelSU"}]},"features":[{"title":"Baseado em kernel","details":"Como o nome sugere, KernelSU funciona no kernel Linux, dando-lhe mais controle sobre os apps do espaço do usuário."},{"title":"Controle de acesso root","details":"Somente apps permitidos podem acessar ou ver su, todos os outros apps não estão cientes disso."},{"title":"Privilégios de root personalizáveis","details":"KernelSU permite a personalização de uid, gid, grupos, capacidades e regras SELinux do su, bloqueando privilégios de root."},{"title":"Módulo","details":"Os módulos podem modificar /system sem sistema usando overlayfs permitindo grande potência."}]},"headers":[],"relativePath":"pt_BR/index.md","filePath":"pt_BR/index.md"}'),a={name:"pt_BR/index.md"};function s(r,i,n,d,l,p){return o(),t("div")}const c=e(a,[["render",s]]);export{u as __pageData,c as default}; +import{_ as e,o,c as t}from"./chunks/framework.ec8f7e8e.js";const u=JSON.parse('{"title":"Uma solução root baseada em kernel para Android","description":"","frontmatter":{"layout":"home","title":"Uma solução root baseada em kernel para Android","hero":{"name":"KernelSU","text":"Uma solução root baseada em kernel para Android","tagline":"","image":{"src":"/logo.png","alt":"KernelSU"},"actions":[{"theme":"brand","text":"Iniciar","link":"/pt_BR/guide/what-is-kernelsu"},{"theme":"alt","text":"Ver no GitHub","link":"https://github.com/tiann/KernelSU"}]},"features":[{"title":"Baseado em kernel","details":"Como o nome sugere, KernelSU funciona no kernel Linux, dando-lhe mais controle sobre os apps do espaço do usuário."},{"title":"Controle de acesso root","details":"Somente apps permitidos podem acessar ou ver su, todos os outros apps não estão cientes disso."},{"title":"Privilégios de root personalizáveis","details":"KernelSU permite a personalização de uid, gid, grupos, capacidades e regras SELinux do su, bloqueando privilégios de root."},{"title":"Módulo","details":"Os módulos podem modificar /system sem sistema usando overlayfs permitindo grande potência."}]},"headers":[],"relativePath":"pt_BR/index.md","filePath":"pt_BR/index.md"}'),a={name:"pt_BR/index.md"};function s(r,i,n,d,l,p){return o(),t("div")}const c=e(a,[["render",s]]);export{u as __pageData,c as default}; diff --git a/assets/ru_RU_guide_app-profile.md.084a5687.js b/assets/ru_RU_guide_app-profile.md.084a5687.js new file mode 100644 index 000000000000..f32be0e3ad44 --- /dev/null +++ b/assets/ru_RU_guide_app-profile.md.084a5687.js @@ -0,0 +1,9 @@ +import{_ as s,o,c as a,Q as l}from"./chunks/framework.ec8f7e8e.js";const F=JSON.parse('{"title":"Профиль приложений","description":"","frontmatter":{},"headers":[],"relativePath":"ru_RU/guide/app-profile.md","filePath":"ru_RU/guide/app-profile.md"}'),p={name:"ru_RU/guide/app-profile.md"},n=l(`

Профиль приложений

Профиль приложений - это механизм, предоставляемый KernelSU для настройки конфигурации различных приложений.

Для приложений, получивших права root (т.е. имеющих возможность использовать su), App Profile может также называться Root Profile. Он позволяет настраивать правила uid, gid, groups, capabilities и SELinux команды su, тем самым ограничивая привилегии пользователя root. Например, она может предоставлять сетевые права только приложениям межсетевого экрана, отказывая в праве доступа к файлам, или предоставлять права shell вместо полного root-доступа для приложений freeze: *сохранение власти в рамках принципа наименьших привилегий.

Для обычных приложений, не имеющих прав root, App Profile может управлять поведением ядра и системы модулей по отношению к этим приложениям. Например, он может определять, следует ли обращать внимание на модификации, возникающие в результате работы модулей. На основе этой конфигурации ядро и система модулей могут принимать решения, например, выполнять операции, аналогичные "скрытию".

Корневой профиль

UID, GID и группы

В системах Linux существуют два понятия: пользователи и группы. Каждый пользователь имеет идентификатор пользователя (UID), а пользователь может принадлежать к нескольким группам, каждая из которых имеет свой идентификатор группы (GID). Эти идентификаторы используются для идентификации пользователей в системе и определяют, к каким системным ресурсам они могут получить доступ.

Пользователи с UID, равным 0, называются корневыми пользователями, а группы с GID, равным 0, - корневыми группами. Группа пользователей root, как правило, обладает самыми высокими системными привилегиями.

В случае системы Android каждое приложение является отдельным пользователем (исключая сценарии с общим UID) с уникальным UID. Например, 0 представляет пользователя root, 1000 - system, 2000 - ADB shell, а UID в диапазоне от 10000 до 19999 - обычные приложения.

INFO

Здесь упомянутый UID не совпадает с концепцией нескольких пользователей или рабочих профилей в системе Android. На самом деле рабочие профили реализуются путем разделения диапазона UID. Например, 10000-19999 представляет собой основного пользователя, а 110000-119999 - рабочий профиль. Каждое обычное приложение среди них имеет свой уникальный UID.

Каждое приложение может иметь несколько групп, причем GID представляет собой основную группу, которая обычно совпадает с UID. Другие группы называются дополнительными. Определенные разрешения контролируются через группы, например, разрешения на доступ к сети или доступ к Bluetooth.

Например, если мы выполним команду id в оболочке ADB, то результат может выглядеть следующим образом:

sh
oriole:/ $ id
+uid=2000(shell) gid=2000(shell) groups=2000(shell),1004(input),1007(log),1011(adb),1015(sdcard_rw),1028(sdcard_r),1078(ext_data_rw),1079(ext_obb_rw),3001(net_bt_admin),3002(net_bt),3003(inet),3006(net_bw_stats),3009(readproc),3011(uhid),3012(readtracefs) context=u:r:shell:s0
oriole:/ $ id
+uid=2000(shell) gid=2000(shell) groups=2000(shell),1004(input),1007(log),1011(adb),1015(sdcard_rw),1028(sdcard_r),1078(ext_data_rw),1079(ext_obb_rw),3001(net_bt_admin),3002(net_bt),3003(inet),3006(net_bw_stats),3009(readproc),3011(uhid),3012(readtracefs) context=u:r:shell:s0

Здесь UID равен 2000, а GID (идентификатор основной группы) также равен 2000. Кроме того, он входит в несколько дополнительных групп, таких как inet (указывает на возможность создания сокетов AF_INET и AF_INET6) и sdcard_rw (указывает на права чтения/записи на SD-карту).

Корневой профиль KernelSU позволяет настраивать UID, GID и группы для корневого процесса после выполнения команды su. Например, в корневом профиле корневого приложения можно установить его UID на 2000, что означает, что при использовании su фактические разрешения приложения будут находиться на уровне оболочки ADB. Группа inet может быть удалена, что не позволит команде su получить доступ к сети.

Примечание

Профиль приложений контролирует только разрешения корневого процесса после использования su; он не контролирует разрешения самого приложения. Если приложение запросило разрешение на доступ к сети, оно может получить доступ к сети даже без использования su. Удаление группы inet из su только предотвращает доступ su к сети.

Корневой профиль реализуется в ядре и не зависит от добровольного поведения root-приложений, в отличие от переключения пользователей или групп через su, предоставление прав su полностью зависит от пользователя, а не от разработчика.

Привилегии

Привилегии - это механизм разделения привилегий в Linux.

В традиционных реализациях UNIX для проверки прав доступа выделяются две категории процессов: привилегированные процессы (эффективный идентификатор пользователя равен 0 и называется суперпользователем или root) и непривилегированные процессы (эффективный UID которых не равен нулю). Привилегированные процессы обходят все проверки прав ядра, в то время как непривилегированные процессы подвергаются полной проверке прав на основе учетных данных процесса (обычно: эффективный UID, эффективный GID и список дополнительных групп).

Начиная с версии Linux 2.2, в Linux привилегии, традиционно ассоциируемые с суперпользователем, разделены на отдельные единицы, называемые возможностями, которые могут быть независимо включены и выключены.

Каждая способность представляет собой одну или несколько привилегий. Например, CAP_DAC_READ_SEARCH представляет собой возможность обхода проверок прав на чтение файлов, а также прав на чтение и выполнение каталогов. Если пользователь с эффективным UID 0 (пользователь root) не имеет возможности CAP_DAC_READ_SEARCH или более высоких возможностей, это означает, что, хотя он и является пользователем root, он не может читать файлы по своему усмотрению.

Корневой профиль KernelSU позволяет настраивать возможности корневого процесса после выполнения su, тем самым добиваясь частичного предоставления "прав root". В отличие от вышеупомянутых UID и GID, некоторые root-приложения после использования su требуют UID, равный 0. В таких случаях ограничение возможностей данного root-пользователя с UID 0 может ограничить их разрешенные операции.

Настоятельная рекомендация

В документе привелегий Linux официальной документации дается подробное объяснение возможностей, представленных каждой привелегией. Если вы собираетесь настраивать привелегии, настоятельно рекомендуется сначала прочитать этот документ.

SELinux

SELinux - это мощный механизм обязательного контроля доступа (MAC). Он работает по принципу запрет по умолчанию: любое действие, не разрешенное в явном виде, запрещается.

SELinux может работать в двух глобальных режимах:

  1. Разрешительный режим: События запрета регистрируются, но не выполняются.
  2. Принудительный режим: События запрета регистрируются и выполняются.

Предупреждение

Современные системы Android в значительной степени опираются на SELinux для обеспечения общей безопасности системы. Настоятельно не рекомендуется использовать пользовательские системы, работающие в "разрешительном режиме", поскольку это не дает существенных преимуществ перед полностью открытой системой.

Объяснение полной концепции SELinux является сложным и выходит за рамки данного документа. Рекомендуется сначала разобраться в его работе с помощью следующих ресурсов:

  1. Wikipedia
  2. Red Hat: Что такое SELinux?
  3. ArchLinux: SELinux

Корневой профиль KernelSU позволяет настраивать SELinux-контекст корневого процесса после выполнения команды su. Для этого контекста могут быть заданы специальные правила управления доступом, позволяющие осуществлять тонкий контроль над правами root.

В типичных сценариях, когда приложение выполняет команду su, оно переключает процесс на домен SELinux с неограниченным доступом, например u:r:su:s0. С помощью профиля Root Profile этот домен может быть переключен на пользовательский домен, например u:r:app1:s0, и для него может быть определен ряд правил:

sh
type app1
+enforce app1
+typeattribute app1 mlstrustedsubject
+allow app1 * * *
type app1
+enforce app1
+typeattribute app1 mlstrustedsubject
+allow app1 * * *

Обратите внимание, что правило allow app1 * * * используется только в демонстрационных целях. На практике это правило не должно широко использоваться, поскольку оно мало чем отличается от разрешительного режима.

Эскалация

При неправильной настройке корневого профиля может возникнуть сценарий эскалации: ограничения, накладываемые корневым профилем, могут непреднамеренно не сработать.

Например, если предоставить права root пользователю ADB shell (что является обычным случаем), а затем предоставить права root обычному приложению, но настроить его профиль root с UID 2000 (это UID пользователя ADB shell), то приложение может получить полный доступ root, выполнив команду su дважды:

  1. При первом выполнении команды su будет применен профиль App Profile и произойдет переход на UID 2000 (adb shell) вместо 0 (root).
  2. При втором выполнении команды su, поскольку UID равен 2000, а в конфигурации вы предоставили доступ root к UID 2000 (adb shell), приложение получит полные привилегии root.

Примечание

Такое поведение вполне ожидаемо и не является ошибкой. Поэтому мы рекомендуем следующее:

Если вам действительно необходимо предоставить права root в ADB (например, как разработчику), не рекомендуется изменять UID на 2000 при настройке корневого профиля. Лучше использовать 1000 (система).

Некорневой профиль

Размонтирование модулей

KernelSU предоставляет бессистемный механизм модификации системных разделов, реализуемый через монтирование overlayfs. Однако некоторые приложения могут быть чувствительны к такому поведению. Поэтому мы можем выгрузить модули, смонтированные в этих приложениях, установив опцию "размонтирование модулей".

Кроме того, в интерфейсе настроек менеджера KernelSU имеется переключатель "размонтирование модулей по умолчанию". По умолчанию этот переключатель включен, что означает, что KernelSU или некоторые модули будут выгружать модули для данного приложения, если не будут применены дополнительные настройки. Если вам не нравится эта настройка или если она влияет на определенные приложения, у вас есть следующие возможности:

  1. Оставить переключатель "размонтирование модулей по умолчанию" и индивидуально отключить опцию "размонтирование модулей" в профиле приложений для приложений, требующих загрузки модулей (действует как "белый список").
  2. Отключить переключатель "размонтирование модулей по умолчанию" и индивидуально включить опцию "размонтирование модулей" в App Profile для приложений, требующих выгрузки модулей (действует как "черный список").

INFO

В устройствах, использующих ядро версии 5.10 и выше, выгрузку модулей выполняет само ядро. Однако для устройств с ядром версии ниже 5.10 этот переключатель является лишь опцией конфигурации, и KernelSU сам по себе не предпринимает никаких действий. Некоторые модули, например, Zygisksu, могут использовать этот переключатель для определения необходимости выгрузки модулей.

`,46),e=[n];function t(c,r,d,E,i,y){return o(),a("div",null,e)}const h=s(p,[["render",t]]);export{F as __pageData,h as default}; diff --git a/assets/ru_RU_guide_app-profile.md.084a5687.lean.js b/assets/ru_RU_guide_app-profile.md.084a5687.lean.js new file mode 100644 index 000000000000..b7453e970567 --- /dev/null +++ b/assets/ru_RU_guide_app-profile.md.084a5687.lean.js @@ -0,0 +1 @@ +import{_ as s,o,c as a,Q as l}from"./chunks/framework.ec8f7e8e.js";const F=JSON.parse('{"title":"Профиль приложений","description":"","frontmatter":{},"headers":[],"relativePath":"ru_RU/guide/app-profile.md","filePath":"ru_RU/guide/app-profile.md"}'),p={name:"ru_RU/guide/app-profile.md"},n=l("",46),e=[n];function t(c,r,d,E,i,y){return o(),a("div",null,e)}const h=s(p,[["render",t]]);export{F as __pageData,h as default}; diff --git a/assets/ru_RU_guide_app-profile.md.8936210e.js b/assets/ru_RU_guide_app-profile.md.8936210e.js deleted file mode 100644 index f60dd3dc8bad..000000000000 --- a/assets/ru_RU_guide_app-profile.md.8936210e.js +++ /dev/null @@ -1,5 +0,0 @@ -import{_ as o,o as s,c as e,O as a}from"./chunks/framework.43781440.js";const y=JSON.parse('{"title":"Профиль приложений","description":"","frontmatter":{},"headers":[],"relativePath":"ru_RU/guide/app-profile.md","filePath":"ru_RU/guide/app-profile.md"}'),l={name:"ru_RU/guide/app-profile.md"},p=a(`

Профиль приложений

Профиль приложений - это механизм, предоставляемый KernelSU для настройки конфигурации различных приложений.

Для приложений, получивших права root (т.е. имеющих возможность использовать su), App Profile может также называться Root Profile. Он позволяет настраивать правила uid, gid, groups, capabilities и SELinux команды su, тем самым ограничивая привилегии пользователя root. Например, она может предоставлять сетевые права только приложениям межсетевого экрана, отказывая в праве доступа к файлам, или предоставлять права shell вместо полного root-доступа для приложений freeze: *сохранение власти в рамках принципа наименьших привилегий.

Для обычных приложений, не имеющих прав root, App Profile может управлять поведением ядра и системы модулей по отношению к этим приложениям. Например, он может определять, следует ли обращать внимание на модификации, возникающие в результате работы модулей. На основе этой конфигурации ядро и система модулей могут принимать решения, например, выполнять операции, аналогичные "скрытию".

Корневой профиль

UID, GID и группы

В системах Linux существуют два понятия: пользователи и группы. Каждый пользователь имеет идентификатор пользователя (UID), а пользователь может принадлежать к нескольким группам, каждая из которых имеет свой идентификатор группы (GID). Эти идентификаторы используются для идентификации пользователей в системе и определяют, к каким системным ресурсам они могут получить доступ.

Пользователи с UID, равным 0, называются корневыми пользователями, а группы с GID, равным 0, - корневыми группами. Группа пользователей root, как правило, обладает самыми высокими системными привилегиями.

В случае системы Android каждое приложение является отдельным пользователем (исключая сценарии с общим UID) с уникальным UID. Например, 0 представляет пользователя root, 1000 - system, 2000 - ADB shell, а UID в диапазоне от 10000 до 19999 - обычные приложения.

INFO

Здесь упомянутый UID не совпадает с концепцией нескольких пользователей или рабочих профилей в системе Android. На самом деле рабочие профили реализуются путем разделения диапазона UID. Например, 10000-19999 представляет собой основного пользователя, а 110000-119999 - рабочий профиль. Каждое обычное приложение среди них имеет свой уникальный UID.

Каждое приложение может иметь несколько групп, причем GID представляет собой основную группу, которая обычно совпадает с UID. Другие группы называются дополнительными. Определенные разрешения контролируются через группы, например, разрешения на доступ к сети или доступ к Bluetooth.

Например, если мы выполним команду id в оболочке ADB, то результат может выглядеть следующим образом:

sh
oriole:/ $ id
-uid=2000(shell) gid=2000(shell) groups=2000(shell),1004(input),1007(log),1011(adb),1015(sdcard_rw),1028(sdcard_r),1078(ext_data_rw),1079(ext_obb_rw),3001(net_bt_admin),3002(net_bt),3003(inet),3006(net_bw_stats),3009(readproc),3011(uhid),3012(readtracefs) context=u:r:shell:s0

Здесь UID равен 2000, а GID (идентификатор основной группы) также равен 2000. Кроме того, он входит в несколько дополнительных групп, таких как inet (указывает на возможность создания сокетов AF_INET и AF_INET6) и sdcard_rw (указывает на права чтения/записи на SD-карту).

Корневой профиль KernelSU позволяет настраивать UID, GID и группы для корневого процесса после выполнения команды su. Например, в корневом профиле корневого приложения можно установить его UID на 2000, что означает, что при использовании su фактические разрешения приложения будут находиться на уровне оболочки ADB. Группа inet может быть удалена, что не позволит команде su получить доступ к сети.

Примечание

Профиль приложений контролирует только разрешения корневого процесса после использования su; он не контролирует разрешения самого приложения. Если приложение запросило разрешение на доступ к сети, оно может получить доступ к сети даже без использования su. Удаление группы inet из su только предотвращает доступ su к сети.

Корневой профиль реализуется в ядре и не зависит от добровольного поведения root-приложений, в отличие от переключения пользователей или групп через su, предоставление прав su полностью зависит от пользователя, а не от разработчика.

Привилегии

Привилегии - это механизм разделения привилегий в Linux.

В традиционных реализациях UNIX для проверки прав доступа выделяются две категории процессов: привилегированные процессы (эффективный идентификатор пользователя равен 0 и называется суперпользователем или root) и непривилегированные процессы (эффективный UID которых не равен нулю). Привилегированные процессы обходят все проверки прав ядра, в то время как непривилегированные процессы подвергаются полной проверке прав на основе учетных данных процесса (обычно: эффективный UID, эффективный GID и список дополнительных групп).

Начиная с версии Linux 2.2, в Linux привилегии, традиционно ассоциируемые с суперпользователем, разделены на отдельные единицы, называемые возможностями, которые могут быть независимо включены и выключены.

Каждая способность представляет собой одну или несколько привилегий. Например, CAP_DAC_READ_SEARCH представляет собой возможность обхода проверок прав на чтение файлов, а также прав на чтение и выполнение каталогов. Если пользователь с эффективным UID 0 (пользователь root) не имеет возможности CAP_DAC_READ_SEARCH или более высоких возможностей, это означает, что, хотя он и является пользователем root, он не может читать файлы по своему усмотрению.

Корневой профиль KernelSU позволяет настраивать возможности корневого процесса после выполнения su, тем самым добиваясь частичного предоставления "прав root". В отличие от вышеупомянутых UID и GID, некоторые root-приложения после использования su требуют UID, равный 0. В таких случаях ограничение возможностей данного root-пользователя с UID 0 может ограничить их разрешенные операции.

Настоятельная рекомендация

В документе привелегий Linux официальной документации дается подробное объяснение возможностей, представленных каждой привелегией. Если вы собираетесь настраивать привелегии, настоятельно рекомендуется сначала прочитать этот документ.

SELinux

SELinux - это мощный механизм обязательного контроля доступа (MAC). Он работает по принципу запрет по умолчанию: любое действие, не разрешенное в явном виде, запрещается.

SELinux может работать в двух глобальных режимах:

  1. Разрешительный режим: События запрета регистрируются, но не выполняются.
  2. Принудительный режим: События запрета регистрируются и выполняются.

Предупреждение

Современные системы Android в значительной степени опираются на SELinux для обеспечения общей безопасности системы. Настоятельно не рекомендуется использовать пользовательские системы, работающие в "разрешительном режиме", поскольку это не дает существенных преимуществ перед полностью открытой системой.

Объяснение полной концепции SELinux является сложным и выходит за рамки данного документа. Рекомендуется сначала разобраться в его работе с помощью следующих ресурсов:

  1. Wikipedia
  2. Red Hat: Что такое SELinux?
  3. ArchLinux: SELinux

Корневой профиль KernelSU позволяет настраивать SELinux-контекст корневого процесса после выполнения команды su. Для этого контекста могут быть заданы специальные правила управления доступом, позволяющие осуществлять тонкий контроль над правами root.

В типичных сценариях, когда приложение выполняет команду su, оно переключает процесс на домен SELinux с неограниченным доступом, например u:r:su:s0. С помощью профиля Root Profile этот домен может быть переключен на пользовательский домен, например u:r:app1:s0, и для него может быть определен ряд правил:

sh
type app1
-enforce app1
-typeattribute app1 mlstrustedsubject
-allow app1 * * *

Обратите внимание, что правило allow app1 * * * используется только в демонстрационных целях. На практике это правило не должно широко использоваться, поскольку оно мало чем отличается от разрешительного режима.

Эскалация

При неправильной настройке корневого профиля может возникнуть сценарий эскалации: ограничения, накладываемые корневым профилем, могут непреднамеренно не сработать.

Например, если предоставить права root пользователю ADB shell (что является обычным случаем), а затем предоставить права root обычному приложению, но настроить его профиль root с UID 2000 (это UID пользователя ADB shell), то приложение может получить полный доступ root, выполнив команду su дважды:

  1. При первом выполнении команды su будет применен профиль App Profile и произойдет переход на UID 2000 (adb shell) вместо 0 (root).
  2. При втором выполнении команды su, поскольку UID равен 2000, а в конфигурации вы предоставили доступ root к UID 2000 (adb shell), приложение получит полные привилегии root.

Примечание

Такое поведение вполне ожидаемо и не является ошибкой. Поэтому мы рекомендуем следующее:

Если вам действительно необходимо предоставить права root в ADB (например, как разработчику), не рекомендуется изменять UID на 2000 при настройке корневого профиля. Лучше использовать 1000 (система).

Некорневой профиль

Размонтирование модулей

KernelSU предоставляет бессистемный механизм модификации системных разделов, реализуемый через монтирование overlayfs. Однако некоторые приложения могут быть чувствительны к такому поведению. Поэтому мы можем выгрузить модули, смонтированные в этих приложениях, установив опцию "размонтирование модулей".

Кроме того, в интерфейсе настроек менеджера KernelSU имеется переключатель "размонтирование модулей по умолчанию". По умолчанию этот переключатель включен, что означает, что KernelSU или некоторые модули будут выгружать модули для данного приложения, если не будут применены дополнительные настройки. Если вам не нравится эта настройка или если она влияет на определенные приложения, у вас есть следующие возможности:

  1. Оставить переключатель "размонтирование модулей по умолчанию" и индивидуально отключить опцию "размонтирование модулей" в профиле приложений для приложений, требующих загрузки модулей (действует как "белый список").
  2. Отключить переключатель "размонтирование модулей по умолчанию" и индивидуально включить опцию "размонтирование модулей" в App Profile для приложений, требующих выгрузки модулей (действует как "черный список").

INFO

В устройствах, использующих ядро версии 5.10 и выше, выгрузку модулей выполняет само ядро. Однако для устройств с ядром версии ниже 5.10 этот переключатель является лишь опцией конфигурации, и KernelSU сам по себе не предпринимает никаких действий. Некоторые модули, например, Zygisksu, могут использовать этот переключатель для определения необходимости выгрузки модулей.

`,46),n=[p];function t(c,r,d,i,D,u){return s(),e("div",null,n)}const C=o(l,[["render",t]]);export{y as __pageData,C as default}; diff --git a/assets/ru_RU_guide_app-profile.md.8936210e.lean.js b/assets/ru_RU_guide_app-profile.md.8936210e.lean.js deleted file mode 100644 index 066cc24bb319..000000000000 --- a/assets/ru_RU_guide_app-profile.md.8936210e.lean.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as o,o as s,c as e,O as a}from"./chunks/framework.43781440.js";const y=JSON.parse('{"title":"Профиль приложений","description":"","frontmatter":{},"headers":[],"relativePath":"ru_RU/guide/app-profile.md","filePath":"ru_RU/guide/app-profile.md"}'),l={name:"ru_RU/guide/app-profile.md"},p=a("",46),n=[p];function t(c,r,d,i,D,u){return s(),e("div",null,n)}const C=o(l,[["render",t]]);export{y as __pageData,C as default}; diff --git a/assets/ru_RU_guide_difference-with-magisk.md.d55db77b.js b/assets/ru_RU_guide_difference-with-magisk.md.928ea8b2.js similarity index 97% rename from assets/ru_RU_guide_difference-with-magisk.md.d55db77b.js rename to assets/ru_RU_guide_difference-with-magisk.md.928ea8b2.js index 5dc3c492aa58..f2c5cc270148 100644 --- a/assets/ru_RU_guide_difference-with-magisk.md.d55db77b.js +++ b/assets/ru_RU_guide_difference-with-magisk.md.928ea8b2.js @@ -1 +1 @@ -import{_ as e,o as i,c as a,O as s}from"./chunks/framework.43781440.js";const m=JSON.parse('{"title":"Различия с Magisk","description":"","frontmatter":{},"headers":[],"relativePath":"ru_RU/guide/difference-with-magisk.md","filePath":"ru_RU/guide/difference-with-magisk.md"}'),o={name:"ru_RU/guide/difference-with-magisk.md"},t=s('

Различия с Magisk

Несмотря на большое количество сходств между модулями KernelSU и модулями Magisk, неизбежно возникают и различия, обусловленные совершенно разными механизмами их реализации. Если вы хотите, чтобы ваш модуль работал как на Magisk, так и на KernelSU, вы должны понимать эти различия.

Сходства

  • Формат файлов модулей: оба используют формат zip для организации модулей, и формат модулей практически одинаков
  • Каталог установки модулей: оба расположены в /data/adb/modules.
  • Бессистемность: оба поддерживают модификацию /system бессистемным способом через модули
  • post-fs-data.sh: время выполнения и семантика полностью совпадают
  • service.sh: время выполнения и семантика полностью совпадают
  • system.prop: полностью совпадает
  • sepolicy.rule: полностью совпадает
  • BusyBox: скрипты запускаются в BusyBox с включенным "автономным режимом" в обоих случаях

Различия

Прежде чем разбираться в различиях, необходимо знать, как отличить, в каком режиме работает ваш модуль - KernelSU или Magisk. Для этого можно использовать переменную окружения KSU во всех местах, где можно запустить скрипты модуля (customize.sh, post-fs-data.sh, service.sh). В KernelSU эта переменная окружения будет установлена в значение true.

Вот некоторые отличия:

  • Модули KernelSU не могут быть установлены в режиме Recovery.
  • Модули KernelSU не имеют встроенной поддержки Zygisk (но вы можете использовать модули Zygisk через ZygiskNext.
  • Метод замены или удаления файлов в модулях KernelSU полностью отличается от Magisk. KernelSU не поддерживает метод .replace. Вместо этого необходимо создать одноименный файл с помощью команды mknod filename c 0 0 для удаления соответствующего файла.
  • Каталоги для BusyBox отличаются. Встроенный BusyBox в KernelSU находится в каталоге /data/adb/ksu/bin/busybox, а в Magisk - в каталоге /data/adb/magisk/busybox. Обратите внимание, что это внутреннее поведение KernelSU и в будущем оно может измениться!
  • KernelSU не поддерживает файлы .replace; однако KernelSU поддерживает переменные REMOVE и REPLACE для удаления или замены файлов и папок.
',8),l=[t];function r(d,c,n,u,h,_){return i(),a("div",null,l)}const p=e(o,[["render",r]]);export{m as __pageData,p as default}; +import{_ as e,o as i,c as a,Q as s}from"./chunks/framework.ec8f7e8e.js";const m=JSON.parse('{"title":"Различия с Magisk","description":"","frontmatter":{},"headers":[],"relativePath":"ru_RU/guide/difference-with-magisk.md","filePath":"ru_RU/guide/difference-with-magisk.md"}'),o={name:"ru_RU/guide/difference-with-magisk.md"},t=s('

Различия с Magisk

Несмотря на большое количество сходств между модулями KernelSU и модулями Magisk, неизбежно возникают и различия, обусловленные совершенно разными механизмами их реализации. Если вы хотите, чтобы ваш модуль работал как на Magisk, так и на KernelSU, вы должны понимать эти различия.

Сходства

  • Формат файлов модулей: оба используют формат zip для организации модулей, и формат модулей практически одинаков
  • Каталог установки модулей: оба расположены в /data/adb/modules.
  • Бессистемность: оба поддерживают модификацию /system бессистемным способом через модули
  • post-fs-data.sh: время выполнения и семантика полностью совпадают
  • service.sh: время выполнения и семантика полностью совпадают
  • system.prop: полностью совпадает
  • sepolicy.rule: полностью совпадает
  • BusyBox: скрипты запускаются в BusyBox с включенным "автономным режимом" в обоих случаях

Различия

Прежде чем разбираться в различиях, необходимо знать, как отличить, в каком режиме работает ваш модуль - KernelSU или Magisk. Для этого можно использовать переменную окружения KSU во всех местах, где можно запустить скрипты модуля (customize.sh, post-fs-data.sh, service.sh). В KernelSU эта переменная окружения будет установлена в значение true.

Вот некоторые отличия:

  • Модули KernelSU не могут быть установлены в режиме Recovery.
  • Модули KernelSU не имеют встроенной поддержки Zygisk (но вы можете использовать модули Zygisk через ZygiskNext.
  • Метод замены или удаления файлов в модулях KernelSU полностью отличается от Magisk. KernelSU не поддерживает метод .replace. Вместо этого необходимо создать одноименный файл с помощью команды mknod filename c 0 0 для удаления соответствующего файла.
  • Каталоги для BusyBox отличаются. Встроенный BusyBox в KernelSU находится в каталоге /data/adb/ksu/bin/busybox, а в Magisk - в каталоге /data/adb/magisk/busybox. Обратите внимание, что это внутреннее поведение KernelSU и в будущем оно может измениться!
  • KernelSU не поддерживает файлы .replace; однако KernelSU поддерживает переменные REMOVE и REPLACE для удаления или замены файлов и папок.
',8),l=[t];function r(d,c,n,u,h,_){return i(),a("div",null,l)}const p=e(o,[["render",r]]);export{m as __pageData,p as default}; diff --git a/assets/ru_RU_guide_difference-with-magisk.md.d55db77b.lean.js b/assets/ru_RU_guide_difference-with-magisk.md.928ea8b2.lean.js similarity index 72% rename from assets/ru_RU_guide_difference-with-magisk.md.d55db77b.lean.js rename to assets/ru_RU_guide_difference-with-magisk.md.928ea8b2.lean.js index 1f83c4c1e301..fd23040023e1 100644 --- a/assets/ru_RU_guide_difference-with-magisk.md.d55db77b.lean.js +++ b/assets/ru_RU_guide_difference-with-magisk.md.928ea8b2.lean.js @@ -1 +1 @@ -import{_ as e,o as i,c as a,O as s}from"./chunks/framework.43781440.js";const m=JSON.parse('{"title":"Различия с Magisk","description":"","frontmatter":{},"headers":[],"relativePath":"ru_RU/guide/difference-with-magisk.md","filePath":"ru_RU/guide/difference-with-magisk.md"}'),o={name:"ru_RU/guide/difference-with-magisk.md"},t=s("",8),l=[t];function r(d,c,n,u,h,_){return i(),a("div",null,l)}const p=e(o,[["render",r]]);export{m as __pageData,p as default}; +import{_ as e,o as i,c as a,Q as s}from"./chunks/framework.ec8f7e8e.js";const m=JSON.parse('{"title":"Различия с Magisk","description":"","frontmatter":{},"headers":[],"relativePath":"ru_RU/guide/difference-with-magisk.md","filePath":"ru_RU/guide/difference-with-magisk.md"}'),o={name:"ru_RU/guide/difference-with-magisk.md"},t=s("",8),l=[t];function r(d,c,n,u,h,_){return i(),a("div",null,l)}const p=e(o,[["render",r]]);export{m as __pageData,p as default}; diff --git a/assets/ru_RU_guide_faq.md.e0aa7004.js b/assets/ru_RU_guide_faq.md.1d543e4d.js similarity index 98% rename from assets/ru_RU_guide_faq.md.e0aa7004.js rename to assets/ru_RU_guide_faq.md.1d543e4d.js index 0e6dad494307..facde29d2471 100644 --- a/assets/ru_RU_guide_faq.md.e0aa7004.js +++ b/assets/ru_RU_guide_faq.md.1d543e4d.js @@ -1 +1 @@ -import{_ as e,o as a,c as r,O as n}from"./chunks/framework.43781440.js";const m=JSON.parse('{"title":"FAQ","description":"","frontmatter":{},"headers":[],"relativePath":"ru_RU/guide/faq.md","filePath":"ru_RU/guide/faq.md"}'),l={name:"ru_RU/guide/faq.md"},o=n('

FAQ

Поддерживает ли KernelSU мое устройство?

Во-первых, ваше устройство должно быть способно разблокировать загрузчик. Если не может, значит, устройство не поддерживается.

Затем установите на устройство приложение KernelSU manager App и откройте его, если оно покажет Unsupported, то ваше устройство не поддерживается из коробки, но вы можете собрать исходный код ядра и интегрировать KernelSU, чтобы заставить его работать, или использовать неофициально-поддерживаемые-устройства.

Нужно ли для KernelSU разблокировать загрузчик?

Безусловно, да.

Поддерживает ли KernelSU модули?

Да, но это ранняя версия, она может быть глючной. Пожалуйста, подождите, пока она станет стабильной 😃

Поддерживает ли KernelSU Xposed?

Да, Dreamland и TaiChi работают. Что касается LSPosed, то его можно заставить работать с помощью Zygisk на KernelSU

Поддерживает ли KernelSU Zygisk?

KernelSU не имеет встроенной поддержки Zygisk, но вы можете использовать Zygisk на KernelSU.

Совместим ли KernelSU с Magisk?

Система модулей KernelSU конфликтует с магическим монтированием Magisk, если в KernelSU включен какой-либо модуль, то весь Magisk не будет работать.

Но если вы используете только su из KernelSU, то он будет хорошо работать с Magisk: KernelSU модифицирует kernel, а Magisk - ramdisk, они могут работать вместе.

Заменит ли KernelSU Magisk?

Мы так не считаем, и это не является нашей целью. Magisk достаточно хорош для решения проблемы root в пользовательском пространстве и будет жить долго. Цель KernelSU - предоставить пользователям интерфейс ядра, а не заменить Magisk.

Может ли KernelSU поддерживать устройства, не относящиеся к GKI?

Это возможно. Но для этого необходимо скачать исходный текст ядра, подключить KernelSU к дереву исходных текстов и скомпилировать ядро самостоятельно.

Может ли KernelSU поддерживать устройства под управлением Android 12?

На совместимость KernelSU влияет ядро устройства, и версия Android здесь ни при чем. Единственное ограничение - устройства, запускаемые с Android 12, должны иметь ядро 5.10+ (устройства GKI). Итак:

  1. Устройства, выпущенные под управлением Android 12, должны поддерживаться.
  2. Устройства со старым ядром (некоторые устройства с Android 12 также имеют старое ядро) совместимы (Вы должны собрать ядро самостоятельно).

Может ли KernelSU поддерживать старое ядро?

Это возможно, KernelSU бэкпортирован на ядро 4.14, для более старых ядер, вам нужно сделать бэкпорт вручную, и PR приветствуются!

Как интегрировать KernelSU в старое ядро?

Пожалуйста, обратитесь к руководству

Почему моя версия Android - 13, а ядро показывает "android12-5.10"?

Версия ядра не имеет никакого отношения к версии Android, если вам нужно прошить ядро, всегда используйте версию ядра, версия Android не так важна.

Есть ли в KernelSU пространство имен --mount-master/global mount?

Сейчас нет (возможно, в будущем), но есть много способов переключиться на глобальное пространство имен монтирования вручную, например:

  1. nsenter -t 1 -m sh для получения оболочки в глобальном пространстве имен монтирования.
  2. Добавить nsenter --mount=/proc/1/ns/mnt к команде, которую вы хотите выполнить, тогда команда будет выполнена в глобальном пространстве имен монтирования. KernelSU также использует этот способ

Я GKI1.0, могу ли я использовать это?

GKI1 полностью отличается от GKI2, вы должны скомпилировать ядро самостоятельно.

',33),t=[o];function i(s,d,h,u,k,c){return a(),r("div",null,t)}const g=e(l,[["render",i]]);export{m as __pageData,g as default}; +import{_ as e,o as a,c as r,Q as n}from"./chunks/framework.ec8f7e8e.js";const m=JSON.parse('{"title":"FAQ","description":"","frontmatter":{},"headers":[],"relativePath":"ru_RU/guide/faq.md","filePath":"ru_RU/guide/faq.md"}'),l={name:"ru_RU/guide/faq.md"},o=n('

FAQ

Поддерживает ли KernelSU мое устройство?

Во-первых, ваше устройство должно быть способно разблокировать загрузчик. Если не может, значит, устройство не поддерживается.

Затем установите на устройство приложение KernelSU manager App и откройте его, если оно покажет Unsupported, то ваше устройство не поддерживается из коробки, но вы можете собрать исходный код ядра и интегрировать KernelSU, чтобы заставить его работать, или использовать неофициально-поддерживаемые-устройства.

Нужно ли для KernelSU разблокировать загрузчик?

Безусловно, да.

Поддерживает ли KernelSU модули?

Да, но это ранняя версия, она может быть глючной. Пожалуйста, подождите, пока она станет стабильной 😃

Поддерживает ли KernelSU Xposed?

Да, Dreamland и TaiChi работают. Что касается LSPosed, то его можно заставить работать с помощью Zygisk на KernelSU

Поддерживает ли KernelSU Zygisk?

KernelSU не имеет встроенной поддержки Zygisk, но вы можете использовать Zygisk на KernelSU.

Совместим ли KernelSU с Magisk?

Система модулей KernelSU конфликтует с магическим монтированием Magisk, если в KernelSU включен какой-либо модуль, то весь Magisk не будет работать.

Но если вы используете только su из KernelSU, то он будет хорошо работать с Magisk: KernelSU модифицирует kernel, а Magisk - ramdisk, они могут работать вместе.

Заменит ли KernelSU Magisk?

Мы так не считаем, и это не является нашей целью. Magisk достаточно хорош для решения проблемы root в пользовательском пространстве и будет жить долго. Цель KernelSU - предоставить пользователям интерфейс ядра, а не заменить Magisk.

Может ли KernelSU поддерживать устройства, не относящиеся к GKI?

Это возможно. Но для этого необходимо скачать исходный текст ядра, подключить KernelSU к дереву исходных текстов и скомпилировать ядро самостоятельно.

Может ли KernelSU поддерживать устройства под управлением Android 12?

На совместимость KernelSU влияет ядро устройства, и версия Android здесь ни при чем. Единственное ограничение - устройства, запускаемые с Android 12, должны иметь ядро 5.10+ (устройства GKI). Итак:

  1. Устройства, выпущенные под управлением Android 12, должны поддерживаться.
  2. Устройства со старым ядром (некоторые устройства с Android 12 также имеют старое ядро) совместимы (Вы должны собрать ядро самостоятельно).

Может ли KernelSU поддерживать старое ядро?

Это возможно, KernelSU бэкпортирован на ядро 4.14, для более старых ядер, вам нужно сделать бэкпорт вручную, и PR приветствуются!

Как интегрировать KernelSU в старое ядро?

Пожалуйста, обратитесь к руководству

Почему моя версия Android - 13, а ядро показывает "android12-5.10"?

Версия ядра не имеет никакого отношения к версии Android, если вам нужно прошить ядро, всегда используйте версию ядра, версия Android не так важна.

Есть ли в KernelSU пространство имен --mount-master/global mount?

Сейчас нет (возможно, в будущем), но есть много способов переключиться на глобальное пространство имен монтирования вручную, например:

  1. nsenter -t 1 -m sh для получения оболочки в глобальном пространстве имен монтирования.
  2. Добавить nsenter --mount=/proc/1/ns/mnt к команде, которую вы хотите выполнить, тогда команда будет выполнена в глобальном пространстве имен монтирования. KernelSU также использует этот способ

Я GKI1.0, могу ли я использовать это?

GKI1 полностью отличается от GKI2, вы должны скомпилировать ядро самостоятельно.

',33),t=[o];function i(s,d,h,u,k,c){return a(),r("div",null,t)}const g=e(l,[["render",i]]);export{m as __pageData,g as default}; diff --git a/assets/ru_RU_guide_faq.md.e0aa7004.lean.js b/assets/ru_RU_guide_faq.md.1d543e4d.lean.js similarity index 67% rename from assets/ru_RU_guide_faq.md.e0aa7004.lean.js rename to assets/ru_RU_guide_faq.md.1d543e4d.lean.js index c7102e773fd1..a7e6705f9047 100644 --- a/assets/ru_RU_guide_faq.md.e0aa7004.lean.js +++ b/assets/ru_RU_guide_faq.md.1d543e4d.lean.js @@ -1 +1 @@ -import{_ as e,o as a,c as r,O as n}from"./chunks/framework.43781440.js";const m=JSON.parse('{"title":"FAQ","description":"","frontmatter":{},"headers":[],"relativePath":"ru_RU/guide/faq.md","filePath":"ru_RU/guide/faq.md"}'),l={name:"ru_RU/guide/faq.md"},o=n("",33),t=[o];function i(s,d,h,u,k,c){return a(),r("div",null,t)}const g=e(l,[["render",i]]);export{m as __pageData,g as default}; +import{_ as e,o as a,c as r,Q as n}from"./chunks/framework.ec8f7e8e.js";const m=JSON.parse('{"title":"FAQ","description":"","frontmatter":{},"headers":[],"relativePath":"ru_RU/guide/faq.md","filePath":"ru_RU/guide/faq.md"}'),l={name:"ru_RU/guide/faq.md"},o=n("",33),t=[o];function i(s,d,h,u,k,c){return a(),r("div",null,t)}const g=e(l,[["render",i]]);export{m as __pageData,g as default}; diff --git a/assets/ru_RU_guide_hidden-features.md.395803e0.js b/assets/ru_RU_guide_hidden-features.md.6c681acf.js similarity index 88% rename from assets/ru_RU_guide_hidden-features.md.395803e0.js rename to assets/ru_RU_guide_hidden-features.md.6c681acf.js index 5708d8eede84..4fbce7ca64ad 100644 --- a/assets/ru_RU_guide_hidden-features.md.395803e0.js +++ b/assets/ru_RU_guide_hidden-features.md.6c681acf.js @@ -1 +1 @@ -import{_ as e,o as a,c as t,O as r}from"./chunks/framework.43781440.js";const m=JSON.parse('{"title":"Скрытые возможности","description":"","frontmatter":{},"headers":[],"relativePath":"ru_RU/guide/hidden-features.md","filePath":"ru_RU/guide/hidden-features.md"}'),s={name:"ru_RU/guide/hidden-features.md"},d=r('

Скрытые возможности

.ksurc

По умолчанию /system/bin/sh загружает /system/etc/mkshrc.

Вы можете заставить su загружать пользовательский rc-файл, создав файл /data/adb/ksu/.ksurc.

',4),c=[d];function o(_,i,n,u,h,l){return a(),t("div",null,c)}const f=e(s,[["render",o]]);export{m as __pageData,f as default}; +import{_ as e,o as a,c as t,Q as r}from"./chunks/framework.ec8f7e8e.js";const m=JSON.parse('{"title":"Скрытые возможности","description":"","frontmatter":{},"headers":[],"relativePath":"ru_RU/guide/hidden-features.md","filePath":"ru_RU/guide/hidden-features.md"}'),s={name:"ru_RU/guide/hidden-features.md"},d=r('

Скрытые возможности

.ksurc

По умолчанию /system/bin/sh загружает /system/etc/mkshrc.

Вы можете заставить su загружать пользовательский rc-файл, создав файл /data/adb/ksu/.ksurc.

',4),c=[d];function o(_,i,n,u,h,l){return a(),t("div",null,c)}const f=e(s,[["render",o]]);export{m as __pageData,f as default}; diff --git a/assets/ru_RU_guide_hidden-features.md.395803e0.lean.js b/assets/ru_RU_guide_hidden-features.md.6c681acf.lean.js similarity index 72% rename from assets/ru_RU_guide_hidden-features.md.395803e0.lean.js rename to assets/ru_RU_guide_hidden-features.md.6c681acf.lean.js index 781d2b96c456..e903dc2d8e19 100644 --- a/assets/ru_RU_guide_hidden-features.md.395803e0.lean.js +++ b/assets/ru_RU_guide_hidden-features.md.6c681acf.lean.js @@ -1 +1 @@ -import{_ as e,o as a,c as t,O as r}from"./chunks/framework.43781440.js";const m=JSON.parse('{"title":"Скрытые возможности","description":"","frontmatter":{},"headers":[],"relativePath":"ru_RU/guide/hidden-features.md","filePath":"ru_RU/guide/hidden-features.md"}'),s={name:"ru_RU/guide/hidden-features.md"},d=r("",4),c=[d];function o(_,i,n,u,h,l){return a(),t("div",null,c)}const f=e(s,[["render",o]]);export{m as __pageData,f as default}; +import{_ as e,o as a,c as t,Q as r}from"./chunks/framework.ec8f7e8e.js";const m=JSON.parse('{"title":"Скрытые возможности","description":"","frontmatter":{},"headers":[],"relativePath":"ru_RU/guide/hidden-features.md","filePath":"ru_RU/guide/hidden-features.md"}'),s={name:"ru_RU/guide/hidden-features.md"},d=r("",4),c=[d];function o(_,i,n,u,h,l){return a(),t("div",null,c)}const f=e(s,[["render",o]]);export{m as __pageData,f as default}; diff --git a/assets/ru_RU_guide_how-to-build.md.1b3dd3fb.js b/assets/ru_RU_guide_how-to-build.md.1b3dd3fb.js deleted file mode 100644 index a086b9f5bee6..000000000000 --- a/assets/ru_RU_guide_how-to-build.md.1b3dd3fb.js +++ /dev/null @@ -1,4 +0,0 @@ -import{_ as s,o as a,c as l,O as e}from"./chunks/framework.43781440.js";const u=JSON.parse('{"title":"Как собрать KernelSU?","description":"","frontmatter":{},"headers":[],"relativePath":"ru_RU/guide/how-to-build.md","filePath":"ru_RU/guide/how-to-build.md"}'),n={name:"ru_RU/guide/how-to-build.md"},o=e(`

Как собрать KernelSU?

Прежде всего, необходимо ознакомиться с официальной документацией Android по сборке ядра:

  1. Сборка ядер
  2. Сборки релизов GKI

WARNING

Эта страница предназначена для устройств GKI, если вы используете старое ядро, пожалуйста, обратитесь к Как интегрировать KernelSU для не GKI ядер?.

Сборка ядра

Синхронизация исходного кода ядра

sh
repo init -u https://android.googlesource.com/kernel/manifest
-mv <kernel_manifest.xml> .repo/manifests
-repo init -m manifest.xml
-repo sync

Файл <kernel_manifest.xml> - это файл манифеста, который может однозначно определять сборку, с его помощью можно выполнить пересборку. Файл манифеста следует загрузить с сайта Сборки релизов Google GKI

Построение

Пожалуйста, сначала ознакомьтесь с официальной документацией.

Например, нам необходимо собрать образ ядра aarch64:

sh
LTO=thin BUILD_CONFIG=common/build.config.gki.aarch64 build/build.sh

Не забудьте добавить флаг LTO=thin, иначе сборка может завершиться неудачей, если память вашего компьютера меньше 24 Гб.

Начиная с Android 13, сборка ядра осуществляется с помощью bazel:

sh
tools/bazel build --config=fast //common:kernel_aarch64_dist

Сборка ядра с помощью KernelSU

Если вы успешно собрали ядро, то собрать KernelSU очень просто, выберите любой запуск в корневом каталоге исходного кода ядра:

  • Последний тэг(стабильный)
sh
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -
  • Основная ветвь(разработка)
sh
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -s main
  • Выбранный тэг(Например, версия v0.5.2)
sh
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -s v0.5.2

А затем пересоберите ядро и получите образ ядра с KernelSU!

`,24),p=[o];function t(r,c,i,C,d,h){return a(),l("div",null,p)}const D=s(n,[["render",t]]);export{u as __pageData,D as default}; diff --git a/assets/ru_RU_guide_how-to-build.md.1b3dd3fb.lean.js b/assets/ru_RU_guide_how-to-build.md.1b3dd3fb.lean.js deleted file mode 100644 index b5c2bdeacc85..000000000000 --- a/assets/ru_RU_guide_how-to-build.md.1b3dd3fb.lean.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as s,o as a,c as l,O as e}from"./chunks/framework.43781440.js";const u=JSON.parse('{"title":"Как собрать KernelSU?","description":"","frontmatter":{},"headers":[],"relativePath":"ru_RU/guide/how-to-build.md","filePath":"ru_RU/guide/how-to-build.md"}'),n={name:"ru_RU/guide/how-to-build.md"},o=e("",24),p=[o];function t(r,c,i,C,d,h){return a(),l("div",null,p)}const D=s(n,[["render",t]]);export{u as __pageData,D as default}; diff --git a/assets/ru_RU_guide_how-to-build.md.3ac4fa09.js b/assets/ru_RU_guide_how-to-build.md.3ac4fa09.js new file mode 100644 index 000000000000..e440080d40e9 --- /dev/null +++ b/assets/ru_RU_guide_how-to-build.md.3ac4fa09.js @@ -0,0 +1,7 @@ +import{_ as s,o as a,c as n,Q as l}from"./chunks/framework.ec8f7e8e.js";const u=JSON.parse('{"title":"Как собрать KernelSU?","description":"","frontmatter":{},"headers":[],"relativePath":"ru_RU/guide/how-to-build.md","filePath":"ru_RU/guide/how-to-build.md"}'),o={name:"ru_RU/guide/how-to-build.md"},e=l(`

Как собрать KernelSU?

Прежде всего, необходимо ознакомиться с официальной документацией Android по сборке ядра:

  1. Сборка ядер
  2. Сборки релизов GKI

WARNING

Эта страница предназначена для устройств GKI, если вы используете старое ядро, пожалуйста, обратитесь к Как интегрировать KernelSU для не GKI ядер?.

Сборка ядра

Синхронизация исходного кода ядра

sh
repo init -u https://android.googlesource.com/kernel/manifest
+mv <kernel_manifest.xml> .repo/manifests
+repo init -m manifest.xml
+repo sync
repo init -u https://android.googlesource.com/kernel/manifest
+mv <kernel_manifest.xml> .repo/manifests
+repo init -m manifest.xml
+repo sync

Файл <kernel_manifest.xml> - это файл манифеста, который может однозначно определять сборку, с его помощью можно выполнить пересборку. Файл манифеста следует загрузить с сайта Сборки релизов Google GKI

Построение

Пожалуйста, сначала ознакомьтесь с официальной документацией.

Например, нам необходимо собрать образ ядра aarch64:

sh
LTO=thin BUILD_CONFIG=common/build.config.gki.aarch64 build/build.sh
LTO=thin BUILD_CONFIG=common/build.config.gki.aarch64 build/build.sh

Не забудьте добавить флаг LTO=thin, иначе сборка может завершиться неудачей, если память вашего компьютера меньше 24 Гб.

Начиная с Android 13, сборка ядра осуществляется с помощью bazel:

sh
tools/bazel build --config=fast //common:kernel_aarch64_dist
tools/bazel build --config=fast //common:kernel_aarch64_dist

Сборка ядра с помощью KernelSU

Если вы успешно собрали ядро, то собрать KernelSU очень просто, выберите любой запуск в корневом каталоге исходного кода ядра:

  • Последний тэг(стабильный)
sh
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -
  • Основная ветвь(разработка)
sh
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -s main
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -s main
  • Выбранный тэг(Например, версия v0.5.2)
sh
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -s v0.5.2
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -s v0.5.2

А затем пересоберите ядро и получите образ ядра с KernelSU!

`,24),p=[e];function t(r,c,i,y,d,E){return a(),n("div",null,p)}const F=s(o,[["render",t]]);export{u as __pageData,F as default}; diff --git a/assets/ru_RU_guide_how-to-build.md.3ac4fa09.lean.js b/assets/ru_RU_guide_how-to-build.md.3ac4fa09.lean.js new file mode 100644 index 000000000000..5f21604d76b5 --- /dev/null +++ b/assets/ru_RU_guide_how-to-build.md.3ac4fa09.lean.js @@ -0,0 +1 @@ +import{_ as s,o as a,c as n,Q as l}from"./chunks/framework.ec8f7e8e.js";const u=JSON.parse('{"title":"Как собрать KernelSU?","description":"","frontmatter":{},"headers":[],"relativePath":"ru_RU/guide/how-to-build.md","filePath":"ru_RU/guide/how-to-build.md"}'),o={name:"ru_RU/guide/how-to-build.md"},e=l("",24),p=[e];function t(r,c,i,y,d,E){return a(),n("div",null,p)}const F=s(o,[["render",t]]);export{u as __pageData,F as default}; diff --git a/assets/ru_RU_guide_how-to-integrate-for-non-gki.md.12445e00.js b/assets/ru_RU_guide_how-to-integrate-for-non-gki.md.12445e00.js new file mode 100644 index 000000000000..de59fabd25ae --- /dev/null +++ b/assets/ru_RU_guide_how-to-integrate-for-non-gki.md.12445e00.js @@ -0,0 +1,303 @@ +import{_ as s,o as n,c as a,Q as l}from"./chunks/framework.ec8f7e8e.js";const _=JSON.parse('{"title":"Как интегрировать KernelSU для не GKI ядер?","description":"","frontmatter":{},"headers":[],"relativePath":"ru_RU/guide/how-to-integrate-for-non-gki.md","filePath":"ru_RU/guide/how-to-integrate-for-non-gki.md"}'),p={name:"ru_RU/guide/how-to-integrate-for-non-gki.md"},e=l(`

Как интегрировать KernelSU для не GKI ядер?

KernelSU может быть интегрирован в ядра, отличные от GKI, и был перенесен на версии 4.14 и ниже.

В связи с фрагментацией ядер, отличных от GKI, у нас нет единого способа их сборки, поэтому мы не можем предоставить загрузочные образы, отличные от GKI. Однако вы можете собрать ядро самостоятельно с помощью интегрированной программы KernelSU.

Во-первых, вы должны уметь собирать загрузочное ядро из исходных текстов ядра. Если ядро не является открытым, то запустить KernelSU для вашего устройства будет затруднительно.

Если вы можете собрать загрузочное ядро, то существует два способа интеграции KernelSU в исходный код ядра:

  1. Автоматически с помощью kprobe
  2. Вручную

Интеграция с kprobe

KernelSU использует kprobe для выполнения хуков ядра, если kprobe хорошо работает в вашем ядре, то рекомендуется использовать именно этот способ.

Сначала добавьте KernelSU в дерево исходных текстов ядра:

sh
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -

Затем необходимо проверить, включена ли функция kprobe в конфигурации ядра, если нет, то добавьте в нее эти настройки:

CONFIG_KPROBES=y
+CONFIG_HAVE_KPROBES=y
+CONFIG_KPROBE_EVENTS=y
CONFIG_KPROBES=y
+CONFIG_HAVE_KPROBES=y
+CONFIG_KPROBE_EVENTS=y

И снова соберите ядро, KernelSU должен работать нормально.

Если вы обнаружите, что KPROBES по-прежнему не активирован, попробуйте включить CONFIG_MODULES. (Если это все равно не даст результата, используйте make menuconfig для поиска других зависимостей KPROBES).

Если же при интеграции KernelSU возникает зацикливание загрузки, то, возможно, в вашем ядре kprobe неисправен, следует исправить ошибку kprobe или воспользоваться вторым способом.

Как проверить, не сломан ли kprobe?

закомментируйте ksu_enable_sucompat() и ksu_enable_ksud() в файле KernelSU/kernel/ksu.c, если устройство загружается нормально, то может быть нарушена работа kprobe.

Ручная модификация исходного кода ядра

Если kprobe не работает в вашем ядре (возможно, это ошибка апстрима или ядра ниже 4.8), то можно попробовать следующий способ:

Сначала добавьте Kernel SU в дерево исходного кода вашего ядра:

  • Последний тэг(стабильный)
sh
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -
  • Основная ветвь(разработка)
sh
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -s main
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -s main
  • Выбранный тэг(Например, версия v0.5.2)
sh
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -s v0.5.2
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -s v0.5.2

Затем добавьте вызовы KernelSU в исходный код ядра, вот патч, на который можно сослаться:

diff
diff --git a/fs/exec.c b/fs/exec.c
+index ac59664eaecf..bdd585e1d2cc 100644
+--- a/fs/exec.c
++++ b/fs/exec.c
+@@ -1890,11 +1890,14 @@ static int __do_execve_file(int fd, struct filename *filename,
+ 	return retval;
+ }
+ 
++extern bool ksu_execveat_hook __read_mostly;
++extern int ksu_handle_execveat(int *fd, struct filename **filename_ptr, void *argv,
++			void *envp, int *flags);
++extern int ksu_handle_execveat_sucompat(int *fd, struct filename **filename_ptr,
++				 void *argv, void *envp, int *flags);
+ static int do_execveat_common(int fd, struct filename *filename,
+ 			      struct user_arg_ptr argv,
+ 			      struct user_arg_ptr envp,
+ 			      int flags)
+ {
++	if (unlikely(ksu_execveat_hook))
++		ksu_handle_execveat(&fd, &filename, &argv, &envp, &flags);
++	else
++		ksu_handle_execveat_sucompat(&fd, &filename, &argv, &envp, &flags);
+ 	return __do_execve_file(fd, filename, argv, envp, flags, NULL);
+ }
+ 
+diff --git a/fs/open.c b/fs/open.c
+index 05036d819197..965b84d486b8 100644
+--- a/fs/open.c
++++ b/fs/open.c
+@@ -348,6 +348,8 @@ SYSCALL_DEFINE4(fallocate, int, fd, int, mode, loff_t, offset, loff_t, len)
+ 	return ksys_fallocate(fd, mode, offset, len);
+ }
+ 
++extern int ksu_handle_faccessat(int *dfd, const char __user **filename_user, int *mode,
++			 int *flags);
+ /*
+  * access() needs to use the real uid/gid, not the effective uid/gid.
+  * We do this by temporarily clearing all FS-related capabilities and
+@@ -355,6 +357,7 @@ SYSCALL_DEFINE4(fallocate, int, fd, int, mode, loff_t, offset, loff_t, len)
+  */
+ long do_faccessat(int dfd, const char __user *filename, int mode)
+ {
++	ksu_handle_faccessat(&dfd, &filename, &mode, NULL);
+ 	const struct cred *old_cred;
+ 	struct cred *override_cred;
+ 	struct path path;
+diff --git a/fs/read_write.c b/fs/read_write.c
+index 650fc7e0f3a6..55be193913b6 100644
+--- a/fs/read_write.c
++++ b/fs/read_write.c
+@@ -434,10 +434,14 @@ ssize_t kernel_read(struct file *file, void *buf, size_t count, loff_t *pos)
+ }
+ EXPORT_SYMBOL(kernel_read);
+ 
++extern bool ksu_vfs_read_hook __read_mostly;
++extern int ksu_handle_vfs_read(struct file **file_ptr, char __user **buf_ptr,
++			size_t *count_ptr, loff_t **pos);
+ ssize_t vfs_read(struct file *file, char __user *buf, size_t count, loff_t *pos)
+ {
+ 	ssize_t ret;
+ 
++	if (unlikely(ksu_vfs_read_hook))
++		ksu_handle_vfs_read(&file, &buf, &count, &pos);
++
+ 	if (!(file->f_mode & FMODE_READ))
+ 		return -EBADF;
+ 	if (!(file->f_mode & FMODE_CAN_READ))
+diff --git a/fs/stat.c b/fs/stat.c
+index 376543199b5a..82adcef03ecc 100644
+--- a/fs/stat.c
++++ b/fs/stat.c
+@@ -148,6 +148,8 @@ int vfs_statx_fd(unsigned int fd, struct kstat *stat,
+ }
+ EXPORT_SYMBOL(vfs_statx_fd);
+ 
++extern int ksu_handle_stat(int *dfd, const char __user **filename_user, int *flags);
++
+ /**
+  * vfs_statx - Get basic and extra attributes by filename
+  * @dfd: A file descriptor representing the base dir for a relative filename
+@@ -170,6 +172,7 @@ int vfs_statx(int dfd, const char __user *filename, int flags,
+ 	int error = -EINVAL;
+ 	unsigned int lookup_flags = LOOKUP_FOLLOW | LOOKUP_AUTOMOUNT;
+ 
++	ksu_handle_stat(&dfd, &filename, &flags);
+ 	if ((flags & ~(AT_SYMLINK_NOFOLLOW | AT_NO_AUTOMOUNT |
+ 		       AT_EMPTY_PATH | KSTAT_QUERY_FLAGS)) != 0)
+ 		return -EINVAL;
diff --git a/fs/exec.c b/fs/exec.c
+index ac59664eaecf..bdd585e1d2cc 100644
+--- a/fs/exec.c
++++ b/fs/exec.c
+@@ -1890,11 +1890,14 @@ static int __do_execve_file(int fd, struct filename *filename,
+ 	return retval;
+ }
+ 
++extern bool ksu_execveat_hook __read_mostly;
++extern int ksu_handle_execveat(int *fd, struct filename **filename_ptr, void *argv,
++			void *envp, int *flags);
++extern int ksu_handle_execveat_sucompat(int *fd, struct filename **filename_ptr,
++				 void *argv, void *envp, int *flags);
+ static int do_execveat_common(int fd, struct filename *filename,
+ 			      struct user_arg_ptr argv,
+ 			      struct user_arg_ptr envp,
+ 			      int flags)
+ {
++	if (unlikely(ksu_execveat_hook))
++		ksu_handle_execveat(&fd, &filename, &argv, &envp, &flags);
++	else
++		ksu_handle_execveat_sucompat(&fd, &filename, &argv, &envp, &flags);
+ 	return __do_execve_file(fd, filename, argv, envp, flags, NULL);
+ }
+ 
+diff --git a/fs/open.c b/fs/open.c
+index 05036d819197..965b84d486b8 100644
+--- a/fs/open.c
++++ b/fs/open.c
+@@ -348,6 +348,8 @@ SYSCALL_DEFINE4(fallocate, int, fd, int, mode, loff_t, offset, loff_t, len)
+ 	return ksys_fallocate(fd, mode, offset, len);
+ }
+ 
++extern int ksu_handle_faccessat(int *dfd, const char __user **filename_user, int *mode,
++			 int *flags);
+ /*
+  * access() needs to use the real uid/gid, not the effective uid/gid.
+  * We do this by temporarily clearing all FS-related capabilities and
+@@ -355,6 +357,7 @@ SYSCALL_DEFINE4(fallocate, int, fd, int, mode, loff_t, offset, loff_t, len)
+  */
+ long do_faccessat(int dfd, const char __user *filename, int mode)
+ {
++	ksu_handle_faccessat(&dfd, &filename, &mode, NULL);
+ 	const struct cred *old_cred;
+ 	struct cred *override_cred;
+ 	struct path path;
+diff --git a/fs/read_write.c b/fs/read_write.c
+index 650fc7e0f3a6..55be193913b6 100644
+--- a/fs/read_write.c
++++ b/fs/read_write.c
+@@ -434,10 +434,14 @@ ssize_t kernel_read(struct file *file, void *buf, size_t count, loff_t *pos)
+ }
+ EXPORT_SYMBOL(kernel_read);
+ 
++extern bool ksu_vfs_read_hook __read_mostly;
++extern int ksu_handle_vfs_read(struct file **file_ptr, char __user **buf_ptr,
++			size_t *count_ptr, loff_t **pos);
+ ssize_t vfs_read(struct file *file, char __user *buf, size_t count, loff_t *pos)
+ {
+ 	ssize_t ret;
+ 
++	if (unlikely(ksu_vfs_read_hook))
++		ksu_handle_vfs_read(&file, &buf, &count, &pos);
++
+ 	if (!(file->f_mode & FMODE_READ))
+ 		return -EBADF;
+ 	if (!(file->f_mode & FMODE_CAN_READ))
+diff --git a/fs/stat.c b/fs/stat.c
+index 376543199b5a..82adcef03ecc 100644
+--- a/fs/stat.c
++++ b/fs/stat.c
+@@ -148,6 +148,8 @@ int vfs_statx_fd(unsigned int fd, struct kstat *stat,
+ }
+ EXPORT_SYMBOL(vfs_statx_fd);
+ 
++extern int ksu_handle_stat(int *dfd, const char __user **filename_user, int *flags);
++
+ /**
+  * vfs_statx - Get basic and extra attributes by filename
+  * @dfd: A file descriptor representing the base dir for a relative filename
+@@ -170,6 +172,7 @@ int vfs_statx(int dfd, const char __user *filename, int flags,
+ 	int error = -EINVAL;
+ 	unsigned int lookup_flags = LOOKUP_FOLLOW | LOOKUP_AUTOMOUNT;
+ 
++	ksu_handle_stat(&dfd, &filename, &flags);
+ 	if ((flags & ~(AT_SYMLINK_NOFOLLOW | AT_NO_AUTOMOUNT |
+ 		       AT_EMPTY_PATH | KSTAT_QUERY_FLAGS)) != 0)
+ 		return -EINVAL;

В исходных кодах ядра можно найти эти четыре функции:

  1. do_faccessat, обычно в fs/open.c.
  2. do_execveat_common, обычно в fs/exec.c.
  3. vfs_read, обычно в fs/read_write.c.
  4. vfs_statx, обычно в fs/stat.c.

Если в вашем ядре нет vfs_statx, используйте вместо него vfs_fstatat:

diff
diff --git a/fs/stat.c b/fs/stat.c
+index 068fdbcc9e26..5348b7bb9db2 100644
+--- a/fs/stat.c
++++ b/fs/stat.c
+@@ -87,6 +87,8 @@ int vfs_fstat(unsigned int fd, struct kstat *stat)
+ }
+ EXPORT_SYMBOL(vfs_fstat);
+ 
++extern int ksu_handle_stat(int *dfd, const char __user **filename_user, int *flags);
++
+ int vfs_fstatat(int dfd, const char __user *filename, struct kstat *stat,
+ 		int flag)
+ {
+@@ -94,6 +96,8 @@ int vfs_fstatat(int dfd, const char __user *filename, struct kstat *stat,
+ 	int error = -EINVAL;
+ 	unsigned int lookup_flags = 0;
+ 
++	ksu_handle_stat(&dfd, &filename, &flag);
++
+ 	if ((flag & ~(AT_SYMLINK_NOFOLLOW | AT_NO_AUTOMOUNT |
+ 		      AT_EMPTY_PATH)) != 0)
+ 		goto out;
diff --git a/fs/stat.c b/fs/stat.c
+index 068fdbcc9e26..5348b7bb9db2 100644
+--- a/fs/stat.c
++++ b/fs/stat.c
+@@ -87,6 +87,8 @@ int vfs_fstat(unsigned int fd, struct kstat *stat)
+ }
+ EXPORT_SYMBOL(vfs_fstat);
+ 
++extern int ksu_handle_stat(int *dfd, const char __user **filename_user, int *flags);
++
+ int vfs_fstatat(int dfd, const char __user *filename, struct kstat *stat,
+ 		int flag)
+ {
+@@ -94,6 +96,8 @@ int vfs_fstatat(int dfd, const char __user *filename, struct kstat *stat,
+ 	int error = -EINVAL;
+ 	unsigned int lookup_flags = 0;
+ 
++	ksu_handle_stat(&dfd, &filename, &flag);
++
+ 	if ((flag & ~(AT_SYMLINK_NOFOLLOW | AT_NO_AUTOMOUNT |
+ 		      AT_EMPTY_PATH)) != 0)
+ 		goto out;

Для ядер младше 4.17, если вы не можете найти do_faccessat, просто перейдите к определению системного вызова faccessat и поместите вызов туда:

diff
diff --git a/fs/open.c b/fs/open.c
+index 2ff887661237..e758d7db7663 100644
+--- a/fs/open.c
++++ b/fs/open.c
+@@ -355,6 +355,9 @@ SYSCALL_DEFINE4(fallocate, int, fd, int, mode, loff_t, offset, loff_t, len)
+ 	return error;
+ }
+ 
++extern int ksu_handle_faccessat(int *dfd, const char __user **filename_user, int *mode,
++			        int *flags);
++
+ /*
+  * access() needs to use the real uid/gid, not the effective uid/gid.
+  * We do this by temporarily clearing all FS-related capabilities and
+@@ -370,6 +373,8 @@ SYSCALL_DEFINE3(faccessat, int, dfd, const char __user *, filename, int, mode)
+ 	int res;
+ 	unsigned int lookup_flags = LOOKUP_FOLLOW;
+ 
++	ksu_handle_faccessat(&dfd, &filename, &mode, NULL);
++
+ 	if (mode & ~S_IRWXO)	/* where's F_OK, X_OK, W_OK, R_OK? */
+ 		return -EINVAL;
diff --git a/fs/open.c b/fs/open.c
+index 2ff887661237..e758d7db7663 100644
+--- a/fs/open.c
++++ b/fs/open.c
+@@ -355,6 +355,9 @@ SYSCALL_DEFINE4(fallocate, int, fd, int, mode, loff_t, offset, loff_t, len)
+ 	return error;
+ }
+ 
++extern int ksu_handle_faccessat(int *dfd, const char __user **filename_user, int *mode,
++			        int *flags);
++
+ /*
+  * access() needs to use the real uid/gid, not the effective uid/gid.
+  * We do this by temporarily clearing all FS-related capabilities and
+@@ -370,6 +373,8 @@ SYSCALL_DEFINE3(faccessat, int, dfd, const char __user *, filename, int, mode)
+ 	int res;
+ 	unsigned int lookup_flags = LOOKUP_FOLLOW;
+ 
++	ksu_handle_faccessat(&dfd, &filename, &mode, NULL);
++
+ 	if (mode & ~S_IRWXO)	/* where's F_OK, X_OK, W_OK, R_OK? */
+ 		return -EINVAL;

Чтобы включить встроенный в KernelSU безопасный режим, необходимо также изменить input_handle_event в файле drivers/input/input.c:

TIP

Настоятельно рекомендуется включить эту функцию, она очень помогает предотвратить циклическую загрузку!

diff
diff --git a/drivers/input/input.c b/drivers/input/input.c
+index 45306f9ef247..815091ebfca4 100755
+--- a/drivers/input/input.c
++++ b/drivers/input/input.c
+@@ -367,10 +367,13 @@ static int input_get_disposition(struct input_dev *dev,
+ 	return disposition;
+ }
+ 
++extern bool ksu_input_hook __read_mostly;
++extern int ksu_handle_input_handle_event(unsigned int *type, unsigned int *code, int *value);
++
+ static void input_handle_event(struct input_dev *dev,
+ 			       unsigned int type, unsigned int code, int value)
+ {
+	int disposition = input_get_disposition(dev, type, code, &value);
++
++	if (unlikely(ksu_input_hook))
++		ksu_handle_input_handle_event(&type, &code, &value);
+ 
+ 	if (disposition != INPUT_IGNORE_EVENT && type != EV_SYN)
+ 		add_input_randomness(type, code, value);
diff --git a/drivers/input/input.c b/drivers/input/input.c
+index 45306f9ef247..815091ebfca4 100755
+--- a/drivers/input/input.c
++++ b/drivers/input/input.c
+@@ -367,10 +367,13 @@ static int input_get_disposition(struct input_dev *dev,
+ 	return disposition;
+ }
+ 
++extern bool ksu_input_hook __read_mostly;
++extern int ksu_handle_input_handle_event(unsigned int *type, unsigned int *code, int *value);
++
+ static void input_handle_event(struct input_dev *dev,
+ 			       unsigned int type, unsigned int code, int value)
+ {
+	int disposition = input_get_disposition(dev, type, code, &value);
++
++	if (unlikely(ksu_input_hook))
++		ksu_handle_input_handle_event(&type, &code, &value);
+ 
+ 	if (disposition != INPUT_IGNORE_EVENT && type != EV_SYN)
+ 		add_input_randomness(type, code, value);

Наконец, снова соберите ядро, KernelSU должен работать хорошо.

`,37),t=[e];function o(c,i,r,E,d,f){return n(),a("div",null,t)}const u=s(p,[["render",o]]);export{_ as __pageData,u as default}; diff --git a/assets/ru_RU_guide_how-to-integrate-for-non-gki.md.12445e00.lean.js b/assets/ru_RU_guide_how-to-integrate-for-non-gki.md.12445e00.lean.js new file mode 100644 index 000000000000..372d97807bcb --- /dev/null +++ b/assets/ru_RU_guide_how-to-integrate-for-non-gki.md.12445e00.lean.js @@ -0,0 +1 @@ +import{_ as s,o as n,c as a,Q as l}from"./chunks/framework.ec8f7e8e.js";const _=JSON.parse('{"title":"Как интегрировать KernelSU для не GKI ядер?","description":"","frontmatter":{},"headers":[],"relativePath":"ru_RU/guide/how-to-integrate-for-non-gki.md","filePath":"ru_RU/guide/how-to-integrate-for-non-gki.md"}'),p={name:"ru_RU/guide/how-to-integrate-for-non-gki.md"},e=l("",37),t=[e];function o(c,i,r,E,d,f){return n(),a("div",null,t)}const u=s(p,[["render",o]]);export{_ as __pageData,u as default}; diff --git a/assets/ru_RU_guide_how-to-integrate-for-non-gki.md.7c70c478.js b/assets/ru_RU_guide_how-to-integrate-for-non-gki.md.7c70c478.js deleted file mode 100644 index 8a40a644cf7b..000000000000 --- a/assets/ru_RU_guide_how-to-integrate-for-non-gki.md.7c70c478.js +++ /dev/null @@ -1,152 +0,0 @@ -import{_ as s,o as n,c as a,O as l}from"./chunks/framework.43781440.js";const d=JSON.parse('{"title":"Как интегрировать KernelSU для не GKI ядер?","description":"","frontmatter":{},"headers":[],"relativePath":"ru_RU/guide/how-to-integrate-for-non-gki.md","filePath":"ru_RU/guide/how-to-integrate-for-non-gki.md"}'),p={name:"ru_RU/guide/how-to-integrate-for-non-gki.md"},e=l(`

Как интегрировать KernelSU для не GKI ядер?

KernelSU может быть интегрирован в ядра, отличные от GKI, и был перенесен на версии 4.14 и ниже.

В связи с фрагментацией ядер, отличных от GKI, у нас нет единого способа их сборки, поэтому мы не можем предоставить загрузочные образы, отличные от GKI. Однако вы можете собрать ядро самостоятельно с помощью интегрированной программы KernelSU.

Во-первых, вы должны уметь собирать загрузочное ядро из исходных текстов ядра. Если ядро не является открытым, то запустить KernelSU для вашего устройства будет затруднительно.

Если вы можете собрать загрузочное ядро, то существует два способа интеграции KernelSU в исходный код ядра:

  1. Автоматически с помощью kprobe
  2. Вручную

Интеграция с kprobe

KernelSU использует kprobe для выполнения хуков ядра, если kprobe хорошо работает в вашем ядре, то рекомендуется использовать именно этот способ.

Сначала добавьте KernelSU в дерево исходных текстов ядра:

sh
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -

Затем необходимо проверить, включена ли функция kprobe в конфигурации ядра, если нет, то добавьте в нее эти настройки:

CONFIG_KPROBES=y
-CONFIG_HAVE_KPROBES=y
-CONFIG_KPROBE_EVENTS=y

И снова соберите ядро, KernelSU должен работать нормально.

Если вы обнаружите, что KPROBES по-прежнему не активирован, попробуйте включить CONFIG_MODULES. (Если это все равно не даст результата, используйте make menuconfig для поиска других зависимостей KPROBES).

Если же при интеграции KernelSU возникает зацикливание загрузки, то, возможно, в вашем ядре kprobe неисправен, следует исправить ошибку kprobe или воспользоваться вторым способом.

Как проверить, не сломан ли kprobe?

закомментируйте ksu_enable_sucompat() и ksu_enable_ksud() в файле KernelSU/kernel/ksu.c, если устройство загружается нормально, то может быть нарушена работа kprobe.

Ручная модификация исходного кода ядра

Если kprobe не работает в вашем ядре (возможно, это ошибка апстрима или ядра ниже 4.8), то можно попробовать следующий способ:

Сначала добавьте Kernel SU в дерево исходного кода вашего ядра:

  • Последний тэг(стабильный)
sh
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -
  • Основная ветвь(разработка)
sh
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -s main
  • Выбранный тэг(Например, версия v0.5.2)
sh
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -s v0.5.2

Затем добавьте вызовы KernelSU в исходный код ядра, вот патч, на который можно сослаться:

diff
diff --git a/fs/exec.c b/fs/exec.c
-index ac59664eaecf..bdd585e1d2cc 100644
---- a/fs/exec.c
-+++ b/fs/exec.c
-@@ -1890,11 +1890,14 @@ static int __do_execve_file(int fd, struct filename *filename,
- 	return retval;
- }
- 
-+extern bool ksu_execveat_hook __read_mostly;
-+extern int ksu_handle_execveat(int *fd, struct filename **filename_ptr, void *argv,
-+			void *envp, int *flags);
-+extern int ksu_handle_execveat_sucompat(int *fd, struct filename **filename_ptr,
-+				 void *argv, void *envp, int *flags);
- static int do_execveat_common(int fd, struct filename *filename,
- 			      struct user_arg_ptr argv,
- 			      struct user_arg_ptr envp,
- 			      int flags)
- {
-+	if (unlikely(ksu_execveat_hook))
-+		ksu_handle_execveat(&fd, &filename, &argv, &envp, &flags);
-+	else
-+		ksu_handle_execveat_sucompat(&fd, &filename, &argv, &envp, &flags);
- 	return __do_execve_file(fd, filename, argv, envp, flags, NULL);
- }
- 
-diff --git a/fs/open.c b/fs/open.c
-index 05036d819197..965b84d486b8 100644
---- a/fs/open.c
-+++ b/fs/open.c
-@@ -348,6 +348,8 @@ SYSCALL_DEFINE4(fallocate, int, fd, int, mode, loff_t, offset, loff_t, len)
- 	return ksys_fallocate(fd, mode, offset, len);
- }
- 
-+extern int ksu_handle_faccessat(int *dfd, const char __user **filename_user, int *mode,
-+			 int *flags);
- /*
-  * access() needs to use the real uid/gid, not the effective uid/gid.
-  * We do this by temporarily clearing all FS-related capabilities and
-@@ -355,6 +357,7 @@ SYSCALL_DEFINE4(fallocate, int, fd, int, mode, loff_t, offset, loff_t, len)
-  */
- long do_faccessat(int dfd, const char __user *filename, int mode)
- {
-+	ksu_handle_faccessat(&dfd, &filename, &mode, NULL);
- 	const struct cred *old_cred;
- 	struct cred *override_cred;
- 	struct path path;
-diff --git a/fs/read_write.c b/fs/read_write.c
-index 650fc7e0f3a6..55be193913b6 100644
---- a/fs/read_write.c
-+++ b/fs/read_write.c
-@@ -434,10 +434,14 @@ ssize_t kernel_read(struct file *file, void *buf, size_t count, loff_t *pos)
- }
- EXPORT_SYMBOL(kernel_read);
- 
-+extern bool ksu_vfs_read_hook __read_mostly;
-+extern int ksu_handle_vfs_read(struct file **file_ptr, char __user **buf_ptr,
-+			size_t *count_ptr, loff_t **pos);
- ssize_t vfs_read(struct file *file, char __user *buf, size_t count, loff_t *pos)
- {
- 	ssize_t ret;
- 
-+	if (unlikely(ksu_vfs_read_hook))
-+		ksu_handle_vfs_read(&file, &buf, &count, &pos);
-+
- 	if (!(file->f_mode & FMODE_READ))
- 		return -EBADF;
- 	if (!(file->f_mode & FMODE_CAN_READ))
-diff --git a/fs/stat.c b/fs/stat.c
-index 376543199b5a..82adcef03ecc 100644
---- a/fs/stat.c
-+++ b/fs/stat.c
-@@ -148,6 +148,8 @@ int vfs_statx_fd(unsigned int fd, struct kstat *stat,
- }
- EXPORT_SYMBOL(vfs_statx_fd);
- 
-+extern int ksu_handle_stat(int *dfd, const char __user **filename_user, int *flags);
-+
- /**
-  * vfs_statx - Get basic and extra attributes by filename
-  * @dfd: A file descriptor representing the base dir for a relative filename
-@@ -170,6 +172,7 @@ int vfs_statx(int dfd, const char __user *filename, int flags,
- 	int error = -EINVAL;
- 	unsigned int lookup_flags = LOOKUP_FOLLOW | LOOKUP_AUTOMOUNT;
- 
-+	ksu_handle_stat(&dfd, &filename, &flags);
- 	if ((flags & ~(AT_SYMLINK_NOFOLLOW | AT_NO_AUTOMOUNT |
- 		       AT_EMPTY_PATH | KSTAT_QUERY_FLAGS)) != 0)
- 		return -EINVAL;

В исходных кодах ядра можно найти эти четыре функции:

  1. do_faccessat, обычно в fs/open.c.
  2. do_execveat_common, обычно в fs/exec.c.
  3. vfs_read, обычно в fs/read_write.c.
  4. vfs_statx, обычно в fs/stat.c.

Если в вашем ядре нет vfs_statx, используйте вместо него vfs_fstatat:

diff
diff --git a/fs/stat.c b/fs/stat.c
-index 068fdbcc9e26..5348b7bb9db2 100644
---- a/fs/stat.c
-+++ b/fs/stat.c
-@@ -87,6 +87,8 @@ int vfs_fstat(unsigned int fd, struct kstat *stat)
- }
- EXPORT_SYMBOL(vfs_fstat);
- 
-+extern int ksu_handle_stat(int *dfd, const char __user **filename_user, int *flags);
-+
- int vfs_fstatat(int dfd, const char __user *filename, struct kstat *stat,
- 		int flag)
- {
-@@ -94,6 +96,8 @@ int vfs_fstatat(int dfd, const char __user *filename, struct kstat *stat,
- 	int error = -EINVAL;
- 	unsigned int lookup_flags = 0;
- 
-+	ksu_handle_stat(&dfd, &filename, &flag);
-+
- 	if ((flag & ~(AT_SYMLINK_NOFOLLOW | AT_NO_AUTOMOUNT |
- 		      AT_EMPTY_PATH)) != 0)
- 		goto out;

Для ядер младше 4.17, если вы не можете найти do_faccessat, просто перейдите к определению системного вызова faccessat и поместите вызов туда:

diff
diff --git a/fs/open.c b/fs/open.c
-index 2ff887661237..e758d7db7663 100644
---- a/fs/open.c
-+++ b/fs/open.c
-@@ -355,6 +355,9 @@ SYSCALL_DEFINE4(fallocate, int, fd, int, mode, loff_t, offset, loff_t, len)
- 	return error;
- }
- 
-+extern int ksu_handle_faccessat(int *dfd, const char __user **filename_user, int *mode,
-+			        int *flags);
-+
- /*
-  * access() needs to use the real uid/gid, not the effective uid/gid.
-  * We do this by temporarily clearing all FS-related capabilities and
-@@ -370,6 +373,8 @@ SYSCALL_DEFINE3(faccessat, int, dfd, const char __user *, filename, int, mode)
- 	int res;
- 	unsigned int lookup_flags = LOOKUP_FOLLOW;
- 
-+	ksu_handle_faccessat(&dfd, &filename, &mode, NULL);
-+
- 	if (mode & ~S_IRWXO)	/* where's F_OK, X_OK, W_OK, R_OK? */
- 		return -EINVAL;

Чтобы включить встроенный в KernelSU безопасный режим, необходимо также изменить input_handle_event в файле drivers/input/input.c:

TIP

Настоятельно рекомендуется включить эту функцию, она очень помогает предотвратить циклическую загрузку!

diff
diff --git a/drivers/input/input.c b/drivers/input/input.c
-index 45306f9ef247..815091ebfca4 100755
---- a/drivers/input/input.c
-+++ b/drivers/input/input.c
-@@ -367,10 +367,13 @@ static int input_get_disposition(struct input_dev *dev,
- 	return disposition;
- }
- 
-+extern bool ksu_input_hook __read_mostly;
-+extern int ksu_handle_input_handle_event(unsigned int *type, unsigned int *code, int *value);
-+
- static void input_handle_event(struct input_dev *dev,
- 			       unsigned int type, unsigned int code, int value)
- {
-	int disposition = input_get_disposition(dev, type, code, &value);
-+
-+	if (unlikely(ksu_input_hook))
-+		ksu_handle_input_handle_event(&type, &code, &value);
- 
- 	if (disposition != INPUT_IGNORE_EVENT && type != EV_SYN)
- 		add_input_randomness(type, code, value);

Наконец, снова соберите ядро, KernelSU должен работать хорошо.

`,37),o=[e];function t(c,r,i,C,D,A){return n(),a("div",null,o)}const f=s(p,[["render",t]]);export{d as __pageData,f as default}; diff --git a/assets/ru_RU_guide_how-to-integrate-for-non-gki.md.7c70c478.lean.js b/assets/ru_RU_guide_how-to-integrate-for-non-gki.md.7c70c478.lean.js deleted file mode 100644 index 32939a42ab7e..000000000000 --- a/assets/ru_RU_guide_how-to-integrate-for-non-gki.md.7c70c478.lean.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as s,o as n,c as a,O as l}from"./chunks/framework.43781440.js";const d=JSON.parse('{"title":"Как интегрировать KernelSU для не GKI ядер?","description":"","frontmatter":{},"headers":[],"relativePath":"ru_RU/guide/how-to-integrate-for-non-gki.md","filePath":"ru_RU/guide/how-to-integrate-for-non-gki.md"}'),p={name:"ru_RU/guide/how-to-integrate-for-non-gki.md"},e=l("",37),o=[e];function t(c,r,i,C,D,A){return n(),a("div",null,o)}const f=s(p,[["render",t]]);export{d as __pageData,f as default}; diff --git a/assets/ru_RU_guide_installation.md.bd641868.js b/assets/ru_RU_guide_installation.md.ad88b078.js similarity index 60% rename from assets/ru_RU_guide_installation.md.bd641868.js rename to assets/ru_RU_guide_installation.md.ad88b078.js index 4546b5a1747f..0e1be9ed5a1b 100644 --- a/assets/ru_RU_guide_installation.md.bd641868.js +++ b/assets/ru_RU_guide_installation.md.ad88b078.js @@ -1,3 +1,5 @@ -import{_ as a,o as l,c as t,O as o,z as e,a as i}from"./chunks/framework.43781440.js";const y=JSON.parse('{"title":"Установка","description":"","frontmatter":{},"headers":[],"relativePath":"ru_RU/guide/installation.md","filePath":"ru_RU/guide/installation.md"}'),r={name:"ru_RU/guide/installation.md"},n=o(`

Установка

Проверьте, поддерживается ли ваше устройство

Скачайте приложение менеджера KernelSU с сайта GitHub Releases или Coolapk market и установите его на устройство:

  • Если приложение показывает Unsupported, это означает, что Вы должны скомпилировать ядро самостоятельно, KernelSU не будет и никогда не предоставит Вам загрузочный образ для прошивки.
  • Если приложение показывает Не установлено, значит, ваши устройства официально поддерживаются KernelSU.

INFO

Для устройств, показывающих Unsupported, здесь находится Unofficially-support-devices, вы можете скомпилировать ядро самостоятельно.

Резервное копирование стокового файла boot.img

Перед прошивкой необходимо создать резервную копию файла boot.img. Если возникнет ошибка загрузки, вы всегда сможете восстановить систему, перепрошив ее на заводскую загрузку с помощью fastboot.

WARNING

Прошивка может привести к потере данных, поэтому обязательно выполните этот шаг перед переходом к следующему шагу!!! При необходимости можно также создать резервную копию всех данных на телефоне.

Необходимые знания

ADB и fastboot

По умолчанию в этом руководстве вы будете использовать инструменты ADB и fastboot, поэтому, если вы их не знаете, рекомендуем сначала воспользоваться поисковой системой, чтобы узнать о них.

KMI

Kernel Module Interface (KMI), версии ядра с одинаковым KMI совместимы Это то, что в GKI означает "общий"; наоборот, если KMI отличается, то эти ядра несовместимы друг с другом, и прошивка образа ядра с другим KMI, чем у вашего устройства, может привести к bootloop.

В частности, для устройств GKI формат версии ядра должен быть следующим:

txt
KernelRelease :=
-Version.PatchLevel.SubLevel-AndroidRelease-KmiGeneration-suffix
-w      .x         .y       -zzz           -k            -something

w.x-zz-k - версия KMI. Например, если версия ядра устройства 5.10.101-android12-9-g30979850fc20, то его KMI - 5.10-android12-9; теоретически оно может нормально загружаться с другими ядрами KMI.

TIP

Обратите внимание, что SubLevel в версии ядра не является частью KMI! Это означает, что 5.10.101-android12-9-g30979850fc20 имеет тот же KMI, что и 5.10.137-android12-9-g30979850fc20!

Версия ядра и версия Android

Обратите внимание: Версия ядра и версия Android - это не обязательно одно и то же!

Если вы обнаружили, что версия ядра android12-5.10.101, а версия системы Android - Android 13 или другая, не удивляйтесь, поскольку номер версии системы Android не обязательно совпадает с номером версии ядра Linux; Номер версии ядра Linux обычно соответствует версии системы Android, поставляемой с устройством при его поставке. При последующем обновлении системы Android версия ядра, как правило, не меняется. При необходимости прошивки укажите версию ядра!!!.

Введение

Существует несколько способов установки KernelSU, каждый из которых подходит для разных сценариев, поэтому выбирайте их по своему усмотрению.

  1. Установка с помощью пользовательского Recovery (например, TWRP)
  2. Установка с помощью приложения для прошивки ядра, например, Franco Kernel Manager
  3. Установка с помощью fastboot с использованием boot.img, предоставленного KernelSU
  4. Восстановить boot.img вручную и установить его

Установка с помощью пользовательского Recovery

Необходимые условия: На устройстве должен быть установлен пользовательский Recovery, например TWRP; если его нет или доступен только официальный Recovery, воспользуйтесь другим способом.

Шаг:

  1. С Release page KernelSU загрузите zip-пакет, начинающийся с AnyKernel3, который соответствует версии вашего телефона; например, версия ядра телефона - android12-5.10. 66, то следует скачать файл AnyKernel3-android12-5.10.66_yyy-MM.zip (где yyyy - год, а MM - месяц).
  2. Перезагрузите телефон в TWRP.
  3. С помощью adb поместите AnyKernel3-*.zip в /sdcard телефона и выберите установку в графическом интерфейсе TWRP; или вы можете напрямую adb sideload AnyKernel-*.zip для установки.

PS. Данный способ подходит для любой установки (не ограничиваясь начальной установкой или последующими обновлениями), если вы используете TWRP.

Установка с помощью Kernel Flasher

Необходимые условия: Ваше устройство должно быть рутованным. Например, вы установили Magisk, чтобы получить root, или установили старую версию KernelSU и должны обновить ее до другой версии; если ваше устройство не укоренено, попробуйте другие методы.

Шаг:

  1. Загрузите zip-архив AnyKernel3; инструкции по загрузке см. в разделе Установка с помощью пользовательского Recovery.
  2. Откройте приложение для прошивки ядра и используйте предоставленный AnyKernel3 zip для прошивки.

Если вы раньше не использовали приложение для прошивки ядра, то наиболее популярными являются следующие.

  1. Kernel Flasher
  2. Franco Kernel Manager
  3. Ex Kernel Manager

PS. Этот способ более удобен при обновлении KernelSU и может быть выполнен без компьютера (сначала сделайте резервную копию!). .

Установка с помощью boot.img, предоставленного KernelSU

Этот способ не требует наличия TWRP и root-прав на телефоне; он подходит для первой установки KernelSU.

Найти подходящий boot.img

KernelSU предоставляет общий boot.img для устройств GKI, и его необходимо прошить в загрузочный раздел устройства.

Вы можете загрузить boot.img с GitHub Release, обратите внимание, что вы должны использовать правильную версию boot.img. Например, если на устройстве установлено ядро android12-5.10.101, то необходимо загрузить android-5.10.101_yyy-MM.boot-<format>.img. , необходимо загрузить android-5.10.101_yyy-MM.boot-<format>.img.(Соблюдайте соответствие KMI!).

Где <format> означает формат сжатия ядра в официальном boot.img, проверьте формат сжатия ядра в оригинальном boot.img, вы должны использовать правильный формат, например, lz4, gz; если вы используете неправильный формат сжатия, вы можете столкнуться с bootloop.

INFO

  1. Вы можете использовать magiskboot для получения формата сжатия исходной загрузки; конечно, вы также можете спросить других, более опытных ребят с той же моделью, что и ваше устройство. Кроме того, формат сжатия ядра обычно не меняется, поэтому, если вы успешно загрузились с определенным форматом сжатия, вы можете попробовать этот формат позже.
  2. Устройства Xiaomi обычно используют gz или без сжатия.
  3. Для устройств Pixel следуйте приведенным ниже инструкциям.

прошить boot.img на устройство

Используйте adb для подключения устройства, затем выполните adb reboot bootloader для входа в режим fastboot, после чего используйте эту команду для прошивки KernelSU:

sh
fastboot flash boot boot.img

INFO

Если устройство поддерживает fastboot boot, можно сначала использовать fastboot boot boot.img, чтобы попытаться использовать boot.img для загрузки системы. Если произойдет что-то непредвиденное, перезагрузите его снова для загрузки.

перезагрузка

После завершения прошивки необходимо перезагрузить устройство:

sh
fastboot reboot

Исправить boot.img вручную

Для некоторых устройств формат boot.img не так распространен, например, не lz4, gz или несжатый; наиболее типичным является Pixel, его boot.img имеет формат lz4_legacy со сжатием, ramdisk может быть gz, также может быть lz4_legacy со сжатием; в это время, если напрямую прошить boot.img, предоставляемый KernelSU, телефон может не загрузиться; в это время можно вручную исправить boot.img для достижения цели.

Как правило, существует два способа исправления:

  1. Android-Image-Kitchen
  2. magiskboot

Среди них Android-Image-Kitchen подходит для работы на ПК, а magiskboot нуждается в сотрудничестве мобильного телефона.

Подготовка

  1. Получите стоковый boot.img вашего телефона; его можно получить у производителя устройства, возможно, вам понадобится payload-dumper-go
  2. Загрузите zip-файл AnyKernel3, предоставленный KernelSU, который соответствует версии KMI вашего устройства (можно обратиться к разделу Установка с помощью пользовательского Recovery).
  3. Распакуйте пакет AnyKernel3 и получите файл Image, который является файлом ядра KernelSU.

Использование Android-Image-Kitchen

  1. Загрузите программу Android-Image-Kitchen на свой компьютер.
  2. Поместите файл boot.img в корневую папку Android-Image-Kitchen.
  3. Выполните команду ./unpackimg.sh boot.img в корневом каталоге Android-Image-Kitchen, в результате чего boot.img распакуется и появятся некоторые файлы.
  4. Замените boot.img-kernel в каталоге split_img тем образом, который вы извлекли из AnyKernel3 (обратите внимание на изменение названия на boot.img-kernel).
  5. Выполните команду ./repackimg.sh в корневом каталоге 在 Android-Image-Kitchen; Вы получите файл с именем image-new.img; Прошейте этот boot.img с помощью fastboot (см. предыдущий раздел).
`,58),s=e("h3",{id:"using",magiskboot:"",tabindex:"-1"},[i("Использование magiskboot "),e("a",{class:"header-anchor",href:"#using","aria-label":'Permalink to "Использование magiskboot {#using magiskboot}"'},"​")],-1),d=o('
  1. Загрузите последнюю версию Magisk с Release Page.
  2. Переименуйте Magisk-*.apk в Magisk-vesion.zip и разархивируйте его.
  3. Закачайте Magisk-v25.2/lib/arm64-v8a/libmagiskboot.so на устройство с помощью adb: adb push Magisk-v25.2/lib/arm64-v8a/libmagiskboot.so /data/local/tmp/magiskboot.
  4. Установите на устройство стоковый boot.img и образ в AnyKernel3.
  5. Войдите в оболочку adb и перейдите в каталог /data/local/tmp/, затем chmod +x magiskboot.
  6. Войдите в adb shell и cd директории /data/local/tmp/, выполните команду ./magiskboot unpack boot.img для распаковки boot.img, вы получите файл kernel, это и есть ваше стоковое ядро.
  7. Замените kernel на Image: mv -f Image kernel.
  8. Выполните команду ./magiskboot repack boot.img, чтобы перепаковать boot img, и получите файл new-boot.img, прошейте его на устройство с помощью fastboot.

Другие методы

На самом деле все эти способы установки имеют только одну основную идею - заменить исходное ядро на ядро, предоставляемое KernelSU; если это возможно, то установка возможна; например, возможны следующие способы.

  1. Сначала установить Magisk, получить права root через Magisk, а затем с помощью kernel flasher прошить AnyKernel zip из KernelSU.
  2. Использовать какой-либо инструментарий для прошивки на ПК, чтобы прошить ядро, предоставленное KernelSU.
',4),c=[n,s,d];function p(h,b,g,m,u,k){return l(),t("div",null,c)}const K=a(r,[["render",p]]);export{y as __pageData,K as default}; +import{_ as a,o as l,c as t,Q as o,k as e,a as i}from"./chunks/framework.ec8f7e8e.js";const y=JSON.parse('{"title":"Установка","description":"","frontmatter":{},"headers":[],"relativePath":"ru_RU/guide/installation.md","filePath":"ru_RU/guide/installation.md"}'),r={name:"ru_RU/guide/installation.md"},n=o(`

Установка

Проверьте, поддерживается ли ваше устройство

Скачайте приложение менеджера KernelSU с сайта GitHub Releases или Coolapk market и установите его на устройство:

  • Если приложение показывает Unsupported, это означает, что Вы должны скомпилировать ядро самостоятельно, KernelSU не будет и никогда не предоставит Вам загрузочный образ для прошивки.
  • Если приложение показывает Не установлено, значит, ваши устройства официально поддерживаются KernelSU.

INFO

Для устройств, показывающих Unsupported, здесь находится Unofficially-support-devices, вы можете скомпилировать ядро самостоятельно.

Резервное копирование стокового файла boot.img

Перед прошивкой необходимо создать резервную копию файла boot.img. Если возникнет ошибка загрузки, вы всегда сможете восстановить систему, перепрошив ее на заводскую загрузку с помощью fastboot.

WARNING

Прошивка может привести к потере данных, поэтому обязательно выполните этот шаг перед переходом к следующему шагу!!! При необходимости можно также создать резервную копию всех данных на телефоне.

Необходимые знания

ADB и fastboot

По умолчанию в этом руководстве вы будете использовать инструменты ADB и fastboot, поэтому, если вы их не знаете, рекомендуем сначала воспользоваться поисковой системой, чтобы узнать о них.

KMI

Kernel Module Interface (KMI), версии ядра с одинаковым KMI совместимы Это то, что в GKI означает "общий"; наоборот, если KMI отличается, то эти ядра несовместимы друг с другом, и прошивка образа ядра с другим KMI, чем у вашего устройства, может привести к bootloop.

В частности, для устройств GKI формат версии ядра должен быть следующим:

txt
KernelRelease :=
+Version.PatchLevel.SubLevel-AndroidRelease-KmiGeneration-suffix
+w      .x         .y       -zzz           -k            -something
KernelRelease :=
+Version.PatchLevel.SubLevel-AndroidRelease-KmiGeneration-suffix
+w      .x         .y       -zzz           -k            -something

w.x-zz-k - версия KMI. Например, если версия ядра устройства 5.10.101-android12-9-g30979850fc20, то его KMI - 5.10-android12-9; теоретически оно может нормально загружаться с другими ядрами KMI.

TIP

Обратите внимание, что SubLevel в версии ядра не является частью KMI! Это означает, что 5.10.101-android12-9-g30979850fc20 имеет тот же KMI, что и 5.10.137-android12-9-g30979850fc20!

Версия ядра и версия Android

Обратите внимание: Версия ядра и версия Android - это не обязательно одно и то же!

Если вы обнаружили, что версия ядра android12-5.10.101, а версия системы Android - Android 13 или другая, не удивляйтесь, поскольку номер версии системы Android не обязательно совпадает с номером версии ядра Linux; Номер версии ядра Linux обычно соответствует версии системы Android, поставляемой с устройством при его поставке. При последующем обновлении системы Android версия ядра, как правило, не меняется. При необходимости прошивки укажите версию ядра!!!.

Введение

Существует несколько способов установки KernelSU, каждый из которых подходит для разных сценариев, поэтому выбирайте их по своему усмотрению.

  1. Установка с помощью пользовательского Recovery (например, TWRP)
  2. Установка с помощью приложения для прошивки ядра, например, Franco Kernel Manager
  3. Установка с помощью fastboot с использованием boot.img, предоставленного KernelSU
  4. Восстановить boot.img вручную и установить его

Установка с помощью пользовательского Recovery

Необходимые условия: На устройстве должен быть установлен пользовательский Recovery, например TWRP; если его нет или доступен только официальный Recovery, воспользуйтесь другим способом.

Шаг:

  1. С Release page KernelSU загрузите zip-пакет, начинающийся с AnyKernel3, который соответствует версии вашего телефона; например, версия ядра телефона - android12-5.10. 66, то следует скачать файл AnyKernel3-android12-5.10.66_yyy-MM.zip (где yyyy - год, а MM - месяц).
  2. Перезагрузите телефон в TWRP.
  3. С помощью adb поместите AnyKernel3-*.zip в /sdcard телефона и выберите установку в графическом интерфейсе TWRP; или вы можете напрямую adb sideload AnyKernel-*.zip для установки.

PS. Данный способ подходит для любой установки (не ограничиваясь начальной установкой или последующими обновлениями), если вы используете TWRP.

Установка с помощью Kernel Flasher

Необходимые условия: Ваше устройство должно быть рутованным. Например, вы установили Magisk, чтобы получить root, или установили старую версию KernelSU и должны обновить ее до другой версии; если ваше устройство не укоренено, попробуйте другие методы.

Шаг:

  1. Загрузите zip-архив AnyKernel3; инструкции по загрузке см. в разделе Установка с помощью пользовательского Recovery.
  2. Откройте приложение для прошивки ядра и используйте предоставленный AnyKernel3 zip для прошивки.

Если вы раньше не использовали приложение для прошивки ядра, то наиболее популярными являются следующие.

  1. Kernel Flasher
  2. Franco Kernel Manager
  3. Ex Kernel Manager

PS. Этот способ более удобен при обновлении KernelSU и может быть выполнен без компьютера (сначала сделайте резервную копию!). .

Установка с помощью boot.img, предоставленного KernelSU

Этот способ не требует наличия TWRP и root-прав на телефоне; он подходит для первой установки KernelSU.

Найти подходящий boot.img

KernelSU предоставляет общий boot.img для устройств GKI, и его необходимо прошить в загрузочный раздел устройства.

Вы можете загрузить boot.img с GitHub Release, обратите внимание, что вы должны использовать правильную версию boot.img. Например, если на устройстве установлено ядро android12-5.10.101, то необходимо загрузить android-5.10.101_yyy-MM.boot-<format>.img. , необходимо загрузить android-5.10.101_yyy-MM.boot-<format>.img.(Соблюдайте соответствие KMI!).

Где <format> означает формат сжатия ядра в официальном boot.img, проверьте формат сжатия ядра в оригинальном boot.img, вы должны использовать правильный формат, например, lz4, gz; если вы используете неправильный формат сжатия, вы можете столкнуться с bootloop.

INFO

  1. Вы можете использовать magiskboot для получения формата сжатия исходной загрузки; конечно, вы также можете спросить других, более опытных ребят с той же моделью, что и ваше устройство. Кроме того, формат сжатия ядра обычно не меняется, поэтому, если вы успешно загрузились с определенным форматом сжатия, вы можете попробовать этот формат позже.
  2. Устройства Xiaomi обычно используют gz или без сжатия.
  3. Для устройств Pixel следуйте приведенным ниже инструкциям.

прошить boot.img на устройство

Используйте adb для подключения устройства, затем выполните adb reboot bootloader для входа в режим fastboot, после чего используйте эту команду для прошивки KernelSU:

sh
fastboot flash boot boot.img
fastboot flash boot boot.img

INFO

Если устройство поддерживает fastboot boot, можно сначала использовать fastboot boot boot.img, чтобы попытаться использовать boot.img для загрузки системы. Если произойдет что-то непредвиденное, перезагрузите его снова для загрузки.

перезагрузка

После завершения прошивки необходимо перезагрузить устройство:

sh
fastboot reboot
fastboot reboot

Исправить boot.img вручную

Для некоторых устройств формат boot.img не так распространен, например, не lz4, gz или несжатый; наиболее типичным является Pixel, его boot.img имеет формат lz4_legacy со сжатием, ramdisk может быть gz, также может быть lz4_legacy со сжатием; в это время, если напрямую прошить boot.img, предоставляемый KernelSU, телефон может не загрузиться; в это время можно вручную исправить boot.img для достижения цели.

Как правило, существует два способа исправления:

  1. Android-Image-Kitchen
  2. magiskboot

Среди них Android-Image-Kitchen подходит для работы на ПК, а magiskboot нуждается в сотрудничестве мобильного телефона.

Подготовка

  1. Получите стоковый boot.img вашего телефона; его можно получить у производителя устройства, возможно, вам понадобится payload-dumper-go
  2. Загрузите zip-файл AnyKernel3, предоставленный KernelSU, который соответствует версии KMI вашего устройства (можно обратиться к разделу Установка с помощью пользовательского Recovery).
  3. Распакуйте пакет AnyKernel3 и получите файл Image, который является файлом ядра KernelSU.

Использование Android-Image-Kitchen

  1. Загрузите программу Android-Image-Kitchen на свой компьютер.
  2. Поместите файл boot.img в корневую папку Android-Image-Kitchen.
  3. Выполните команду ./unpackimg.sh boot.img в корневом каталоге Android-Image-Kitchen, в результате чего boot.img распакуется и появятся некоторые файлы.
  4. Замените boot.img-kernel в каталоге split_img тем образом, который вы извлекли из AnyKernel3 (обратите внимание на изменение названия на boot.img-kernel).
  5. Выполните команду ./repackimg.sh в корневом каталоге 在 Android-Image-Kitchen; Вы получите файл с именем image-new.img; Прошейте этот boot.img с помощью fastboot (см. предыдущий раздел).
`,58),s=e("h3",{id:"using",magiskboot:"",tabindex:"-1"},[i("Использование magiskboot "),e("a",{class:"header-anchor",href:"#using","aria-label":'Permalink to "Использование magiskboot {#using magiskboot}"'},"​")],-1),c=o('
  1. Загрузите последнюю версию Magisk с Release Page.
  2. Переименуйте Magisk-*.apk в Magisk-vesion.zip и разархивируйте его.
  3. Закачайте Magisk-v25.2/lib/arm64-v8a/libmagiskboot.so на устройство с помощью adb: adb push Magisk-v25.2/lib/arm64-v8a/libmagiskboot.so /data/local/tmp/magiskboot.
  4. Установите на устройство стоковый boot.img и образ в AnyKernel3.
  5. Войдите в оболочку adb и перейдите в каталог /data/local/tmp/, затем chmod +x magiskboot.
  6. Войдите в adb shell и cd директории /data/local/tmp/, выполните команду ./magiskboot unpack boot.img для распаковки boot.img, вы получите файл kernel, это и есть ваше стоковое ядро.
  7. Замените kernel на Image: mv -f Image kernel.
  8. Выполните команду ./magiskboot repack boot.img, чтобы перепаковать boot img, и получите файл new-boot.img, прошейте его на устройство с помощью fastboot.

Другие методы

На самом деле все эти способы установки имеют только одну основную идею - заменить исходное ядро на ядро, предоставляемое KernelSU; если это возможно, то установка возможна; например, возможны следующие способы.

  1. Сначала установить Magisk, получить права root через Magisk, а затем с помощью kernel flasher прошить AnyKernel zip из KernelSU.
  2. Использовать какой-либо инструментарий для прошивки на ПК, чтобы прошить ядро, предоставленное KernelSU.
',4),d=[n,s,c];function p(h,b,g,m,u,k){return l(),t("div",null,d)}const K=a(r,[["render",p]]);export{y as __pageData,K as default}; diff --git a/assets/ru_RU_guide_installation.md.bd641868.lean.js b/assets/ru_RU_guide_installation.md.ad88b078.lean.js similarity index 63% rename from assets/ru_RU_guide_installation.md.bd641868.lean.js rename to assets/ru_RU_guide_installation.md.ad88b078.lean.js index d89347ae441a..2c1334fe4fe6 100644 --- a/assets/ru_RU_guide_installation.md.bd641868.lean.js +++ b/assets/ru_RU_guide_installation.md.ad88b078.lean.js @@ -1 +1 @@ -import{_ as a,o as l,c as t,O as o,z as e,a as i}from"./chunks/framework.43781440.js";const y=JSON.parse('{"title":"Установка","description":"","frontmatter":{},"headers":[],"relativePath":"ru_RU/guide/installation.md","filePath":"ru_RU/guide/installation.md"}'),r={name:"ru_RU/guide/installation.md"},n=o("",58),s=e("h3",{id:"using",magiskboot:"",tabindex:"-1"},[i("Использование magiskboot "),e("a",{class:"header-anchor",href:"#using","aria-label":'Permalink to "Использование magiskboot {#using magiskboot}"'},"​")],-1),d=o("",4),c=[n,s,d];function p(h,b,g,m,u,k){return l(),t("div",null,c)}const K=a(r,[["render",p]]);export{y as __pageData,K as default}; +import{_ as a,o as l,c as t,Q as o,k as e,a as i}from"./chunks/framework.ec8f7e8e.js";const y=JSON.parse('{"title":"Установка","description":"","frontmatter":{},"headers":[],"relativePath":"ru_RU/guide/installation.md","filePath":"ru_RU/guide/installation.md"}'),r={name:"ru_RU/guide/installation.md"},n=o("",58),s=e("h3",{id:"using",magiskboot:"",tabindex:"-1"},[i("Использование magiskboot "),e("a",{class:"header-anchor",href:"#using","aria-label":'Permalink to "Использование magiskboot {#using magiskboot}"'},"​")],-1),c=o("",4),d=[n,s,c];function p(h,b,g,m,u,k){return l(),t("div",null,d)}const K=a(r,[["render",p]]);export{y as __pageData,K as default}; diff --git a/assets/ru_RU_guide_module.md.2cf2aec7.js b/assets/ru_RU_guide_module.md.19fabb40.js similarity index 62% rename from assets/ru_RU_guide_module.md.2cf2aec7.js rename to assets/ru_RU_guide_module.md.19fabb40.js index 5d7cc06a06b5..16bd9fb48109 100644 --- a/assets/ru_RU_guide_module.md.2cf2aec7.js +++ b/assets/ru_RU_guide_module.md.19fabb40.js @@ -1,81 +1,161 @@ -import{_ as s,o as e,c as a,O as n}from"./chunks/framework.43781440.js";const C=JSON.parse('{"title":"Руководство по разработке модулей","description":"","frontmatter":{},"headers":[],"relativePath":"ru_RU/guide/module.md","filePath":"ru_RU/guide/module.md"}'),l={name:"ru_RU/guide/module.md"},o=n(`

Руководство по разработке модулей

KernelSU предоставляет механизм модулей, позволяющий добиться эффекта модификации системного каталога при сохранении целостности системного раздела. Этот механизм принято называть "бессистемным".

Модульный механизм KernelSU практически аналогичен механизму Magisk. Если вы знакомы с разработкой модулей Magisk, то разработка модулей KernelSU очень похожа. Представление модулей ниже можно пропустить, достаточно прочитать [различия-с-magisk] (difference-with-magisk.md).

Busybox

В комплект поставки KernelSU входит полнофункциональный бинарный файл BusyBox (включая полную поддержку SELinux). Исполняемый файл находится по адресу /data/adb/ksu/bin/busybox. BusyBox от KernelSU поддерживает переключаемый во время работы "ASH Standalone Shell Mode". Этот автономный режим означает, что при запуске в оболочке ash BusyBox каждая команда будет напрямую использовать апплет внутри BusyBox, независимо от того, что задано в качестве PATH. Например, такие команды, как ls, rm, chmod будут НЕ использовать то, что находится в PATH (в случае Android по умолчанию это будут /system/bin/ls, /system/bin/rm и /system/bin/chmod соответственно), а вместо этого будут напрямую вызывать внутренние апплеты BusyBox. Это гарантирует, что скрипты всегда будут выполняться в предсказуемом окружении и всегда будут иметь полный набор команд, независимо от того, на какой версии Android они выполняются. Чтобы заставить команду не использовать BusyBox, необходимо вызвать исполняемый файл с полными путями.

Каждый сценарий оболочки, запущенный в контексте KernelSU, будет выполняться в оболочке BusyBox ash с включенным автономным режимом. Для сторонних разработчиков это касается всех загрузочных скриптов и скриптов установки модулей.

Для тех, кто хочет использовать эту возможность "Автономного режима" вне KernelSU, есть два способа включить ее:

  1. Установите переменной окружения ASH_STANDALONE значение 1
    Пример: ASH_STANDALONE=1 /data/adb/ksu/bin/busybox sh <script>
  2. Переключитесь с помощью параметров командной строки:
    /data/adb/ksu/bin/busybox sh -o standalone <script>

Чтобы убедиться, что все последующие запуски оболочки sh также выполняются в автономном режиме, предпочтительным методом является вариант 1 (и это то, что KernelSU и менеджер KernelSU используют внутри), поскольку переменные окружения наследуются вплоть до дочерних процессов.

отличие от Magisk

BusyBox в KernelSU теперь использует бинарный файл, скомпилированный непосредственно из проекта Magisk. **Поэтому вам не нужно беспокоиться о проблемах совместимости между скриптами BusyBox в Magisk и KernelSU, поскольку они абсолютно одинаковы!

Модули KernelSU

Модуль KernelSU - это папка, размещенная в каталоге /data/adb/modules и имеющая следующую структуру:

txt
/data/adb/modules
-├── .
-├── .
-|
-├── $MODID                  <--- Папка имеет имя с идентификатором модуля
-│   │
-│   │      *** Идентификация модуля ***
-│   │
-│   ├── module.prop         <--- В этом файле хранятся метаданные модуля
-│   │
-│   │      *** Основное содержимое ***
-│   │
-│   ├── system              <--- Эта папка будет смонтирована, если skip_mount не существует
-│   │   ├── ...
-│   │   ├── ...
-│   │   └── ...
-│   │
-│   │      *** Флаги состояния ***
-│   │
-│   ├── skip_mount          <--- Если он существует, то KernelSU НЕ будет монтировать вашу системную папку
-│   ├── disable             <--- Если модуль существует, то он будет отключен
-│   ├── remove              <--- Если модуль существует, то при следующей перезагрузке он будет удален
-│   │
-│   │      *** Необязательные файлы ***
-│   │
-│   ├── post-fs-data.sh     <--- Этот скрипт будет выполняться в post-fs-data
-│   ├── service.sh          <--- Этот скрипт будет выполняться в сервисе late_start
-|   ├── uninstall.sh        <--- Этот скрипт будет выполнен, когда KernelSU удалит ваш модуль
-│   ├── system.prop         <--- Свойства из этого файла будут загружены в качестве системных свойств программой resetprop
-│   ├── sepolicy.rule       <--- Дополнительные пользовательские правила sepolicy
-│   │
-│   │      *** Автоматически генерируется, НЕЛЬЗЯ создавать или изменять вручную ***
-│   │
-│   ├── vendor              <--- Символьная ссылка на $MODID/system/vendor
-│   ├── product             <--- Символьная ссылка на $MODID/system/product
-│   ├── system_ext          <--- Симлинк на $MODID/system/system_ext
-│   │
-│   │      *** Допускается использование любых дополнительных файлов/папок ***
-│   │
-│   ├── ...
-│   └── ...
-|
-├── another_module
-│   ├── .
-│   └── .
-├── .
-├── .

различия с Magisk

KernelSU не имеет встроенной поддержки Zygisk, поэтому в модуле нет содержимого, связанного с Zygisk. Однако для поддержки модулей Zygisk можно использовать ZygiskNext. В этом случае содержимое модуля Zygisk идентично содержимому, поддерживаемому Magisk.

module.prop

module.prop - это конфигурационный файл модуля. В KernelSU, если модуль не содержит этого файла, он не будет распознан как модуль. Формат этого файла следующий:

txt
id=<string>
-name=<string>
-version=<string>
-versionCode=<int>
-author=<string>
-description=<string>
  • id должно соответствовать данному регулярному выражению: ^[a-zA-Z][a-zA-Z0-9._-]+$
    экс: ✓ a_module, ✓ a.module, ✓ module-101, ✗ a module, ✗ 1_module, ✗ -a-module
    Это уникальный идентификатор вашего модуля. Не следует изменять его после публикации.
  • versionCode должен быть целым. Это используется для сравнения версий
  • Другими, не упомянутыми выше, могут быть любые однострочные строки.
  • Обязательно используйте тип перевода строки UNIX (LF), а не Windows (CR+LF) или Macintosh (CR).

Сценарии командной оболочки

Чтобы понять разницу между post-fs-data.sh и Service.sh, прочитайте раздел Boot Scripts. Для большинства разработчиков модулей service.sh должно быть достаточно, если вам нужно просто запустить загрузочный скрипт.

Во всех скриптах вашего модуля используйте MODDIR=\${0%/*} для получения пути к базовому каталогу вашего модуля; НЕ кодируйте жестко путь к вашему модулю в скриптах.

различия с Magisk

С помощью переменной окружения KSU можно определить, выполняется ли сценарий в KernelSU или в Magisk. Если скрипт выполняется в KernelSU, то это значение будет равно true.

каталог system

После загрузки системы содержимое этого каталога будет наложено поверх раздела /system с помощью overlayfs. Это означает, что:

  1. Файлы с теми же именами, что и в соответствующем каталоге в системе, будут перезаписаны файлами в этом каталоге.
  2. Папки с теми же именами, что и в соответствующем каталоге в системе, будут объединены с папками в этом каталоге.

Если вы хотите удалить файл или папку в исходном каталоге системы, необходимо создать файл с тем же именем, что и файл/папка, в каталоге модуля с помощью команды mknod filename c 0 0. Таким образом, система overlayfs автоматически "забелит" этот файл, как если бы он был удален (раздел /system при этом фактически не изменится).

Вы также можете объявить в customize.sh переменную с именем REMOVE, содержащую список каталогов для выполнения операций удаления, и KernelSU автоматически выполнит команду mknod <TARGET> c 0 0 в соответствующих каталогах модуля. Например:

sh
REMOVE="
-/system/app/YouTube
-/system/app/Bloatware
-"

В приведенном выше списке будут выполнены команды mknod $MODPATH/system/app/YouTuBe c 0 0 и mknod $MODPATH/system/app/Bloatware c 0 0; при этом /system/app/YouTube и /system/app/Bloatware будут удалены после вступления модуля в силу.

Если вы хотите заменить каталог в системе, то необходимо создать каталог с тем же путем в каталоге модуля, а затем установить для этого каталога атрибут setfattr -n trusted.overlay.opaque -v y <TARGET>. Таким образом, система overlayfs автоматически заменит соответствующий каталог в системе (без изменения раздела /system).

Вы можете объявить в файле customize.sh переменную с именем REPLACE, содержащую список заменяемых каталогов, и KernelSU автоматически выполнит соответствующие операции в каталоге вашего модуля. Например:

REPLACE=" /system/app/YouTube /system/app/Bloatware "

В этом списке будут автоматически созданы каталоги $MODPATH/system/app/YouTube и $MODPATH/system/app/Bloatware, а затем выполнены команды setfattr -n trusted.overlay.opaque -v y $MODPATH/system/app/YouTube и setfattr -n trusted.overlay.opaque -v y $MODPATH/system/app/Bloatware. После вступления модуля в силу каталоги /system/app/YouTube и /system/app/Bloatware будут заменены на пустые.

различия с Magisk

В KernelSU бессистемный механизм реализован через overlayfs ядра, а в Magisk в настоящее время используется магическое монтирование (bind mount). Эти два метода реализации имеют существенные различия, но конечная цель у них одна: модификация файлов /system без физического изменения раздела /system.

Если вы заинтересованы в использовании overlayfs, рекомендуется прочитать документацию по overlayfs ядра Linux.

system.prop

Этот файл имеет тот же формат, что и build.prop. Каждая строка состоит из [key]=[value].

sepolicy.rule

Если для вашего модуля требуются дополнительные патчи sepolicy, добавьте эти правила в данный файл. Каждая строка в этом файле будет рассматриваться как утверждение политики.

Установщик модулей

Инсталлятор модуля KernelSU - это модуль KernelSU, упакованный в zip-файл, который может быть прошит в APP-менеджере KernelSU. Простейший установщик модуля KernelSU - это просто модуль KernelSU, упакованный в zip-файл.

txt
module.zip
-
-├── customize.sh                       <--- (Необязательно, более подробно позже)
-│                                           Этот скрипт будет использоваться в update-binary
-├── ...
-├── ...  /* Остальные файлы модуля */
-

WARNING

Модуль KernelSU НЕ поддерживается для установки в пользовательское Recovery!!!

Персонализация

Если вам необходимо настроить процесс установки модуля, то в качестве опции вы можете создать в программе установки скрипт с именем customize.sh. Этот скрипт будет источником (не исполняться!) сценария установщика модуля после извлечения всех файлов и применения стандартных разрешений и secontext. Это очень удобно, если ваш модуль требует дополнительной настройки в зависимости от ABI устройства, или вам необходимо установить специальные разрешения/секонтекст для некоторых файлов модуля.

Если вы хотите полностью контролировать и настраивать процесс установки, объявите SKIPUNZIP=1 в файле customize.sh, чтобы пропустить все шаги установки по умолчанию. При этом ваш customize.sh будет сам отвечать за установку.

Сценарий customize.sh запускается в оболочке BusyBox ash KernelSU с включенным "Автономным режимом". Доступны следующие переменные и функции:

Переменные

  • KSU (bool): переменная, отмечающая, что скрипт выполняется в окружении KernelSU, причем значение этой переменной всегда будет true. Ее можно использовать для различения KernelSU и Magisk.
  • KSU_VER (string): строка версии текущего установленного KernelSU (например, v0.4.0)
  • KSU_VER_CODE (int): код версии текущего установленного KernelSU в пользовательском пространстве (например, 10672)
  • KSU_KERNEL_VER_CODE (int): код версии текущей установленной KernelSU в пространстве ядра (например, 10672)
  • BOOTMODE (bool): в KernelSU всегда должно быть true.
  • MODPATH (path): путь, по которому должны быть установлены файлы ваших модулей
  • TMPDIR (path): место, где вы можете временно хранить файлы
  • ZIPFILE (path): установочный zip-архив вашего модуля
  • ARCH (string): архитектура процессора устройства. Значение: arm, arm64, x86 или x64.
  • IS64BIT (bool): true, если $ARCH имеет значение arm64 или x64.
  • API (int): уровень API (версия Android) устройства (например, 23 для Android 6.0)

WARNING

В KernelSU MAGISK_VER_CODE всегда равен 25200, а MAGISK_VER всегда равен v25.2. Пожалуйста, не используйте эти две переменные для определения того, запущен ли он на KernelSU или нет.

Функции

txt
ui_print <msg>
-    вывести <msg> на консоль
-    Избегайте использования 'echo', так как он не будет отображаться в консоли пользовательского recovery
-
-abort <msg>
-    вывести сообщение об ошибке <msg> на консоль и завершить установку
-    Избегайте использования команды 'exit', так как в этом случае будут пропущены шаги очистки завершения установки
-
-set_perm <target> <owner> <group> <permission> [context]
-    если [context] не задан, то по умолчанию используется "u:object_r:system_file:s0".
-    Эта функция является сокращением для следующих команд:
-       chown owner.group target
-       chmod permission target
-       chcon context target
-
-set_perm_recursive <directory> <owner> <group> <dirpermission> <filepermission> [context]
-    если [context] не задан, то по умолчанию используется "u:object_r:system_file:s0".
-    для всех файлов в <directory> будет вызвана команда:
-       set_perm file owner group filepermission context
-    для всех каталогов в <directory> (включая себя самого), он вызовет:
-       set_perm dir owner group dirpermission context

Загрузочные сценарии

В KernelSU скрипты делятся на два типа в зависимости от режима их работы: режим post-fs-data и режим late_start service:

  • режим post-fs-data
    • Эта стадия является БЛОКИРУЮЩЕЙ. Процесс загрузки приостанавливается до завершения выполнения или по истечении 10 секунд.
    • Сценарии запускаются до того, как будут смонтированы какие-либо модули. Это позволяет разработчику модулей динамически настраивать свои модули до того, как они будут смонтированы.
    • Этот этап происходит до запуска Zygote, что практически означает, что все в Android
    • ПРЕДУПРЕЖДЕНИЕ: использование setprop приведет к блокировке процесса загрузки! Вместо этого используйте resetprop -n <prop_name> <prop_value>.
    • Запускайте скрипты в этом режиме только в случае необходимости.
  • режим обслуживания late_start
    • Эта стадия является НЕБЛОКИРУЮЩЕЙ. Ваш скрипт выполняется параллельно с остальным процессом загрузки.
    • Это рекомендуемый этап для запуска большинства скриптов.

В KernelSU скрипты запуска делятся на два типа по месту их хранения: общие скрипты и скрипты модулей:

  • Общие скрипты
    • Размещаются в файлах /data/adb/post-fs-data.d или /data/adb/service.d.
    • Выполняется только в том случае, если скрипт установлен как исполняемый (chmod +x script.sh)
    • Скрипты в post-fs-data.d выполняются в режиме post-fs-data, а скрипты в service.d - в режиме late_start service.
    • Модули не должны НЕ добавлять общие скрипты при установке
  • Скрипты модуля
    • Размещаются в отдельной папке модуля
    • Выполняются только в том случае, если модуль включен
    • post-fs-data.sh запускается в режиме post-fs-data, а service.sh - в режиме late_start service.

Все загрузочные скрипты будут выполняться в оболочке BusyBox ash от KernelSU с включенным "Автономным режимом".

`,58),t=[o];function p(c,i,r,d,u,A){return e(),a("div",null,t)}const m=s(l,[["render",p]]);export{C as __pageData,m as default}; +import{_ as s,o as e,c as n,Q as a}from"./chunks/framework.ec8f7e8e.js";const g=JSON.parse('{"title":"Руководство по разработке модулей","description":"","frontmatter":{},"headers":[],"relativePath":"ru_RU/guide/module.md","filePath":"ru_RU/guide/module.md"}'),l={name:"ru_RU/guide/module.md"},o=a(`

Руководство по разработке модулей

KernelSU предоставляет механизм модулей, позволяющий добиться эффекта модификации системного каталога при сохранении целостности системного раздела. Этот механизм принято называть "бессистемным".

Модульный механизм KernelSU практически аналогичен механизму Magisk. Если вы знакомы с разработкой модулей Magisk, то разработка модулей KernelSU очень похожа. Представление модулей ниже можно пропустить, достаточно прочитать [различия-с-magisk] (difference-with-magisk.md).

Busybox

В комплект поставки KernelSU входит полнофункциональный бинарный файл BusyBox (включая полную поддержку SELinux). Исполняемый файл находится по адресу /data/adb/ksu/bin/busybox. BusyBox от KernelSU поддерживает переключаемый во время работы "ASH Standalone Shell Mode". Этот автономный режим означает, что при запуске в оболочке ash BusyBox каждая команда будет напрямую использовать апплет внутри BusyBox, независимо от того, что задано в качестве PATH. Например, такие команды, как ls, rm, chmod будут НЕ использовать то, что находится в PATH (в случае Android по умолчанию это будут /system/bin/ls, /system/bin/rm и /system/bin/chmod соответственно), а вместо этого будут напрямую вызывать внутренние апплеты BusyBox. Это гарантирует, что скрипты всегда будут выполняться в предсказуемом окружении и всегда будут иметь полный набор команд, независимо от того, на какой версии Android они выполняются. Чтобы заставить команду не использовать BusyBox, необходимо вызвать исполняемый файл с полными путями.

Каждый сценарий оболочки, запущенный в контексте KernelSU, будет выполняться в оболочке BusyBox ash с включенным автономным режимом. Для сторонних разработчиков это касается всех загрузочных скриптов и скриптов установки модулей.

Для тех, кто хочет использовать эту возможность "Автономного режима" вне KernelSU, есть два способа включить ее:

  1. Установите переменной окружения ASH_STANDALONE значение 1
    Пример: ASH_STANDALONE=1 /data/adb/ksu/bin/busybox sh <script>
  2. Переключитесь с помощью параметров командной строки:
    /data/adb/ksu/bin/busybox sh -o standalone <script>

Чтобы убедиться, что все последующие запуски оболочки sh также выполняются в автономном режиме, предпочтительным методом является вариант 1 (и это то, что KernelSU и менеджер KernelSU используют внутри), поскольку переменные окружения наследуются вплоть до дочерних процессов.

отличие от Magisk

BusyBox в KernelSU теперь использует бинарный файл, скомпилированный непосредственно из проекта Magisk. **Поэтому вам не нужно беспокоиться о проблемах совместимости между скриптами BusyBox в Magisk и KernelSU, поскольку они абсолютно одинаковы!

Модули KernelSU

Модуль KernelSU - это папка, размещенная в каталоге /data/adb/modules и имеющая следующую структуру:

txt
/data/adb/modules
+├── .
+├── .
+|
+├── $MODID                  <--- Папка имеет имя с идентификатором модуля
+│   │
+│   │      *** Идентификация модуля ***
+│   │
+│   ├── module.prop         <--- В этом файле хранятся метаданные модуля
+│   │
+│   │      *** Основное содержимое ***
+│   │
+│   ├── system              <--- Эта папка будет смонтирована, если skip_mount не существует
+│   │   ├── ...
+│   │   ├── ...
+│   │   └── ...
+│   │
+│   │      *** Флаги состояния ***
+│   │
+│   ├── skip_mount          <--- Если он существует, то KernelSU НЕ будет монтировать вашу системную папку
+│   ├── disable             <--- Если модуль существует, то он будет отключен
+│   ├── remove              <--- Если модуль существует, то при следующей перезагрузке он будет удален
+│   │
+│   │      *** Необязательные файлы ***
+│   │
+│   ├── post-fs-data.sh     <--- Этот скрипт будет выполняться в post-fs-data
+│   ├── service.sh          <--- Этот скрипт будет выполняться в сервисе late_start
+|   ├── uninstall.sh        <--- Этот скрипт будет выполнен, когда KernelSU удалит ваш модуль
+│   ├── system.prop         <--- Свойства из этого файла будут загружены в качестве системных свойств программой resetprop
+│   ├── sepolicy.rule       <--- Дополнительные пользовательские правила sepolicy
+│   │
+│   │      *** Автоматически генерируется, НЕЛЬЗЯ создавать или изменять вручную ***
+│   │
+│   ├── vendor              <--- Символьная ссылка на $MODID/system/vendor
+│   ├── product             <--- Символьная ссылка на $MODID/system/product
+│   ├── system_ext          <--- Симлинк на $MODID/system/system_ext
+│   │
+│   │      *** Допускается использование любых дополнительных файлов/папок ***
+│   │
+│   ├── ...
+│   └── ...
+|
+├── another_module
+│   ├── .
+│   └── .
+├── .
+├── .
/data/adb/modules
+├── .
+├── .
+|
+├── $MODID                  <--- Папка имеет имя с идентификатором модуля
+│   │
+│   │      *** Идентификация модуля ***
+│   │
+│   ├── module.prop         <--- В этом файле хранятся метаданные модуля
+│   │
+│   │      *** Основное содержимое ***
+│   │
+│   ├── system              <--- Эта папка будет смонтирована, если skip_mount не существует
+│   │   ├── ...
+│   │   ├── ...
+│   │   └── ...
+│   │
+│   │      *** Флаги состояния ***
+│   │
+│   ├── skip_mount          <--- Если он существует, то KernelSU НЕ будет монтировать вашу системную папку
+│   ├── disable             <--- Если модуль существует, то он будет отключен
+│   ├── remove              <--- Если модуль существует, то при следующей перезагрузке он будет удален
+│   │
+│   │      *** Необязательные файлы ***
+│   │
+│   ├── post-fs-data.sh     <--- Этот скрипт будет выполняться в post-fs-data
+│   ├── service.sh          <--- Этот скрипт будет выполняться в сервисе late_start
+|   ├── uninstall.sh        <--- Этот скрипт будет выполнен, когда KernelSU удалит ваш модуль
+│   ├── system.prop         <--- Свойства из этого файла будут загружены в качестве системных свойств программой resetprop
+│   ├── sepolicy.rule       <--- Дополнительные пользовательские правила sepolicy
+│   │
+│   │      *** Автоматически генерируется, НЕЛЬЗЯ создавать или изменять вручную ***
+│   │
+│   ├── vendor              <--- Символьная ссылка на $MODID/system/vendor
+│   ├── product             <--- Символьная ссылка на $MODID/system/product
+│   ├── system_ext          <--- Симлинк на $MODID/system/system_ext
+│   │
+│   │      *** Допускается использование любых дополнительных файлов/папок ***
+│   │
+│   ├── ...
+│   └── ...
+|
+├── another_module
+│   ├── .
+│   └── .
+├── .
+├── .

различия с Magisk

KernelSU не имеет встроенной поддержки Zygisk, поэтому в модуле нет содержимого, связанного с Zygisk. Однако для поддержки модулей Zygisk можно использовать ZygiskNext. В этом случае содержимое модуля Zygisk идентично содержимому, поддерживаемому Magisk.

module.prop

module.prop - это конфигурационный файл модуля. В KernelSU, если модуль не содержит этого файла, он не будет распознан как модуль. Формат этого файла следующий:

txt
id=<string>
+name=<string>
+version=<string>
+versionCode=<int>
+author=<string>
+description=<string>
id=<string>
+name=<string>
+version=<string>
+versionCode=<int>
+author=<string>
+description=<string>
  • id должно соответствовать данному регулярному выражению: ^[a-zA-Z][a-zA-Z0-9._-]+$
    экс: ✓ a_module, ✓ a.module, ✓ module-101, ✗ a module, ✗ 1_module, ✗ -a-module
    Это уникальный идентификатор вашего модуля. Не следует изменять его после публикации.
  • versionCode должен быть целым. Это используется для сравнения версий
  • Другими, не упомянутыми выше, могут быть любые однострочные строки.
  • Обязательно используйте тип перевода строки UNIX (LF), а не Windows (CR+LF) или Macintosh (CR).

Сценарии командной оболочки

Чтобы понять разницу между post-fs-data.sh и Service.sh, прочитайте раздел Boot Scripts. Для большинства разработчиков модулей service.sh должно быть достаточно, если вам нужно просто запустить загрузочный скрипт.

Во всех скриптах вашего модуля используйте MODDIR=\${0%/*} для получения пути к базовому каталогу вашего модуля; НЕ кодируйте жестко путь к вашему модулю в скриптах.

различия с Magisk

С помощью переменной окружения KSU можно определить, выполняется ли сценарий в KernelSU или в Magisk. Если скрипт выполняется в KernelSU, то это значение будет равно true.

каталог system

После загрузки системы содержимое этого каталога будет наложено поверх раздела /system с помощью overlayfs. Это означает, что:

  1. Файлы с теми же именами, что и в соответствующем каталоге в системе, будут перезаписаны файлами в этом каталоге.
  2. Папки с теми же именами, что и в соответствующем каталоге в системе, будут объединены с папками в этом каталоге.

Если вы хотите удалить файл или папку в исходном каталоге системы, необходимо создать файл с тем же именем, что и файл/папка, в каталоге модуля с помощью команды mknod filename c 0 0. Таким образом, система overlayfs автоматически "забелит" этот файл, как если бы он был удален (раздел /system при этом фактически не изменится).

Вы также можете объявить в customize.sh переменную с именем REMOVE, содержащую список каталогов для выполнения операций удаления, и KernelSU автоматически выполнит команду mknod <TARGET> c 0 0 в соответствующих каталогах модуля. Например:

sh
REMOVE="
+/system/app/YouTube
+/system/app/Bloatware
+"
REMOVE="
+/system/app/YouTube
+/system/app/Bloatware
+"

В приведенном выше списке будут выполнены команды mknod $MODPATH/system/app/YouTuBe c 0 0 и mknod $MODPATH/system/app/Bloatware c 0 0; при этом /system/app/YouTube и /system/app/Bloatware будут удалены после вступления модуля в силу.

Если вы хотите заменить каталог в системе, то необходимо создать каталог с тем же путем в каталоге модуля, а затем установить для этого каталога атрибут setfattr -n trusted.overlay.opaque -v y <TARGET>. Таким образом, система overlayfs автоматически заменит соответствующий каталог в системе (без изменения раздела /system).

Вы можете объявить в файле customize.sh переменную с именем REPLACE, содержащую список заменяемых каталогов, и KernelSU автоматически выполнит соответствующие операции в каталоге вашего модуля. Например:

REPLACE=" /system/app/YouTube /system/app/Bloatware "

В этом списке будут автоматически созданы каталоги $MODPATH/system/app/YouTube и $MODPATH/system/app/Bloatware, а затем выполнены команды setfattr -n trusted.overlay.opaque -v y $MODPATH/system/app/YouTube и setfattr -n trusted.overlay.opaque -v y $MODPATH/system/app/Bloatware. После вступления модуля в силу каталоги /system/app/YouTube и /system/app/Bloatware будут заменены на пустые.

различия с Magisk

В KernelSU бессистемный механизм реализован через overlayfs ядра, а в Magisk в настоящее время используется магическое монтирование (bind mount). Эти два метода реализации имеют существенные различия, но конечная цель у них одна: модификация файлов /system без физического изменения раздела /system.

Если вы заинтересованы в использовании overlayfs, рекомендуется прочитать документацию по overlayfs ядра Linux.

system.prop

Этот файл имеет тот же формат, что и build.prop. Каждая строка состоит из [key]=[value].

sepolicy.rule

Если для вашего модуля требуются дополнительные патчи sepolicy, добавьте эти правила в данный файл. Каждая строка в этом файле будет рассматриваться как утверждение политики.

Установщик модулей

Инсталлятор модуля KernelSU - это модуль KernelSU, упакованный в zip-файл, который может быть прошит в APP-менеджере KernelSU. Простейший установщик модуля KernelSU - это просто модуль KernelSU, упакованный в zip-файл.

txt
module.zip
+
+├── customize.sh                       <--- (Необязательно, более подробно позже)
+│                                           Этот скрипт будет использоваться в update-binary
+├── ...
+├── ...  /* Остальные файлы модуля */
+
module.zip
+
+├── customize.sh                       <--- (Необязательно, более подробно позже)
+│                                           Этот скрипт будет использоваться в update-binary
+├── ...
+├── ...  /* Остальные файлы модуля */
+

WARNING

Модуль KernelSU НЕ поддерживается для установки в пользовательское Recovery!!!

Персонализация

Если вам необходимо настроить процесс установки модуля, то в качестве опции вы можете создать в программе установки скрипт с именем customize.sh. Этот скрипт будет источником (не исполняться!) сценария установщика модуля после извлечения всех файлов и применения стандартных разрешений и secontext. Это очень удобно, если ваш модуль требует дополнительной настройки в зависимости от ABI устройства, или вам необходимо установить специальные разрешения/секонтекст для некоторых файлов модуля.

Если вы хотите полностью контролировать и настраивать процесс установки, объявите SKIPUNZIP=1 в файле customize.sh, чтобы пропустить все шаги установки по умолчанию. При этом ваш customize.sh будет сам отвечать за установку.

Сценарий customize.sh запускается в оболочке BusyBox ash KernelSU с включенным "Автономным режимом". Доступны следующие переменные и функции:

Переменные

  • KSU (bool): переменная, отмечающая, что скрипт выполняется в окружении KernelSU, причем значение этой переменной всегда будет true. Ее можно использовать для различения KernelSU и Magisk.
  • KSU_VER (string): строка версии текущего установленного KernelSU (например, v0.4.0)
  • KSU_VER_CODE (int): код версии текущего установленного KernelSU в пользовательском пространстве (например, 10672)
  • KSU_KERNEL_VER_CODE (int): код версии текущей установленной KernelSU в пространстве ядра (например, 10672)
  • BOOTMODE (bool): в KernelSU всегда должно быть true.
  • MODPATH (path): путь, по которому должны быть установлены файлы ваших модулей
  • TMPDIR (path): место, где вы можете временно хранить файлы
  • ZIPFILE (path): установочный zip-архив вашего модуля
  • ARCH (string): архитектура процессора устройства. Значение: arm, arm64, x86 или x64.
  • IS64BIT (bool): true, если $ARCH имеет значение arm64 или x64.
  • API (int): уровень API (версия Android) устройства (например, 23 для Android 6.0)

WARNING

В KernelSU MAGISK_VER_CODE всегда равен 25200, а MAGISK_VER всегда равен v25.2. Пожалуйста, не используйте эти две переменные для определения того, запущен ли он на KernelSU или нет.

Функции

txt
ui_print <msg>
+    вывести <msg> на консоль
+    Избегайте использования 'echo', так как он не будет отображаться в консоли пользовательского recovery
+
+abort <msg>
+    вывести сообщение об ошибке <msg> на консоль и завершить установку
+    Избегайте использования команды 'exit', так как в этом случае будут пропущены шаги очистки завершения установки
+
+set_perm <target> <owner> <group> <permission> [context]
+    если [context] не задан, то по умолчанию используется "u:object_r:system_file:s0".
+    Эта функция является сокращением для следующих команд:
+       chown owner.group target
+       chmod permission target
+       chcon context target
+
+set_perm_recursive <directory> <owner> <group> <dirpermission> <filepermission> [context]
+    если [context] не задан, то по умолчанию используется "u:object_r:system_file:s0".
+    для всех файлов в <directory> будет вызвана команда:
+       set_perm file owner group filepermission context
+    для всех каталогов в <directory> (включая себя самого), он вызовет:
+       set_perm dir owner group dirpermission context
ui_print <msg>
+    вывести <msg> на консоль
+    Избегайте использования 'echo', так как он не будет отображаться в консоли пользовательского recovery
+
+abort <msg>
+    вывести сообщение об ошибке <msg> на консоль и завершить установку
+    Избегайте использования команды 'exit', так как в этом случае будут пропущены шаги очистки завершения установки
+
+set_perm <target> <owner> <group> <permission> [context]
+    если [context] не задан, то по умолчанию используется "u:object_r:system_file:s0".
+    Эта функция является сокращением для следующих команд:
+       chown owner.group target
+       chmod permission target
+       chcon context target
+
+set_perm_recursive <directory> <owner> <group> <dirpermission> <filepermission> [context]
+    если [context] не задан, то по умолчанию используется "u:object_r:system_file:s0".
+    для всех файлов в <directory> будет вызвана команда:
+       set_perm file owner group filepermission context
+    для всех каталогов в <directory> (включая себя самого), он вызовет:
+       set_perm dir owner group dirpermission context

Загрузочные сценарии

В KernelSU скрипты делятся на два типа в зависимости от режима их работы: режим post-fs-data и режим late_start service:

  • режим post-fs-data
    • Эта стадия является БЛОКИРУЮЩЕЙ. Процесс загрузки приостанавливается до завершения выполнения или по истечении 10 секунд.
    • Сценарии запускаются до того, как будут смонтированы какие-либо модули. Это позволяет разработчику модулей динамически настраивать свои модули до того, как они будут смонтированы.
    • Этот этап происходит до запуска Zygote, что практически означает, что все в Android
    • ПРЕДУПРЕЖДЕНИЕ: использование setprop приведет к блокировке процесса загрузки! Вместо этого используйте resetprop -n <prop_name> <prop_value>.
    • Запускайте скрипты в этом режиме только в случае необходимости.
  • режим обслуживания late_start
    • Эта стадия является НЕБЛОКИРУЮЩЕЙ. Ваш скрипт выполняется параллельно с остальным процессом загрузки.
    • Это рекомендуемый этап для запуска большинства скриптов.

В KernelSU скрипты запуска делятся на два типа по месту их хранения: общие скрипты и скрипты модулей:

  • Общие скрипты
    • Размещаются в файлах /data/adb/post-fs-data.d или /data/adb/service.d.
    • Выполняется только в том случае, если скрипт установлен как исполняемый (chmod +x script.sh)
    • Скрипты в post-fs-data.d выполняются в режиме post-fs-data, а скрипты в service.d - в режиме late_start service.
    • Модули не должны НЕ добавлять общие скрипты при установке
  • Скрипты модуля
    • Размещаются в отдельной папке модуля
    • Выполняются только в том случае, если модуль включен
    • post-fs-data.sh запускается в режиме post-fs-data, а service.sh - в режиме late_start service.

Все загрузочные скрипты будут выполняться в оболочке BusyBox ash от KernelSU с включенным "Автономным режимом".

`,58),p=[o];function t(c,i,r,d,y,u){return e(),n("div",null,p)}const h=s(l,[["render",t]]);export{g as __pageData,h as default}; diff --git a/assets/ru_RU_guide_module.md.19fabb40.lean.js b/assets/ru_RU_guide_module.md.19fabb40.lean.js new file mode 100644 index 000000000000..e72e1bffeca9 --- /dev/null +++ b/assets/ru_RU_guide_module.md.19fabb40.lean.js @@ -0,0 +1 @@ +import{_ as s,o as e,c as n,Q as a}from"./chunks/framework.ec8f7e8e.js";const g=JSON.parse('{"title":"Руководство по разработке модулей","description":"","frontmatter":{},"headers":[],"relativePath":"ru_RU/guide/module.md","filePath":"ru_RU/guide/module.md"}'),l={name:"ru_RU/guide/module.md"},o=a("",58),p=[o];function t(c,i,r,d,y,u){return e(),n("div",null,p)}const h=s(l,[["render",t]]);export{g as __pageData,h as default}; diff --git a/assets/ru_RU_guide_module.md.2cf2aec7.lean.js b/assets/ru_RU_guide_module.md.2cf2aec7.lean.js deleted file mode 100644 index 40c023f5c0aa..000000000000 --- a/assets/ru_RU_guide_module.md.2cf2aec7.lean.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as s,o as e,c as a,O as n}from"./chunks/framework.43781440.js";const C=JSON.parse('{"title":"Руководство по разработке модулей","description":"","frontmatter":{},"headers":[],"relativePath":"ru_RU/guide/module.md","filePath":"ru_RU/guide/module.md"}'),l={name:"ru_RU/guide/module.md"},o=n("",58),t=[o];function p(c,i,r,d,u,A){return e(),a("div",null,t)}const m=s(l,[["render",p]]);export{C as __pageData,m as default}; diff --git a/assets/ru_RU_guide_rescue-from-bootloop.md.d9d6936e.js b/assets/ru_RU_guide_rescue-from-bootloop.md.c45eb81e.js similarity index 98% rename from assets/ru_RU_guide_rescue-from-bootloop.md.d9d6936e.js rename to assets/ru_RU_guide_rescue-from-bootloop.md.c45eb81e.js index 8824d6ad57c2..bd3568363b0d 100644 --- a/assets/ru_RU_guide_rescue-from-bootloop.md.d9d6936e.js +++ b/assets/ru_RU_guide_rescue-from-bootloop.md.c45eb81e.js @@ -1 +1 @@ -import{_ as o,o as e,c as a,O as t}from"./chunks/framework.43781440.js";const m=JSON.parse('{"title":"Выход из циклической загрузки","description":"","frontmatter":{},"headers":[],"relativePath":"ru_RU/guide/rescue-from-bootloop.md","filePath":"ru_RU/guide/rescue-from-bootloop.md"}'),r={name:"ru_RU/guide/rescue-from-bootloop.md"},l=t('

Выход из циклической загрузки

При прошивке устройства могут возникать ситуации, когда устройство становится "окирпиченным". Теоретически, если использовать fastboot только для прошивки загрузочного раздела или установить неподходящие модули, из-за которых устройство не загружается, то это можно восстановить соответствующими операциями. В данном документе описаны некоторые экстренные методы восстановления работоспособности "окирпиченного" устройства.

Кирпич путем перепрошивки загрузочного раздела

В KernelSU при прошивке загрузочного раздела могут возникнуть следующие ситуации:

  1. Загрузочный образ прошивается в неправильном формате. Например, если формат загрузки телефона - gz, а вы прошили образ в формате lz4, то телефон не сможет загрузиться.
  2. Для корректной загрузки телефона необходимо отключить проверку AVB (обычно для этого требуется стереть все данные на телефоне).
  3. Ядро содержит ошибки или не подходит для прошивки телефона.

Независимо от ситуации, восстановить работоспособность можно путем прошивки стокового загрузочного образа. Поэтому в начале руководства по установке мы настоятельно рекомендуем создать резервную копию стокового загрузочного образа перед прошивкой. Если у вас нет резервной копии, вы можете получить оригинальную заводскую загрузку от других пользователей с таким же устройством, как у вас, или из официальной прошивки.

Окирпичивание из-за модулей

Установка модулей может быть более распространенной причиной окирпичивания устройства, но мы должны серьезно предупредить вас: Не устанавливайте модули из неизвестных источников! Поскольку модули обладают правами root, они могут нанести непоправимый ущерб вашему устройству!

Нормальные модули

Если вы прошили модуль, безопасность которого доказана, но он приводит к невозможности загрузки устройства, то такая ситуация легко восстанавливается в KernelSU без каких-либо проблем. KernelSU имеет встроенные механизмы для спасения устройства, в том числе следующие:

  1. Обновление AB
  2. Восстановление при нажатии клавиши уменьшения громкости

AB-обновление

Механизм обновления модулей в KernelSU основан на механизме AB-обновления, используемом в OTA-обновлениях системы Android. При установке нового модуля или обновлении существующего он не будет напрямую изменять текущий файл модуля. Вместо этого все модули будут встроены в другой образ обновления. После перезагрузки системы она попытается начать использовать этот образ обновления. Если система Android успешно загрузится, то модули будут действительно обновлены.

Поэтому самым простым и наиболее часто используемым методом спасения устройства является принудительная перезагрузка. Если после прошивки модуля не удается запустить систему, можно нажать и удерживать кнопку питания более 10 секунд, после чего система автоматически перезагрузится; после перезагрузки произойдет откат к состоянию до обновления модуля, а ранее обновленные модули будут автоматически отключены.

Спасение, с зажатой клавишей уменьшения громкости

Если обновление AB не помогло решить проблему, можно попробовать использовать Безопасный режим. В безопасном режиме все модули отключены.

Войти в безопасный режим можно двумя способами:

  1. Встроенный безопасный режим некоторых систем; некоторые системы имеют встроенный безопасный режим, доступ к которому осуществляется долгим нажатием кнопки уменьшения громкости, в то время как другие (например, MIUI) могут включить безопасный режим в Recovery. При входе в безопасный режим системы KernelSU также переходит в безопасный режим и автоматически отключает модули.
  2. Встроенный безопасный режим KernelSU; метод работы заключается в том, что после первого экрана загрузки необходимо непрерывно нажать клавишу уменьшения громкости более трех раз. Обратите внимание, что именно нажать-отпустить, нажать-отпустить, нажать-отпустить, а не нажать и удерживать.

После входа в безопасный режим все модули на странице модулей менеджера KernelSU Manager отключаются, но можно выполнить операцию "деинсталляция" для удаления модулей, которые могут вызывать проблемы.

Встроенный безопасный режим реализован в ядре, поэтому вероятность пропуска ключевых событий из-за перехвата исключена. Однако для ядер, отличных от ГКИ, может потребоваться ручная интеграция кода, и за рекомендациями можно обратиться к официальной документации.

Вредоносные модули

Если описанные выше способы не помогли спасти устройство, то высока вероятность того, что установленный модуль имеет вредоносные операции или повредил устройство иным способом. В этом случае есть только два варианта:

  1. Стереть данные и прошить официальную систему.
  2. Обратиться в сервисную службу.
',23),i=[l];function n(d,s,u,c,p,h){return e(),a("div",null,i)}const b=o(r,[["render",n]]);export{m as __pageData,b as default}; +import{_ as o,o as e,c as a,Q as t}from"./chunks/framework.ec8f7e8e.js";const m=JSON.parse('{"title":"Выход из циклической загрузки","description":"","frontmatter":{},"headers":[],"relativePath":"ru_RU/guide/rescue-from-bootloop.md","filePath":"ru_RU/guide/rescue-from-bootloop.md"}'),r={name:"ru_RU/guide/rescue-from-bootloop.md"},l=t('

Выход из циклической загрузки

При прошивке устройства могут возникать ситуации, когда устройство становится "окирпиченным". Теоретически, если использовать fastboot только для прошивки загрузочного раздела или установить неподходящие модули, из-за которых устройство не загружается, то это можно восстановить соответствующими операциями. В данном документе описаны некоторые экстренные методы восстановления работоспособности "окирпиченного" устройства.

Кирпич путем перепрошивки загрузочного раздела

В KernelSU при прошивке загрузочного раздела могут возникнуть следующие ситуации:

  1. Загрузочный образ прошивается в неправильном формате. Например, если формат загрузки телефона - gz, а вы прошили образ в формате lz4, то телефон не сможет загрузиться.
  2. Для корректной загрузки телефона необходимо отключить проверку AVB (обычно для этого требуется стереть все данные на телефоне).
  3. Ядро содержит ошибки или не подходит для прошивки телефона.

Независимо от ситуации, восстановить работоспособность можно путем прошивки стокового загрузочного образа. Поэтому в начале руководства по установке мы настоятельно рекомендуем создать резервную копию стокового загрузочного образа перед прошивкой. Если у вас нет резервной копии, вы можете получить оригинальную заводскую загрузку от других пользователей с таким же устройством, как у вас, или из официальной прошивки.

Окирпичивание из-за модулей

Установка модулей может быть более распространенной причиной окирпичивания устройства, но мы должны серьезно предупредить вас: Не устанавливайте модули из неизвестных источников! Поскольку модули обладают правами root, они могут нанести непоправимый ущерб вашему устройству!

Нормальные модули

Если вы прошили модуль, безопасность которого доказана, но он приводит к невозможности загрузки устройства, то такая ситуация легко восстанавливается в KernelSU без каких-либо проблем. KernelSU имеет встроенные механизмы для спасения устройства, в том числе следующие:

  1. Обновление AB
  2. Восстановление при нажатии клавиши уменьшения громкости

AB-обновление

Механизм обновления модулей в KernelSU основан на механизме AB-обновления, используемом в OTA-обновлениях системы Android. При установке нового модуля или обновлении существующего он не будет напрямую изменять текущий файл модуля. Вместо этого все модули будут встроены в другой образ обновления. После перезагрузки системы она попытается начать использовать этот образ обновления. Если система Android успешно загрузится, то модули будут действительно обновлены.

Поэтому самым простым и наиболее часто используемым методом спасения устройства является принудительная перезагрузка. Если после прошивки модуля не удается запустить систему, можно нажать и удерживать кнопку питания более 10 секунд, после чего система автоматически перезагрузится; после перезагрузки произойдет откат к состоянию до обновления модуля, а ранее обновленные модули будут автоматически отключены.

Спасение, с зажатой клавишей уменьшения громкости

Если обновление AB не помогло решить проблему, можно попробовать использовать Безопасный режим. В безопасном режиме все модули отключены.

Войти в безопасный режим можно двумя способами:

  1. Встроенный безопасный режим некоторых систем; некоторые системы имеют встроенный безопасный режим, доступ к которому осуществляется долгим нажатием кнопки уменьшения громкости, в то время как другие (например, MIUI) могут включить безопасный режим в Recovery. При входе в безопасный режим системы KernelSU также переходит в безопасный режим и автоматически отключает модули.
  2. Встроенный безопасный режим KernelSU; метод работы заключается в том, что после первого экрана загрузки необходимо непрерывно нажать клавишу уменьшения громкости более трех раз. Обратите внимание, что именно нажать-отпустить, нажать-отпустить, нажать-отпустить, а не нажать и удерживать.

После входа в безопасный режим все модули на странице модулей менеджера KernelSU Manager отключаются, но можно выполнить операцию "деинсталляция" для удаления модулей, которые могут вызывать проблемы.

Встроенный безопасный режим реализован в ядре, поэтому вероятность пропуска ключевых событий из-за перехвата исключена. Однако для ядер, отличных от ГКИ, может потребоваться ручная интеграция кода, и за рекомендациями можно обратиться к официальной документации.

Вредоносные модули

Если описанные выше способы не помогли спасти устройство, то высока вероятность того, что установленный модуль имеет вредоносные операции или повредил устройство иным способом. В этом случае есть только два варианта:

  1. Стереть данные и прошить официальную систему.
  2. Обратиться в сервисную службу.
',23),i=[l];function n(d,s,u,c,p,h){return e(),a("div",null,i)}const b=o(r,[["render",n]]);export{m as __pageData,b as default}; diff --git a/assets/ru_RU_guide_rescue-from-bootloop.md.d9d6936e.lean.js b/assets/ru_RU_guide_rescue-from-bootloop.md.c45eb81e.lean.js similarity index 73% rename from assets/ru_RU_guide_rescue-from-bootloop.md.d9d6936e.lean.js rename to assets/ru_RU_guide_rescue-from-bootloop.md.c45eb81e.lean.js index 7837a5df5f4e..392754e52261 100644 --- a/assets/ru_RU_guide_rescue-from-bootloop.md.d9d6936e.lean.js +++ b/assets/ru_RU_guide_rescue-from-bootloop.md.c45eb81e.lean.js @@ -1 +1 @@ -import{_ as o,o as e,c as a,O as t}from"./chunks/framework.43781440.js";const m=JSON.parse('{"title":"Выход из циклической загрузки","description":"","frontmatter":{},"headers":[],"relativePath":"ru_RU/guide/rescue-from-bootloop.md","filePath":"ru_RU/guide/rescue-from-bootloop.md"}'),r={name:"ru_RU/guide/rescue-from-bootloop.md"},l=t("",23),i=[l];function n(d,s,u,c,p,h){return e(),a("div",null,i)}const b=o(r,[["render",n]]);export{m as __pageData,b as default}; +import{_ as o,o as e,c as a,Q as t}from"./chunks/framework.ec8f7e8e.js";const m=JSON.parse('{"title":"Выход из циклической загрузки","description":"","frontmatter":{},"headers":[],"relativePath":"ru_RU/guide/rescue-from-bootloop.md","filePath":"ru_RU/guide/rescue-from-bootloop.md"}'),r={name:"ru_RU/guide/rescue-from-bootloop.md"},l=t("",23),i=[l];function n(d,s,u,c,p,h){return e(),a("div",null,i)}const b=o(r,[["render",n]]);export{m as __pageData,b as default}; diff --git a/assets/ru_RU_guide_unofficially-support-devices.md.fdcfea2d.js b/assets/ru_RU_guide_unofficially-support-devices.md.903ca1e7.js similarity index 94% rename from assets/ru_RU_guide_unofficially-support-devices.md.fdcfea2d.js rename to assets/ru_RU_guide_unofficially-support-devices.md.903ca1e7.js index 742e2ef4f0d4..3f40b72a7505 100644 --- a/assets/ru_RU_guide_unofficially-support-devices.md.fdcfea2d.js +++ b/assets/ru_RU_guide_unofficially-support-devices.md.903ca1e7.js @@ -1 +1 @@ -import{d as n}from"./chunks/repos.9a031e32.js";import{o as a,c as l,z as e,F as s,L as i,b as o,O as c,t as r}from"./chunks/framework.43781440.js";const _=c('

Неофициально поддерживаемые устройства

WARNING

На этой странице представлены ядра для устройств, не поддерживающих ГКИ и поддерживающих KernelSU, которые поддерживаются другими разработчиками.

WARNING

Эта страница предназначена только для поиска исходного кода, соответствующего вашему устройству, и НЕ означает, что исходный код был проверен разработчиками KernelSU. Вы должны использовать его на свой страх и риск.

',3),u=e("thead",null,[e("tr",null,[e("th",null,"Сопровождающий"),e("th",null,"Репозиторий"),e("th",null,"Поддерживаемое устройство")])],-1),d=["href"],m=["href"],v=JSON.parse('{"title":"Неофициально поддерживаемые устройства","description":"","frontmatter":{},"headers":[],"relativePath":"ru_RU/guide/unofficially-support-devices.md","filePath":"ru_RU/guide/unofficially-support-devices.md"}'),f={name:"ru_RU/guide/unofficially-support-devices.md"},N=Object.assign(f,{setup(h){return(p,k)=>(a(),l("div",null,[_,e("table",null,[u,e("tbody",null,[(a(!0),l(s,null,i(o(n),t=>(a(),l("tr",{key:t.devices},[e("td",null,[e("a",{href:t.maintainer_link,target:"_blank",rel:"noreferrer"},r(t.maintainer),9,d)]),e("td",null,[e("a",{href:t.kernel_link,target:"_blank",rel:"noreferrer"},r(t.kernel_name),9,m)]),e("td",null,r(t.devices),1)]))),128))])])]))}});export{v as __pageData,N as default}; +import{d as n}from"./chunks/repos.9a031e32.js";import{o as a,c as l,k as e,F as s,D as i,l as o,Q as c,t as r}from"./chunks/framework.ec8f7e8e.js";const _=c('

Неофициально поддерживаемые устройства

WARNING

На этой странице представлены ядра для устройств, не поддерживающих ГКИ и поддерживающих KernelSU, которые поддерживаются другими разработчиками.

WARNING

Эта страница предназначена только для поиска исходного кода, соответствующего вашему устройству, и НЕ означает, что исходный код был проверен разработчиками KernelSU. Вы должны использовать его на свой страх и риск.

',3),u=e("thead",null,[e("tr",null,[e("th",null,"Сопровождающий"),e("th",null,"Репозиторий"),e("th",null,"Поддерживаемое устройство")])],-1),d=["href"],m=["href"],v=JSON.parse('{"title":"Неофициально поддерживаемые устройства","description":"","frontmatter":{},"headers":[],"relativePath":"ru_RU/guide/unofficially-support-devices.md","filePath":"ru_RU/guide/unofficially-support-devices.md"}'),f={name:"ru_RU/guide/unofficially-support-devices.md"},N=Object.assign(f,{setup(h){return(p,k)=>(a(),l("div",null,[_,e("table",null,[u,e("tbody",null,[(a(!0),l(s,null,i(o(n),t=>(a(),l("tr",{key:t.devices},[e("td",null,[e("a",{href:t.maintainer_link,target:"_blank",rel:"noreferrer"},r(t.maintainer),9,d)]),e("td",null,[e("a",{href:t.kernel_link,target:"_blank",rel:"noreferrer"},r(t.kernel_name),9,m)]),e("td",null,r(t.devices),1)]))),128))])])]))}});export{v as __pageData,N as default}; diff --git a/assets/ru_RU_guide_unofficially-support-devices.md.fdcfea2d.lean.js b/assets/ru_RU_guide_unofficially-support-devices.md.903ca1e7.lean.js similarity index 88% rename from assets/ru_RU_guide_unofficially-support-devices.md.fdcfea2d.lean.js rename to assets/ru_RU_guide_unofficially-support-devices.md.903ca1e7.lean.js index 9677ada7ee5f..51cd648aadbe 100644 --- a/assets/ru_RU_guide_unofficially-support-devices.md.fdcfea2d.lean.js +++ b/assets/ru_RU_guide_unofficially-support-devices.md.903ca1e7.lean.js @@ -1 +1 @@ -import{d as n}from"./chunks/repos.9a031e32.js";import{o as a,c as l,z as e,F as s,L as i,b as o,O as c,t as r}from"./chunks/framework.43781440.js";const _=c("",3),u=e("thead",null,[e("tr",null,[e("th",null,"Сопровождающий"),e("th",null,"Репозиторий"),e("th",null,"Поддерживаемое устройство")])],-1),d=["href"],m=["href"],v=JSON.parse('{"title":"Неофициально поддерживаемые устройства","description":"","frontmatter":{},"headers":[],"relativePath":"ru_RU/guide/unofficially-support-devices.md","filePath":"ru_RU/guide/unofficially-support-devices.md"}'),f={name:"ru_RU/guide/unofficially-support-devices.md"},N=Object.assign(f,{setup(h){return(p,k)=>(a(),l("div",null,[_,e("table",null,[u,e("tbody",null,[(a(!0),l(s,null,i(o(n),t=>(a(),l("tr",{key:t.devices},[e("td",null,[e("a",{href:t.maintainer_link,target:"_blank",rel:"noreferrer"},r(t.maintainer),9,d)]),e("td",null,[e("a",{href:t.kernel_link,target:"_blank",rel:"noreferrer"},r(t.kernel_name),9,m)]),e("td",null,r(t.devices),1)]))),128))])])]))}});export{v as __pageData,N as default}; +import{d as n}from"./chunks/repos.9a031e32.js";import{o as a,c as l,k as e,F as s,D as i,l as o,Q as c,t as r}from"./chunks/framework.ec8f7e8e.js";const _=c("",3),u=e("thead",null,[e("tr",null,[e("th",null,"Сопровождающий"),e("th",null,"Репозиторий"),e("th",null,"Поддерживаемое устройство")])],-1),d=["href"],m=["href"],v=JSON.parse('{"title":"Неофициально поддерживаемые устройства","description":"","frontmatter":{},"headers":[],"relativePath":"ru_RU/guide/unofficially-support-devices.md","filePath":"ru_RU/guide/unofficially-support-devices.md"}'),f={name:"ru_RU/guide/unofficially-support-devices.md"},N=Object.assign(f,{setup(h){return(p,k)=>(a(),l("div",null,[_,e("table",null,[u,e("tbody",null,[(a(!0),l(s,null,i(o(n),t=>(a(),l("tr",{key:t.devices},[e("td",null,[e("a",{href:t.maintainer_link,target:"_blank",rel:"noreferrer"},r(t.maintainer),9,d)]),e("td",null,[e("a",{href:t.kernel_link,target:"_blank",rel:"noreferrer"},r(t.kernel_name),9,m)]),e("td",null,r(t.devices),1)]))),128))])])]))}});export{v as __pageData,N as default}; diff --git a/assets/ru_RU_guide_what-is-kernelsu.md.b17b08a8.js b/assets/ru_RU_guide_what-is-kernelsu.md.11586959.js similarity index 95% rename from assets/ru_RU_guide_what-is-kernelsu.md.b17b08a8.js rename to assets/ru_RU_guide_what-is-kernelsu.md.11586959.js index 0a65676dccfa..5f76fb6c5f4e 100644 --- a/assets/ru_RU_guide_what-is-kernelsu.md.b17b08a8.js +++ b/assets/ru_RU_guide_what-is-kernelsu.md.11586959.js @@ -1 +1 @@ -import{_ as e,o as a,c as t,O as r}from"./chunks/framework.43781440.js";const f=JSON.parse('{"title":"Что такое KernelSU?","description":"","frontmatter":{},"headers":[],"relativePath":"ru_RU/guide/what-is-kernelsu.md","filePath":"ru_RU/guide/what-is-kernelsu.md"}'),o={name:"ru_RU/guide/what-is-kernelsu.md"},s=r('

Что такое KernelSU?

KernelSU - это root-решение для устройств Android GKI, работающее в режиме ядра и предоставляющее root-права пользовательским приложениям непосредственно в пространстве ядра.

Особенности

Основной особенностью KernelSU является то, что он основан на ядре. KernelSU работает в режиме ядра, поэтому он может предоставить интерфейс ядра, которого раньше не было. Например, мы можем добавить аппаратную точку останова любому процессу в режиме ядра; мы можем получить доступ к физической памяти любого процесса без чьего-либо ведома; мы можем перехватить любой syscall в пространстве ядра; и т.д.

Кроме того, KernelSU предоставляет систему модулей через overlayfs, что позволяет загружать в систему пользовательские плагины. Также предусмотрен механизм модификации файлов в разделе /system.

Как использовать

Пожалуйста, обратитесь к: Установка

Как собрать

Как собрать

Обсуждение

',11),n=[s];function i(l,h,d,u,c,_){return a(),t("div",null,n)}const m=e(o,[["render",i]]);export{f as __pageData,m as default}; +import{_ as e,o as a,c as t,Q as r}from"./chunks/framework.ec8f7e8e.js";const f=JSON.parse('{"title":"Что такое KernelSU?","description":"","frontmatter":{},"headers":[],"relativePath":"ru_RU/guide/what-is-kernelsu.md","filePath":"ru_RU/guide/what-is-kernelsu.md"}'),o={name:"ru_RU/guide/what-is-kernelsu.md"},s=r('

Что такое KernelSU?

KernelSU - это root-решение для устройств Android GKI, работающее в режиме ядра и предоставляющее root-права пользовательским приложениям непосредственно в пространстве ядра.

Особенности

Основной особенностью KernelSU является то, что он основан на ядре. KernelSU работает в режиме ядра, поэтому он может предоставить интерфейс ядра, которого раньше не было. Например, мы можем добавить аппаратную точку останова любому процессу в режиме ядра; мы можем получить доступ к физической памяти любого процесса без чьего-либо ведома; мы можем перехватить любой syscall в пространстве ядра; и т.д.

Кроме того, KernelSU предоставляет систему модулей через overlayfs, что позволяет загружать в систему пользовательские плагины. Также предусмотрен механизм модификации файлов в разделе /system.

Как использовать

Пожалуйста, обратитесь к: Установка

Как собрать

Как собрать

Обсуждение

',11),n=[s];function i(l,h,d,u,c,_){return a(),t("div",null,n)}const m=e(o,[["render",i]]);export{f as __pageData,m as default}; diff --git a/assets/ru_RU_guide_what-is-kernelsu.md.b17b08a8.lean.js b/assets/ru_RU_guide_what-is-kernelsu.md.11586959.lean.js similarity index 71% rename from assets/ru_RU_guide_what-is-kernelsu.md.b17b08a8.lean.js rename to assets/ru_RU_guide_what-is-kernelsu.md.11586959.lean.js index b750912d338e..af0091e32315 100644 --- a/assets/ru_RU_guide_what-is-kernelsu.md.b17b08a8.lean.js +++ b/assets/ru_RU_guide_what-is-kernelsu.md.11586959.lean.js @@ -1 +1 @@ -import{_ as e,o as a,c as t,O as r}from"./chunks/framework.43781440.js";const f=JSON.parse('{"title":"Что такое KernelSU?","description":"","frontmatter":{},"headers":[],"relativePath":"ru_RU/guide/what-is-kernelsu.md","filePath":"ru_RU/guide/what-is-kernelsu.md"}'),o={name:"ru_RU/guide/what-is-kernelsu.md"},s=r("",11),n=[s];function i(l,h,d,u,c,_){return a(),t("div",null,n)}const m=e(o,[["render",i]]);export{f as __pageData,m as default}; +import{_ as e,o as a,c as t,Q as r}from"./chunks/framework.ec8f7e8e.js";const f=JSON.parse('{"title":"Что такое KernelSU?","description":"","frontmatter":{},"headers":[],"relativePath":"ru_RU/guide/what-is-kernelsu.md","filePath":"ru_RU/guide/what-is-kernelsu.md"}'),o={name:"ru_RU/guide/what-is-kernelsu.md"},s=r("",11),n=[s];function i(l,h,d,u,c,_){return a(),t("div",null,n)}const m=e(o,[["render",i]]);export{f as __pageData,m as default}; diff --git a/assets/ru_RU_index.md.4f8b1975.js b/assets/ru_RU_index.md.4a8cc387.js similarity index 96% rename from assets/ru_RU_index.md.4f8b1975.js rename to assets/ru_RU_index.md.4a8cc387.js index 352a093db370..34a7e12521e2 100644 --- a/assets/ru_RU_index.md.4f8b1975.js +++ b/assets/ru_RU_index.md.4a8cc387.js @@ -1 +1 @@ -import{_ as e,o as t,c as r}from"./chunks/framework.43781440.js";const m=JSON.parse('{"title":"Основанное на ядре root-решение для Android","description":"","frontmatter":{"layout":"home","title":"Основанное на ядре root-решение для Android","hero":{"name":"KernelSU","text":"Основанное на ядре root-решение для Android","tagline":"","image":{"src":"/logo.png","alt":"KernelSU"},"actions":[{"theme":"brand","text":"Начало работы","link":"/ru_RU/guide/what-is-kernelsu"},{"theme":"alt","text":"Посмотр на GitHub","link":"https://github.com/tiann/KernelSU"}]},"features":[{"title":"Основанный на ядре","details":"KernelSU работает в режиме ядра Linux, он имеет больше контроля над пользовательскими приложениями."},{"title":"Контроль доступа по белому списку","details":"Только приложение, которому предоставлено разрешение root, может получить доступ к `su`, другие приложения не могут воспринимать su."},{"title":"Ограниченные root-права","details":"KernelSU позволяет вам настраивать uid, gid, группы, возможности и правила SELinux для su. Заприте root-власть в клетке."},{"title":"Модульность и открытый исходный код","details":"KernelSU поддерживает модификацию /system бессистемно с помощью overlayfs и имеет открытый исходный код под лицензией GPL-3."}]},"headers":[],"relativePath":"ru_RU/index.md","filePath":"ru_RU/index.md"}'),i={name:"ru_RU/index.md"};function n(o,a,s,l,d,u){return t(),r("div")}const _=e(i,[["render",n]]);export{m as __pageData,_ as default}; +import{_ as e,o as t,c as r}from"./chunks/framework.ec8f7e8e.js";const m=JSON.parse('{"title":"Основанное на ядре root-решение для Android","description":"","frontmatter":{"layout":"home","title":"Основанное на ядре root-решение для Android","hero":{"name":"KernelSU","text":"Основанное на ядре root-решение для Android","tagline":"","image":{"src":"/logo.png","alt":"KernelSU"},"actions":[{"theme":"brand","text":"Начало работы","link":"/ru_RU/guide/what-is-kernelsu"},{"theme":"alt","text":"Посмотр на GitHub","link":"https://github.com/tiann/KernelSU"}]},"features":[{"title":"Основанный на ядре","details":"KernelSU работает в режиме ядра Linux, он имеет больше контроля над пользовательскими приложениями."},{"title":"Контроль доступа по белому списку","details":"Только приложение, которому предоставлено разрешение root, может получить доступ к `su`, другие приложения не могут воспринимать su."},{"title":"Ограниченные root-права","details":"KernelSU позволяет вам настраивать uid, gid, группы, возможности и правила SELinux для su. Заприте root-власть в клетке."},{"title":"Модульность и открытый исходный код","details":"KernelSU поддерживает модификацию /system бессистемно с помощью overlayfs и имеет открытый исходный код под лицензией GPL-3."}]},"headers":[],"relativePath":"ru_RU/index.md","filePath":"ru_RU/index.md"}'),i={name:"ru_RU/index.md"};function n(o,a,s,l,d,u){return t(),r("div")}const _=e(i,[["render",n]]);export{m as __pageData,_ as default}; diff --git a/assets/ru_RU_index.md.4f8b1975.lean.js b/assets/ru_RU_index.md.4a8cc387.lean.js similarity index 96% rename from assets/ru_RU_index.md.4f8b1975.lean.js rename to assets/ru_RU_index.md.4a8cc387.lean.js index 352a093db370..34a7e12521e2 100644 --- a/assets/ru_RU_index.md.4f8b1975.lean.js +++ b/assets/ru_RU_index.md.4a8cc387.lean.js @@ -1 +1 @@ -import{_ as e,o as t,c as r}from"./chunks/framework.43781440.js";const m=JSON.parse('{"title":"Основанное на ядре root-решение для Android","description":"","frontmatter":{"layout":"home","title":"Основанное на ядре root-решение для Android","hero":{"name":"KernelSU","text":"Основанное на ядре root-решение для Android","tagline":"","image":{"src":"/logo.png","alt":"KernelSU"},"actions":[{"theme":"brand","text":"Начало работы","link":"/ru_RU/guide/what-is-kernelsu"},{"theme":"alt","text":"Посмотр на GitHub","link":"https://github.com/tiann/KernelSU"}]},"features":[{"title":"Основанный на ядре","details":"KernelSU работает в режиме ядра Linux, он имеет больше контроля над пользовательскими приложениями."},{"title":"Контроль доступа по белому списку","details":"Только приложение, которому предоставлено разрешение root, может получить доступ к `su`, другие приложения не могут воспринимать su."},{"title":"Ограниченные root-права","details":"KernelSU позволяет вам настраивать uid, gid, группы, возможности и правила SELinux для su. Заприте root-власть в клетке."},{"title":"Модульность и открытый исходный код","details":"KernelSU поддерживает модификацию /system бессистемно с помощью overlayfs и имеет открытый исходный код под лицензией GPL-3."}]},"headers":[],"relativePath":"ru_RU/index.md","filePath":"ru_RU/index.md"}'),i={name:"ru_RU/index.md"};function n(o,a,s,l,d,u){return t(),r("div")}const _=e(i,[["render",n]]);export{m as __pageData,_ as default}; +import{_ as e,o as t,c as r}from"./chunks/framework.ec8f7e8e.js";const m=JSON.parse('{"title":"Основанное на ядре root-решение для Android","description":"","frontmatter":{"layout":"home","title":"Основанное на ядре root-решение для Android","hero":{"name":"KernelSU","text":"Основанное на ядре root-решение для Android","tagline":"","image":{"src":"/logo.png","alt":"KernelSU"},"actions":[{"theme":"brand","text":"Начало работы","link":"/ru_RU/guide/what-is-kernelsu"},{"theme":"alt","text":"Посмотр на GitHub","link":"https://github.com/tiann/KernelSU"}]},"features":[{"title":"Основанный на ядре","details":"KernelSU работает в режиме ядра Linux, он имеет больше контроля над пользовательскими приложениями."},{"title":"Контроль доступа по белому списку","details":"Только приложение, которому предоставлено разрешение root, может получить доступ к `su`, другие приложения не могут воспринимать su."},{"title":"Ограниченные root-права","details":"KernelSU позволяет вам настраивать uid, gid, группы, возможности и правила SELinux для su. Заприте root-власть в клетке."},{"title":"Модульность и открытый исходный код","details":"KernelSU поддерживает модификацию /system бессистемно с помощью overlayfs и имеет открытый исходный код под лицензией GPL-3."}]},"headers":[],"relativePath":"ru_RU/index.md","filePath":"ru_RU/index.md"}'),i={name:"ru_RU/index.md"};function n(o,a,s,l,d,u){return t(),r("div")}const _=e(i,[["render",n]]);export{m as __pageData,_ as default}; diff --git a/assets/style.7b8b1841.css b/assets/style.7b8b1841.css deleted file mode 100644 index b5ee2f2d2346..000000000000 --- a/assets/style.7b8b1841.css +++ /dev/null @@ -1 +0,0 @@ -@font-face{font-family:Inter var;font-weight:100 900;font-display:swap;font-style:normal;font-named-instance:"Regular";src:url(/assets/inter-roman-cyrillic.5f2c6c8c.woff2) format("woff2");unicode-range:U+0301,U+0400-045F,U+0490-0491,U+04B0-04B1,U+2116}@font-face{font-family:Inter var;font-weight:100 900;font-display:swap;font-style:normal;font-named-instance:"Regular";src:url(/assets/inter-roman-cyrillic-ext.e75737ce.woff2) format("woff2");unicode-range:U+0460-052F,U+1C80-1C88,U+20B4,U+2DE0-2DFF,U+A640-A69F,U+FE2E-FE2F}@font-face{font-family:Inter var;font-weight:100 900;font-display:swap;font-style:normal;font-named-instance:"Regular";src:url(/assets/inter-roman-greek.d5a6d92a.woff2) format("woff2");unicode-range:U+0370-03FF}@font-face{font-family:Inter var;font-weight:100 900;font-display:swap;font-style:normal;font-named-instance:"Regular";src:url(/assets/inter-roman-greek-ext.ab0619bc.woff2) format("woff2");unicode-range:U+1F00-1FFF}@font-face{font-family:Inter var;font-weight:100 900;font-display:swap;font-style:normal;font-named-instance:"Regular";src:url(/assets/inter-roman-latin.2ed14f66.woff2) format("woff2");unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+2000-206F,U+2074,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}@font-face{font-family:Inter var;font-weight:100 900;font-display:swap;font-style:normal;font-named-instance:"Regular";src:url(/assets/inter-roman-latin-ext.0030eebd.woff2) format("woff2");unicode-range:U+0100-024F,U+0259,U+1E00-1EFF,U+2020,U+20A0-20AB,U+20AD-20CF,U+2113,U+2C60-2C7F,U+A720-A7FF}@font-face{font-family:Inter var;font-weight:100 900;font-display:swap;font-style:normal;font-named-instance:"Regular";src:url(/assets/inter-roman-vietnamese.14ce25a6.woff2) format("woff2");unicode-range:U+0102-0103,U+0110-0111,U+0128-0129,U+0168-0169,U+01A0-01A1,U+01AF-01B0,U+1EA0-1EF9,U+20AB}@font-face{font-family:Inter var;font-weight:100 900;font-display:swap;font-style:italic;font-named-instance:"Italic";src:url(/assets/inter-italic-cyrillic.ea42a392.woff2) format("woff2");unicode-range:U+0301,U+0400-045F,U+0490-0491,U+04B0-04B1,U+2116}@font-face{font-family:Inter var;font-weight:100 900;font-display:swap;font-style:italic;font-named-instance:"Italic";src:url(/assets/inter-italic-cyrillic-ext.33bd5a8e.woff2) format("woff2");unicode-range:U+0460-052F,U+1C80-1C88,U+20B4,U+2DE0-2DFF,U+A640-A69F,U+FE2E-FE2F}@font-face{font-family:Inter var;font-weight:100 900;font-display:swap;font-style:italic;font-named-instance:"Italic";src:url(/assets/inter-italic-greek.8f4463c4.woff2) format("woff2");unicode-range:U+0370-03FF}@font-face{font-family:Inter var;font-weight:100 900;font-display:swap;font-style:italic;font-named-instance:"Italic";src:url(/assets/inter-italic-greek-ext.4fbe9427.woff2) format("woff2");unicode-range:U+1F00-1FFF}@font-face{font-family:Inter var;font-weight:100 900;font-display:swap;font-style:italic;font-named-instance:"Italic";src:url(/assets/inter-italic-latin.bd3b6f56.woff2) format("woff2");unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+2000-206F,U+2074,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}@font-face{font-family:Inter var;font-weight:100 900;font-display:swap;font-style:italic;font-named-instance:"Italic";src:url(/assets/inter-italic-latin-ext.bd8920cc.woff2) format("woff2");unicode-range:U+0100-024F,U+0259,U+1E00-1EFF,U+2020,U+20A0-20AB,U+20AD-20CF,U+2113,U+2C60-2C7F,U+A720-A7FF}@font-face{font-family:Inter var;font-weight:100 900;font-display:swap;font-style:italic;font-named-instance:"Italic";src:url(/assets/inter-italic-vietnamese.6ce511fb.woff2) format("woff2");unicode-range:U+0102-0103,U+0110-0111,U+0128-0129,U+0168-0169,U+01A0-01A1,U+01AF-01B0,U+1EA0-1EF9,U+20AB}@font-face{font-family:Chinese Quotes;src:local("PingFang SC Regular"),local("PingFang SC"),local("SimHei"),local("Source Han Sans SC");unicode-range:U+2018,U+2019,U+201C,U+201D}:root{--vp-c-white: #ffffff;--vp-c-black: #000000;--vp-c-gray: #8e8e93;--vp-c-text-light-1: rgba(60, 60, 67);--vp-c-text-light-2: rgba(60, 60, 67, .75);--vp-c-text-light-3: rgba(60, 60, 67, .33);--vp-c-text-dark-1: rgba(255, 255, 245, .86);--vp-c-text-dark-2: rgba(235, 235, 245, .6);--vp-c-text-dark-3: rgba(235, 235, 245, .38);--vp-c-green: #10b981;--vp-c-green-light: #34d399;--vp-c-green-lighter: #6ee7b7;--vp-c-green-dark: #059669;--vp-c-green-darker: #047857;--vp-c-green-dimm-1: rgba(16, 185, 129, .05);--vp-c-green-dimm-2: rgba(16, 185, 129, .2);--vp-c-green-dimm-3: rgba(16, 185, 129, .5);--vp-c-yellow: #d97706;--vp-c-yellow-light: #f59e0b;--vp-c-yellow-lighter: #fbbf24;--vp-c-yellow-dark: #b45309;--vp-c-yellow-darker: #92400e;--vp-c-yellow-dimm-1: rgba(234, 179, 8, .05);--vp-c-yellow-dimm-2: rgba(234, 179, 8, .2);--vp-c-yellow-dimm-3: rgba(234, 179, 8, .5);--vp-c-red: #f43f5e;--vp-c-red-light: #fb7185;--vp-c-red-lighter: #fda4af;--vp-c-red-dark: #e11d48;--vp-c-red-darker: #be123c;--vp-c-red-dimm-1: rgba(244, 63, 94, .05);--vp-c-red-dimm-2: rgba(244, 63, 94, .2);--vp-c-red-dimm-3: rgba(244, 63, 94, .5);--vp-c-sponsor: #db2777}:root{--vp-c-bg: #ffffff;--vp-c-bg-elv: #ffffff;--vp-c-bg-elv-up: #ffffff;--vp-c-bg-elv-down: #f6f6f7;--vp-c-bg-elv-mute: #f6f6f7;--vp-c-bg-soft: #f6f6f7;--vp-c-bg-soft-up: #f9f9fa;--vp-c-bg-soft-down: #e3e3e5;--vp-c-bg-soft-mute: #e3e3e5;--vp-c-bg-alt: #f6f6f7;--vp-c-border: rgba(60, 60, 67, .29);--vp-c-divider: rgba(60, 60, 67, .12);--vp-c-gutter: rgba(60, 60, 67, .12);--vp-c-neutral: var(--vp-c-black);--vp-c-neutral-inverse: var(--vp-c-white);--vp-c-text-1: var(--vp-c-text-light-1);--vp-c-text-2: var(--vp-c-text-light-2);--vp-c-text-3: var(--vp-c-text-light-3);--vp-c-text-inverse-1: var(--vp-c-text-dark-1);--vp-c-text-inverse-2: var(--vp-c-text-dark-2);--vp-c-text-inverse-3: var(--vp-c-text-dark-3);--vp-c-text-code: #476582;--vp-c-brand: var(--vp-c-green);--vp-c-brand-light: var(--vp-c-green-light);--vp-c-brand-lighter: var(--vp-c-green-lighter);--vp-c-brand-dark: var(--vp-c-green-dark);--vp-c-brand-darker: var(--vp-c-green-darker);--vp-c-mute: #f6f6f7;--vp-c-mute-light: #f9f9fc;--vp-c-mute-lighter: #ffffff;--vp-c-mute-dark: #e3e3e5;--vp-c-mute-darker: #d7d7d9}.dark{--vp-c-bg: #1e1e20;--vp-c-bg-elv: #252529;--vp-c-bg-elv-up: #313136;--vp-c-bg-elv-down: #1e1e20;--vp-c-bg-elv-mute: #313136;--vp-c-bg-soft: #252529;--vp-c-bg-soft-up: #313136;--vp-c-bg-soft-down: #1e1e20;--vp-c-bg-soft-mute: #313136;--vp-c-bg-alt: #161618;--vp-c-border: rgba(82, 82, 89, .68);--vp-c-divider: rgba(82, 82, 89, .32);--vp-c-gutter: #000000;--vp-c-neutral: var(--vp-c-white);--vp-c-neutral-inverse: var(--vp-c-black);--vp-c-text-1: var(--vp-c-text-dark-1);--vp-c-text-2: var(--vp-c-text-dark-2);--vp-c-text-3: var(--vp-c-text-dark-3);--vp-c-text-inverse-1: var(--vp-c-text-light-1);--vp-c-text-inverse-2: var(--vp-c-text-light-2);--vp-c-text-inverse-3: var(--vp-c-text-light-3);--vp-c-text-code: #c9def1;--vp-c-mute: #313136;--vp-c-mute-light: #3a3a3c;--vp-c-mute-lighter: #505053;--vp-c-mute-dark: #2c2c30;--vp-c-mute-darker: #252529}:root{--vp-font-family-base: "Chinese Quotes", "Inter var", "Inter", ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Helvetica, Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";--vp-font-family-mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace}:root{--vp-shadow-1: 0 1px 2px rgba(0, 0, 0, .04), 0 1px 2px rgba(0, 0, 0, .06);--vp-shadow-2: 0 3px 12px rgba(0, 0, 0, .07), 0 1px 4px rgba(0, 0, 0, .07);--vp-shadow-3: 0 12px 32px rgba(0, 0, 0, .1), 0 2px 6px rgba(0, 0, 0, .08);--vp-shadow-4: 0 14px 44px rgba(0, 0, 0, .12), 0 3px 9px rgba(0, 0, 0, .12);--vp-shadow-5: 0 18px 56px rgba(0, 0, 0, .16), 0 4px 12px rgba(0, 0, 0, .16)}:root{--vp-z-index-local-nav: 10;--vp-z-index-nav: 20;--vp-z-index-layout-top: 30;--vp-z-index-backdrop: 40;--vp-z-index-sidebar: 50;--vp-z-index-footer: 60}:root{--vp-icon-copy: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' height='20' width='20' stroke='rgba(128,128,128,1)' stroke-width='2' viewBox='0 0 24 24'%3E%3Cpath stroke-linecap='round' stroke-linejoin='round' d='M9 5H7a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V7a2 2 0 0 0-2-2h-2M9 5a2 2 0 0 0 2 2h2a2 2 0 0 0 2-2M9 5a2 2 0 0 1 2-2h2a2 2 0 0 1 2 2'/%3E%3C/svg%3E");--vp-icon-copied: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' height='20' width='20' stroke='rgba(128,128,128,1)' stroke-width='2' viewBox='0 0 24 24'%3E%3Cpath stroke-linecap='round' stroke-linejoin='round' d='M9 5H7a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V7a2 2 0 0 0-2-2h-2M9 5a2 2 0 0 0 2 2h2a2 2 0 0 0 2-2M9 5a2 2 0 0 1 2-2h2a2 2 0 0 1 2 2m-6 9 2 2 4-4'/%3E%3C/svg%3E")}:root{--vp-layout-max-width: 1440px}:root{--vp-header-anchor-symbol: "#"}:root{--vp-code-line-height: 1.7;--vp-code-font-size: .875em;--vp-c-code-dimm: var(--vp-c-text-dark-3);--vp-code-block-color: var(--vp-c-text-dark-1);--vp-code-block-bg: #292b30;--vp-code-block-bg-light: #1e1e20;--vp-code-block-divider-color: #000000;--vp-code-line-highlight-color: rgba(0, 0, 0, .5);--vp-code-line-number-color: var(--vp-c-code-dimm);--vp-code-line-diff-add-color: var(--vp-c-green-dimm-2);--vp-code-line-diff-add-symbol-color: var(--vp-c-green);--vp-code-line-diff-remove-color: var(--vp-c-red-dimm-2);--vp-code-line-diff-remove-symbol-color: var(--vp-c-red);--vp-code-line-warning-color: var(--vp-c-yellow-dimm-2);--vp-code-line-error-color: var(--vp-c-red-dimm-2);--vp-code-copy-code-border-color: transparent;--vp-code-copy-code-bg: var(--vp-code-block-bg-light);--vp-code-copy-code-hover-border-color: var(--vp-c-divider);--vp-code-copy-code-hover-bg: var(--vp-code-block-bg-light);--vp-code-copy-code-active-text: var(--vp-c-text-dark-2);--vp-code-tab-divider: var(--vp-code-block-divider-color);--vp-code-tab-text-color: var(--vp-c-text-dark-2);--vp-code-tab-bg: var(--vp-code-block-bg);--vp-code-tab-hover-text-color: var(--vp-c-text-dark-1);--vp-code-tab-active-text-color: var(--vp-c-text-dark-1);--vp-code-tab-active-bar-color: var(--vp-c-brand)}.dark{--vp-code-block-bg: #161618}:root:not(.dark) .vp-adaptive-theme{--vp-c-code-dimm: var(--vp-c-text-2);--vp-code-block-color: var(--vp-c-text-1);--vp-code-block-bg: #f8f8f8;--vp-code-block-divider-color: var(--vp-c-divider);--vp-code-line-highlight-color: #ececec;--vp-code-line-number-color: var(--vp-c-code-dimm);--vp-code-copy-code-bg: #e2e2e2;--vp-code-copy-code-hover-bg: #dcdcdc;--vp-code-copy-code-active-text: var(--vp-c-text-2);--vp-code-tab-divider: var(--vp-c-divider);--vp-code-tab-text-color: var(--vp-c-text-2);--vp-code-tab-bg: var(--vp-code-block-bg);--vp-code-tab-hover-text-color: var(--vp-c-text-1);--vp-code-tab-active-text-color: var(--vp-c-text-1)}:root{--vp-button-brand-border: var(--vp-c-brand-lighter);--vp-button-brand-text: var(--vp-c-white);--vp-button-brand-bg: var(--vp-c-brand);--vp-button-brand-hover-border: var(--vp-c-brand-lighter);--vp-button-brand-hover-text: var(--vp-c-white);--vp-button-brand-hover-bg: var(--vp-c-brand-dark);--vp-button-brand-active-border: var(--vp-c-brand-lighter);--vp-button-brand-active-text: var(--vp-c-white);--vp-button-brand-active-bg: var(--vp-c-brand-darker);--vp-button-alt-border: var(--vp-c-border);--vp-button-alt-text: var(--vp-c-neutral);--vp-button-alt-bg: var(--vp-c-mute);--vp-button-alt-hover-border: var(--vp-c-border);--vp-button-alt-hover-text: var(--vp-c-neutral);--vp-button-alt-hover-bg: var(--vp-c-mute-dark);--vp-button-alt-active-border: var(--vp-c-border);--vp-button-alt-active-text: var(--vp-c-neutral);--vp-button-alt-active-bg: var(--vp-c-mute-darker);--vp-button-sponsor-border: var(--vp-c-gray-light-3);--vp-button-sponsor-text: var(--vp-c-text-light-2);--vp-button-sponsor-bg: transparent;--vp-button-sponsor-hover-border: var(--vp-c-sponsor);--vp-button-sponsor-hover-text: var(--vp-c-sponsor);--vp-button-sponsor-hover-bg: transparent;--vp-button-sponsor-active-border: var(--vp-c-sponsor);--vp-button-sponsor-active-text: var(--vp-c-sponsor);--vp-button-sponsor-active-bg: transparent}.dark{--vp-button-sponsor-border: var(--vp-c-gray-dark-1);--vp-button-sponsor-text: var(--vp-c-text-dark-2)}:root{--vp-custom-block-font-size: 14px;--vp-custom-block-code-font-size: 13px;--vp-custom-block-info-border: var(--vp-c-border);--vp-custom-block-info-text: var(--vp-c-text-2);--vp-custom-block-info-bg: var(--vp-c-bg-soft-up);--vp-custom-block-info-code-bg: var(--vp-c-bg-soft);--vp-custom-block-tip-border: var(--vp-c-green);--vp-custom-block-tip-text: var(--vp-c-green-dark);--vp-custom-block-tip-bg: var(--vp-c-bg-soft-up);--vp-custom-block-tip-code-bg: var(--vp-c-bg-soft);--vp-custom-block-warning-border: var(--vp-c-yellow);--vp-custom-block-warning-text: var(--vp-c-yellow);--vp-custom-block-warning-bg: var(--vp-c-bg-soft-up);--vp-custom-block-warning-code-bg: var(--vp-c-bg-soft);--vp-custom-block-danger-border: var(--vp-c-red);--vp-custom-block-danger-text: var(--vp-c-red);--vp-custom-block-danger-bg: var(--vp-c-bg-soft-up);--vp-custom-block-danger-code-bg: var(--vp-c-bg-soft);--vp-custom-block-details-border: var(--vp-custom-block-info-border);--vp-custom-block-details-text: var(--vp-custom-block-info-text);--vp-custom-block-details-bg: var(--vp-custom-block-info-bg);--vp-custom-block-details-code-bg: var(--vp-custom-block-details-bg)}:root{--vp-input-border-color: var(--vp-c-border);--vp-input-bg-color: var(--vp-c-bg-alt);--vp-input-hover-border-color: var(--vp-c-gray);--vp-input-switch-bg-color: var(--vp-c-mute)}:root{--vp-nav-height: 64px;--vp-nav-bg-color: var(--vp-c-bg);--vp-nav-screen-bg-color: var(--vp-c-bg)}:root{--vp-local-nav-bg-color: var(--vp-c-bg)}:root{--vp-sidebar-width: 272px;--vp-sidebar-bg-color: var(--vp-c-bg-alt)}:root{--vp-backdrop-bg-color: rgba(0, 0, 0, .6)}:root{--vp-home-hero-name-color: var(--vp-c-brand);--vp-home-hero-name-background: transparent;--vp-home-hero-image-background-image: none;--vp-home-hero-image-filter: none}:root{--vp-badge-info-border: var(--vp-c-border);--vp-badge-info-text: var(--vp-c-text-2);--vp-badge-info-bg: var(--vp-c-bg-soft-up);--vp-badge-tip-border: var(--vp-c-green-dark);--vp-badge-tip-text: var(--vp-c-green);--vp-badge-tip-bg: var(--vp-c-green-dimm-1);--vp-badge-warning-border: var(--vp-c-yellow-dark);--vp-badge-warning-text: var(--vp-c-yellow);--vp-badge-warning-bg: var(--vp-c-yellow-dimm-1);--vp-badge-danger-border: var(--vp-c-red-dark);--vp-badge-danger-text: var(--vp-c-red);--vp-badge-danger-bg: var(--vp-c-red-dimm-1)}:root{--vp-carbon-ads-text-color: var(--vp-c-text-1);--vp-carbon-ads-poweredby-color: var(--vp-c-text-2);--vp-carbon-ads-bg-color: var(--vp-c-bg-soft);--vp-carbon-ads-hover-text-color: var(--vp-c-brand);--vp-carbon-ads-hover-poweredby-color: var(--vp-c-text-1)}:root{--vp-local-search-bg: var(--vp-c-bg);--vp-local-search-result-bg: var(--vp-c-bg);--vp-local-search-result-border: var(--vp-c-divider);--vp-local-search-result-selected-bg: var(--vp-c-bg);--vp-local-search-result-selected-border: var(--vp-c-brand);--vp-local-search-highlight-bg: var(--vp-c-green-lighter);--vp-local-search-highlight-text: var(--vp-c-black)}*,:before,:after{box-sizing:border-box}html{line-height:1.4;font-size:16px;-webkit-text-size-adjust:100%}html.dark{color-scheme:dark}body{margin:0;width:100%;min-width:320px;min-height:100vh;line-height:24px;font-family:var(--vp-font-family-base);font-size:16px;font-weight:400;color:var(--vp-c-text-1);background-color:var(--vp-c-bg);direction:ltr;font-synthesis:style;text-rendering:optimizeLegibility;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}main{display:block}h1,h2,h3,h4,h5,h6{margin:0;line-height:24px;font-size:16px;font-weight:400}p{margin:0}strong,b{font-weight:600}a,area,button,[role=button],input,label,select,summary,textarea{touch-action:manipulation}a{color:inherit;text-decoration:inherit}ol,ul{list-style:none;margin:0;padding:0}blockquote{margin:0}pre,code,kbd,samp{font-family:var(--vp-font-family-mono)}img,svg,video,canvas,audio,iframe,embed,object{display:block}figure{margin:0}img,video{max-width:100%;height:auto}button,input,optgroup,select,textarea{border:0;padding:0;line-height:inherit;color:inherit}button{padding:0;font-family:inherit;background-color:transparent;background-image:none}button:enabled,[role=button]:enabled{cursor:pointer}button:focus,button:focus-visible{outline:1px dotted;outline:4px auto -webkit-focus-ring-color}button:focus:not(:focus-visible){outline:none!important}input:focus,textarea:focus,select:focus{outline:none}table{border-collapse:collapse}input{background-color:transparent}input:-ms-input-placeholder,textarea:-ms-input-placeholder{color:var(--vp-c-text-3)}input::-ms-input-placeholder,textarea::-ms-input-placeholder{color:var(--vp-c-text-3)}input::placeholder,textarea::placeholder{color:var(--vp-c-text-3)}input::-webkit-outer-spin-button,input::-webkit-inner-spin-button{-webkit-appearance:none;margin:0}input[type=number]{-moz-appearance:textfield}textarea{resize:vertical}select{-webkit-appearance:none}fieldset{margin:0;padding:0}h1,h2,h3,h4,h5,h6,li,p{overflow-wrap:break-word}vite-error-overlay{z-index:9999}.visually-hidden{position:absolute;width:1px;height:1px;white-space:nowrap;clip:rect(0 0 0 0);clip-path:inset(50%);overflow:hidden}.custom-block{border:1px solid transparent;border-radius:8px;padding:16px 16px 8px;line-height:24px;font-size:var(--vp-custom-block-font-size);color:var(--vp-c-text-2)}.custom-block.info{border-color:var(--vp-custom-block-info-border);color:var(--vp-custom-block-info-text);background-color:var(--vp-custom-block-info-bg)}.custom-block.custom-block th,.custom-block.custom-block blockquote>p{font-size:var(--vp-custom-block-font-size);color:inherit}.custom-block.info code{background-color:var(--vp-custom-block-info-code-bg)}.custom-block.tip{border-color:var(--vp-custom-block-tip-border);color:var(--vp-custom-block-tip-text);background-color:var(--vp-custom-block-tip-bg)}.custom-block.tip code{background-color:var(--vp-custom-block-tip-code-bg)}.custom-block.warning{border-color:var(--vp-custom-block-warning-border);color:var(--vp-custom-block-warning-text);background-color:var(--vp-custom-block-warning-bg)}.custom-block.warning code{background-color:var(--vp-custom-block-warning-code-bg)}.custom-block.danger{border-color:var(--vp-custom-block-danger-border);color:var(--vp-custom-block-danger-text);background-color:var(--vp-custom-block-danger-bg)}.custom-block.danger code{background-color:var(--vp-custom-block-danger-code-bg)}.custom-block.details{border-color:var(--vp-custom-block-details-border);color:var(--vp-custom-block-details-text);background-color:var(--vp-custom-block-details-bg)}.custom-block.details code{background-color:var(--vp-custom-block-details-code-bg)}.custom-block-title{font-weight:600}.custom-block p+p{margin:8px 0}.custom-block.details summary{margin:0 0 8px;font-weight:700;cursor:pointer}.custom-block.details summary+p{margin:8px 0}.custom-block a{color:inherit;font-weight:600}.custom-block a:hover{text-decoration:underline}.custom-block code{font-size:var(--vp-custom-block-code-font-size)}.dark .vp-code-light{display:none}html:not(.dark) .vp-code-dark{display:none}.vp-code-group{margin-top:16px}.vp-code-group .tabs{position:relative;display:flex;margin-right:-24px;margin-left:-24px;padding:0 12px;background-color:var(--vp-code-tab-bg);overflow-x:auto;overflow-y:hidden}.vp-code-group .tabs:after{position:absolute;right:0;bottom:0;left:0;height:1px;background-color:var(--vp-code-tab-divider);content:""}@media (min-width: 640px){.vp-code-group .tabs{margin-right:0;margin-left:0;border-radius:8px 8px 0 0}}.vp-code-group .tabs input{position:absolute;opacity:0;pointer-events:none}.vp-code-group .tabs label{position:relative;display:inline-block;border-bottom:1px solid transparent;padding:0 12px;line-height:48px;font-size:14px;font-weight:500;color:var(--vp-code-tab-text-color);white-space:nowrap;cursor:pointer;transition:color .25s}.vp-code-group .tabs label:after{position:absolute;right:8px;bottom:-1px;left:8px;z-index:10;height:1px;content:"";background-color:transparent;transition:background-color .25s}.vp-code-group label:hover{color:var(--vp-code-tab-hover-text-color)}.vp-code-group input:checked+label{color:var(--vp-code-tab-active-text-color)}.vp-code-group input:checked+label:after{background-color:var(--vp-code-tab-active-bar-color)}.vp-code-group div[class*=language-]{display:none;margin-top:0!important;border-top-left-radius:0!important;border-top-right-radius:0!important}.vp-code-group div[class*=language-].active{display:block}.vp-doc h1,.vp-doc h2,.vp-doc h3,.vp-doc h4,.vp-doc h5,.vp-doc h6{position:relative;font-weight:600;outline:none}.vp-doc h1{letter-spacing:-.02em;line-height:40px;font-size:28px}.vp-doc h2{margin:48px 0 16px;border-top:1px solid var(--vp-c-divider);padding-top:24px;letter-spacing:-.02em;line-height:32px;font-size:24px}.vp-doc h3{margin:32px 0 0;letter-spacing:-.01em;line-height:28px;font-size:20px}.vp-doc .header-anchor{float:left;margin-left:-.87em;padding-right:.23em;font-weight:500;user-select:none;opacity:0;transition:color .25s,opacity .25s}.vp-doc .header-anchor:before{content:var(--vp-header-anchor-symbol)}.vp-doc h1:hover .header-anchor,.vp-doc h1 .header-anchor:focus,.vp-doc h2:hover .header-anchor,.vp-doc h2 .header-anchor:focus,.vp-doc h3:hover .header-anchor,.vp-doc h3 .header-anchor:focus,.vp-doc h4:hover .header-anchor,.vp-doc h4 .header-anchor:focus,.vp-doc h5:hover .header-anchor,.vp-doc h5 .header-anchor:focus,.vp-doc h6:hover .header-anchor,.vp-doc h6 .header-anchor:focus{opacity:1}@media (min-width: 768px){.vp-doc h1{letter-spacing:-.02em;line-height:40px;font-size:32px}}.vp-doc p,.vp-doc summary{margin:16px 0}.vp-doc p{line-height:28px}.vp-doc blockquote{margin:16px 0;border-left:2px solid var(--vp-c-divider);padding-left:16px;transition:border-color .5s}.vp-doc blockquote>p{margin:0;font-size:16px;color:var(--vp-c-text-2);transition:color .5s}.vp-doc a{font-weight:500;color:var(--vp-c-brand);text-decoration-style:dotted;transition:color .25s}.vp-doc a:hover{text-decoration:underline}.vp-doc strong{font-weight:600}.vp-doc ul,.vp-doc ol{padding-left:1.25rem;margin:16px 0}.vp-doc ul{list-style:disc}.vp-doc ol{list-style:decimal}.vp-doc li+li{margin-top:8px}.vp-doc li>ol,.vp-doc li>ul{margin:8px 0 0}.vp-doc table{display:block;border-collapse:collapse;margin:20px 0;overflow-x:auto}.vp-doc tr{border-top:1px solid var(--vp-c-divider);transition:background-color .5s}.vp-doc tr:nth-child(2n){background-color:var(--vp-c-bg-soft)}.vp-doc th,.vp-doc td{border:1px solid var(--vp-c-divider);padding:8px 16px}.vp-doc th{text-align:left;font-size:14px;font-weight:600;color:var(--vp-c-text-2);background-color:var(--vp-c-bg-soft)}.vp-doc td{font-size:14px}.vp-doc hr{margin:16px 0;border:none;border-top:1px solid var(--vp-c-divider)}.vp-doc .custom-block{margin:16px 0}.vp-doc .custom-block p{margin:8px 0;line-height:24px}.vp-doc .custom-block p:first-child{margin:0}.vp-doc .custom-block a{color:inherit;font-weight:600}.vp-doc .custom-block a:hover{text-decoration:underline}.vp-doc .custom-block code{font-size:var(--vp-custom-block-code-font-size);font-weight:700;color:inherit}.vp-doc .custom-block div[class*=language-]{margin:8px 0}.vp-doc .custom-block div[class*=language-] code{font-weight:400;background-color:transparent}.vp-doc :not(pre,h1,h2,h3,h4,h5,h6)>code{font-size:var(--vp-code-font-size)}.vp-doc :not(pre)>code{border-radius:4px;padding:3px 6px;color:var(--vp-c-text-code);background-color:var(--vp-c-mute);transition:color .5s,background-color .5s}.vp-doc h1>code,.vp-doc h2>code,.vp-doc h3>code{font-size:.9em}.vp-doc a>code{color:var(--vp-c-brand);transition:color .25s}.vp-doc a:hover>code{color:var(--vp-c-brand-dark)}.vp-doc div[class*=language-]{position:relative;margin:16px -24px;background-color:var(--vp-code-block-bg);overflow-x:auto;transition:background-color .5s}@media (min-width: 640px){.vp-doc div[class*=language-]{border-radius:8px;margin:16px 0}}@media (max-width: 639px){.vp-doc li div[class*=language-]{border-radius:8px 0 0 8px}}.vp-doc div[class*=language-]+div[class*=language-],.vp-doc div[class$=-api]+div[class*=language-],.vp-doc div[class*=language-]+div[class$=-api]>div[class*=language-]{margin-top:-8px}.vp-doc [class*=language-] pre,.vp-doc [class*=language-] code{direction:ltr;text-align:left;white-space:pre;word-spacing:normal;word-break:normal;word-wrap:normal;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-hyphens:none;-moz-hyphens:none;-ms-hyphens:none;hyphens:none}.vp-doc [class*=language-] pre{position:relative;z-index:1;margin:0;padding:20px 0;background:transparent;overflow-x:auto}.vp-doc [class*=language-] code{display:block;padding:0 24px;width:fit-content;min-width:100%;line-height:var(--vp-code-line-height);font-size:var(--vp-code-font-size);color:var(--vp-code-block-color);transition:color .5s}.vp-doc [class*=language-] code .highlighted{background-color:var(--vp-code-line-highlight-color);transition:background-color .5s;margin:0 -24px;padding:0 24px;width:calc(100% + 48px);display:inline-block}.vp-doc [class*=language-] code .highlighted.error{background-color:var(--vp-code-line-error-color)}.vp-doc [class*=language-] code .highlighted.warning{background-color:var(--vp-code-line-warning-color)}.vp-doc [class*=language-] code .diff{transition:background-color .5s;margin:0 -24px;padding:0 24px;width:calc(100% + 48px);display:inline-block}.vp-doc [class*=language-] code .diff:before{position:absolute;left:10px}.vp-doc [class*=language-] .has-focused-lines .line:not(.has-focus){filter:blur(.095rem);opacity:.4;transition:filter .35s,opacity .35s}.vp-doc [class*=language-] .has-focused-lines .line:not(.has-focus){opacity:.7;transition:filter .35s,opacity .35s}.vp-doc [class*=language-]:hover .has-focused-lines .line:not(.has-focus){filter:blur(0);opacity:1}.vp-doc [class*=language-] code .diff.remove{background-color:var(--vp-code-line-diff-remove-color);opacity:.7}.vp-doc [class*=language-] code .diff.remove:before{content:"-";color:var(--vp-code-line-diff-remove-symbol-color)}.vp-doc [class*=language-] code .diff.add{background-color:var(--vp-code-line-diff-add-color)}.vp-doc [class*=language-] code .diff.add:before{content:"+";color:var(--vp-code-line-diff-add-symbol-color)}.vp-doc div[class*=language-].line-numbers-mode{padding-left:32px}.vp-doc .line-numbers-wrapper{position:absolute;top:0;bottom:0;left:0;z-index:3;border-right:1px solid var(--vp-code-block-divider-color);padding-top:20px;width:32px;text-align:center;font-family:var(--vp-font-family-mono);line-height:var(--vp-code-line-height);font-size:var(--vp-code-font-size);color:var(--vp-code-line-number-color);transition:border-color .5s,color .5s}.vp-doc [class*=language-]>button.copy{direction:ltr;position:absolute;top:12px;right:12px;z-index:3;border:1px solid var(--vp-code-copy-code-border-color);border-radius:4px;width:40px;height:40px;background-color:var(--vp-code-copy-code-bg);opacity:0;cursor:pointer;background-image:var(--vp-icon-copy);background-position:50%;background-size:20px;background-repeat:no-repeat;transition:border-color .25s,background-color .25s,opacity .25s}.vp-doc [class*=language-]:hover>button.copy,.vp-doc [class*=language-]>button.copy:focus{opacity:1}.vp-doc [class*=language-]>button.copy:hover,.vp-doc [class*=language-]>button.copy.copied{border-color:var(--vp-code-copy-code-hover-border-color);background-color:var(--vp-code-copy-code-hover-bg)}.vp-doc [class*=language-]>button.copy.copied,.vp-doc [class*=language-]>button.copy:hover.copied{border-radius:0 4px 4px 0;background-color:var(--vp-code-copy-code-hover-bg);background-image:var(--vp-icon-copied)}.vp-doc [class*=language-]>button.copy.copied:before,.vp-doc [class*=language-]>button.copy:hover.copied:before{position:relative;top:-1px;left:-65px;display:flex;justify-content:center;align-items:center;border:1px solid var(--vp-code-copy-code-hover-border-color);border-right:0;border-radius:4px 0 0 4px;width:64px;height:40px;text-align:center;font-size:12px;font-weight:500;color:var(--vp-code-copy-code-active-text);background-color:var(--vp-code-copy-code-hover-bg);white-space:nowrap;content:"Copied"}.vp-doc [class*=language-]>span.lang{position:absolute;top:2px;right:8px;z-index:2;font-size:12px;font-weight:500;color:var(--vp-c-code-dimm);transition:color .4s,opacity .4s}.vp-doc [class*=language-]:hover>button.copy+span.lang,.vp-doc [class*=language-]>button.copy:focus+span.lang{opacity:0}.vp-doc .VPTeamMembers{margin-top:24px}.vp-doc .VPTeamMembers.small.count-1 .container{margin:0!important;max-width:calc((100% - 24px)/2)!important}.vp-doc .VPTeamMembers.small.count-2 .container,.vp-doc .VPTeamMembers.small.count-3 .container{max-width:100%!important}.vp-doc .VPTeamMembers.medium.count-1 .container{margin:0!important;max-width:calc((100% - 24px)/2)!important}.vp-sponsor{border-radius:16px;overflow:hidden}.vp-sponsor.aside{border-radius:12px}.vp-sponsor-section+.vp-sponsor-section{margin-top:4px}.vp-sponsor-tier{margin-bottom:4px;text-align:center;letter-spacing:1px;line-height:24px;width:100%;font-weight:600;color:var(--vp-c-text-2);background-color:var(--vp-c-bg-soft)}.vp-sponsor.normal .vp-sponsor-tier{padding:13px 0 11px;font-size:14px}.vp-sponsor.aside .vp-sponsor-tier{padding:9px 0 7px;font-size:12px}.vp-sponsor-grid+.vp-sponsor-tier{margin-top:4px}.vp-sponsor-grid{display:flex;flex-wrap:wrap;gap:4px}.vp-sponsor-grid.xmini .vp-sponsor-grid-link{height:64px}.vp-sponsor-grid.xmini .vp-sponsor-grid-image{max-width:64px;max-height:22px}.vp-sponsor-grid.mini .vp-sponsor-grid-link{height:72px}.vp-sponsor-grid.mini .vp-sponsor-grid-image{max-width:96px;max-height:24px}.vp-sponsor-grid.small .vp-sponsor-grid-link{height:96px}.vp-sponsor-grid.small .vp-sponsor-grid-image{max-width:96px;max-height:24px}.vp-sponsor-grid.medium .vp-sponsor-grid-link{height:112px}.vp-sponsor-grid.medium .vp-sponsor-grid-image{max-width:120px;max-height:36px}.vp-sponsor-grid.big .vp-sponsor-grid-link{height:184px}.vp-sponsor-grid.big .vp-sponsor-grid-image{max-width:192px;max-height:56px}.vp-sponsor-grid[data-vp-grid="2"] .vp-sponsor-grid-item{width:calc((100% - 4px)/2)}.vp-sponsor-grid[data-vp-grid="3"] .vp-sponsor-grid-item{width:calc((100% - 4px * 2) / 3)}.vp-sponsor-grid[data-vp-grid="4"] .vp-sponsor-grid-item{width:calc((100% - 12px)/4)}.vp-sponsor-grid[data-vp-grid="5"] .vp-sponsor-grid-item{width:calc((100% - 16px)/5)}.vp-sponsor-grid[data-vp-grid="6"] .vp-sponsor-grid-item{width:calc((100% - 4px * 5) / 6)}.vp-sponsor-grid-item{flex-shrink:0;width:100%;background-color:var(--vp-c-bg-soft);transition:background-color .25s}.vp-sponsor-grid-item:hover{background-color:var(--vp-c-bg-soft-down)}.vp-sponsor-grid-item:hover .vp-sponsor-grid-image{filter:grayscale(0) invert(0)}.vp-sponsor-grid-item.empty:hover{background-color:var(--vp-c-bg-soft)}.dark .vp-sponsor-grid-item:hover{background-color:var(--vp-c-white)}.dark .vp-sponsor-grid-item.empty:hover{background-color:var(--vp-c-bg-soft)}.vp-sponsor-grid-link{display:flex}.vp-sponsor-grid-box{display:flex;justify-content:center;align-items:center;width:100%}.vp-sponsor-grid-image{max-width:100%;filter:grayscale(1);transition:filter .25s}.dark .vp-sponsor-grid-image{filter:grayscale(1) invert(1)}.VPBadge[data-v-350d3852]{display:inline-block;margin-left:2px;border:1px solid transparent;border-radius:10px;padding:0 8px;line-height:18px;font-size:12px;font-weight:600;transform:translateY(-2px)}h1 .VPBadge[data-v-350d3852],h2 .VPBadge[data-v-350d3852],h3 .VPBadge[data-v-350d3852],h4 .VPBadge[data-v-350d3852],h5 .VPBadge[data-v-350d3852],h6 .VPBadge[data-v-350d3852]{vertical-align:top}h2 .VPBadge[data-v-350d3852]{border-radius:11px;line-height:20px}.VPBadge.info[data-v-350d3852]{border-color:var(--vp-badge-info-border);color:var(--vp-badge-info-text);background-color:var(--vp-badge-info-bg)}.VPBadge.tip[data-v-350d3852]{border-color:var(--vp-badge-tip-border);color:var(--vp-badge-tip-text);background-color:var(--vp-badge-tip-bg)}.VPBadge.warning[data-v-350d3852]{border-color:var(--vp-badge-warning-border);color:var(--vp-badge-warning-text);background-color:var(--vp-badge-warning-bg)}.VPBadge.danger[data-v-350d3852]{border-color:var(--vp-badge-danger-border);color:var(--vp-badge-danger-text);background-color:var(--vp-badge-danger-bg)}.VPSkipLink[data-v-c8616af1]{top:8px;left:8px;padding:8px 16px;z-index:999;border-radius:8px;font-size:12px;font-weight:700;text-decoration:none;color:var(--vp-c-brand);box-shadow:var(--vp-shadow-3);background-color:var(--vp-c-bg)}.VPSkipLink[data-v-c8616af1]:focus{height:auto;width:auto;clip:auto;clip-path:none}.dark .VPSkipLink[data-v-c8616af1]{color:var(--vp-c-green)}@media (min-width: 1280px){.VPSkipLink[data-v-c8616af1]{top:14px;left:16px}}.VPBackdrop[data-v-c79a1216]{position:fixed;top:0;right:0;bottom:0;left:0;z-index:var(--vp-z-index-backdrop);background:var(--vp-backdrop-bg-color);transition:opacity .5s}.VPBackdrop.fade-enter-from[data-v-c79a1216],.VPBackdrop.fade-leave-to[data-v-c79a1216]{opacity:0}.VPBackdrop.fade-leave-active[data-v-c79a1216]{transition-duration:.25s}@media (min-width: 1280px){.VPBackdrop[data-v-c79a1216]{display:none}}html:not(.dark) .VPImage.dark[data-v-6db2186b]{display:none}.dark .VPImage.light[data-v-6db2186b]{display:none}.title[data-v-f4ef19a3]{display:flex;align-items:center;border-bottom:1px solid transparent;width:100%;height:var(--vp-nav-height);font-size:16px;font-weight:600;color:var(--vp-c-text-1);transition:opacity .25s}.title[data-v-f4ef19a3]:hover{opacity:.6}@media (min-width: 960px){.title[data-v-f4ef19a3]{flex-shrink:0}.VPNavBarTitle.has-sidebar .title[data-v-f4ef19a3]{border-bottom-color:var(--vp-c-divider)}}[data-v-f4ef19a3] .logo{margin-right:8px;height:24px}/*! @docsearch/css 3.3.4 | MIT License | © Algolia, Inc. and contributors | https://docsearch.algolia.com */:root{--docsearch-primary-color:#5468ff;--docsearch-text-color:#1c1e21;--docsearch-spacing:12px;--docsearch-icon-stroke-width:1.4;--docsearch-highlight-color:var(--docsearch-primary-color);--docsearch-muted-color:#969faf;--docsearch-container-background:rgba(101,108,133,.8);--docsearch-logo-color:#5468ff;--docsearch-modal-width:560px;--docsearch-modal-height:600px;--docsearch-modal-background:#f5f6f7;--docsearch-modal-shadow:inset 1px 1px 0 0 hsla(0,0%,100%,.5),0 3px 8px 0 #555a64;--docsearch-searchbox-height:56px;--docsearch-searchbox-background:#ebedf0;--docsearch-searchbox-focus-background:#fff;--docsearch-searchbox-shadow:inset 0 0 0 2px var(--docsearch-primary-color);--docsearch-hit-height:56px;--docsearch-hit-color:#444950;--docsearch-hit-active-color:#fff;--docsearch-hit-background:#fff;--docsearch-hit-shadow:0 1px 3px 0 #d4d9e1;--docsearch-key-gradient:linear-gradient(-225deg,#d5dbe4,#f8f8f8);--docsearch-key-shadow:inset 0 -2px 0 0 #cdcde6,inset 0 0 1px 1px #fff,0 1px 2px 1px rgba(30,35,90,.4);--docsearch-footer-height:44px;--docsearch-footer-background:#fff;--docsearch-footer-shadow:0 -1px 0 0 #e0e3e8,0 -3px 6px 0 rgba(69,98,155,.12)}html[data-theme=dark]{--docsearch-text-color:#f5f6f7;--docsearch-container-background:rgba(9,10,17,.8);--docsearch-modal-background:#15172a;--docsearch-modal-shadow:inset 1px 1px 0 0 #2c2e40,0 3px 8px 0 #000309;--docsearch-searchbox-background:#090a11;--docsearch-searchbox-focus-background:#000;--docsearch-hit-color:#bec3c9;--docsearch-hit-shadow:none;--docsearch-hit-background:#090a11;--docsearch-key-gradient:linear-gradient(-26.5deg,#565872,#31355b);--docsearch-key-shadow:inset 0 -2px 0 0 #282d55,inset 0 0 1px 1px #51577d,0 2px 2px 0 rgba(3,4,9,.3);--docsearch-footer-background:#1e2136;--docsearch-footer-shadow:inset 0 1px 0 0 rgba(73,76,106,.5),0 -4px 8px 0 rgba(0,0,0,.2);--docsearch-logo-color:#fff;--docsearch-muted-color:#7f8497}.DocSearch-Button{align-items:center;background:var(--docsearch-searchbox-background);border:0;border-radius:40px;color:var(--docsearch-muted-color);cursor:pointer;display:flex;font-weight:500;height:36px;justify-content:space-between;margin:0 0 0 16px;padding:0 8px;user-select:none}.DocSearch-Button:active,.DocSearch-Button:focus,.DocSearch-Button:hover{background:var(--docsearch-searchbox-focus-background);box-shadow:var(--docsearch-searchbox-shadow);color:var(--docsearch-text-color);outline:none}.DocSearch-Button-Container{align-items:center;display:flex}.DocSearch-Search-Icon{stroke-width:1.6}.DocSearch-Button .DocSearch-Search-Icon{color:var(--docsearch-text-color)}.DocSearch-Button-Placeholder{font-size:1rem;padding:0 12px 0 6px}.DocSearch-Button-Keys{display:flex;min-width:calc(40px + .8em)}.DocSearch-Button-Key{align-items:center;background:var(--docsearch-key-gradient);border-radius:3px;box-shadow:var(--docsearch-key-shadow);color:var(--docsearch-muted-color);display:flex;height:18px;justify-content:center;margin-right:.4em;position:relative;padding:0 0 2px;border:0;top:-1px;width:20px}@media (max-width:768px){.DocSearch-Button-Keys,.DocSearch-Button-Placeholder{display:none}}.DocSearch--active{overflow:hidden!important}.DocSearch-Container,.DocSearch-Container *{box-sizing:border-box}.DocSearch-Container{background-color:var(--docsearch-container-background);height:100vh;left:0;position:fixed;top:0;width:100vw;z-index:200}.DocSearch-Container a{text-decoration:none}.DocSearch-Link{appearance:none;background:none;border:0;color:var(--docsearch-highlight-color);cursor:pointer;font:inherit;margin:0;padding:0}.DocSearch-Modal{background:var(--docsearch-modal-background);border-radius:6px;box-shadow:var(--docsearch-modal-shadow);flex-direction:column;margin:60px auto auto;max-width:var(--docsearch-modal-width);position:relative}.DocSearch-SearchBar{display:flex;padding:var(--docsearch-spacing) var(--docsearch-spacing) 0}.DocSearch-Form{align-items:center;background:var(--docsearch-searchbox-focus-background);border-radius:4px;box-shadow:var(--docsearch-searchbox-shadow);display:flex;height:var(--docsearch-searchbox-height);margin:0;padding:0 var(--docsearch-spacing);position:relative;width:100%}.DocSearch-Input{appearance:none;background:transparent;border:0;color:var(--docsearch-text-color);flex:1;font:inherit;font-size:1.2em;height:100%;outline:none;padding:0 0 0 8px;width:80%}.DocSearch-Input::placeholder{color:var(--docsearch-muted-color);opacity:1}.DocSearch-Input::-webkit-search-cancel-button,.DocSearch-Input::-webkit-search-decoration,.DocSearch-Input::-webkit-search-results-button,.DocSearch-Input::-webkit-search-results-decoration{display:none}.DocSearch-LoadingIndicator,.DocSearch-MagnifierLabel,.DocSearch-Reset{margin:0;padding:0}.DocSearch-MagnifierLabel,.DocSearch-Reset{align-items:center;color:var(--docsearch-highlight-color);display:flex;justify-content:center}.DocSearch-Container--Stalled .DocSearch-MagnifierLabel,.DocSearch-LoadingIndicator{display:none}.DocSearch-Container--Stalled .DocSearch-LoadingIndicator{align-items:center;color:var(--docsearch-highlight-color);display:flex;justify-content:center}@media screen and (prefers-reduced-motion:reduce){.DocSearch-Reset{animation:none;appearance:none;background:none;border:0;border-radius:50%;color:var(--docsearch-icon-color);cursor:pointer;right:0;stroke-width:var(--docsearch-icon-stroke-width)}}.DocSearch-Reset{animation:fade-in .1s ease-in forwards;appearance:none;background:none;border:0;border-radius:50%;color:var(--docsearch-icon-color);cursor:pointer;padding:2px;right:0;stroke-width:var(--docsearch-icon-stroke-width)}.DocSearch-Reset[hidden]{display:none}.DocSearch-Reset:hover{color:var(--docsearch-highlight-color)}.DocSearch-LoadingIndicator svg,.DocSearch-MagnifierLabel svg{height:24px;width:24px}.DocSearch-Cancel{display:none}.DocSearch-Dropdown{max-height:calc(var(--docsearch-modal-height) - var(--docsearch-searchbox-height) - var(--docsearch-spacing) - var(--docsearch-footer-height));min-height:var(--docsearch-spacing);overflow-y:auto;overflow-y:overlay;padding:0 var(--docsearch-spacing);scrollbar-color:var(--docsearch-muted-color) var(--docsearch-modal-background);scrollbar-width:thin}.DocSearch-Dropdown::-webkit-scrollbar{width:12px}.DocSearch-Dropdown::-webkit-scrollbar-track{background:transparent}.DocSearch-Dropdown::-webkit-scrollbar-thumb{background-color:var(--docsearch-muted-color);border:3px solid var(--docsearch-modal-background);border-radius:20px}.DocSearch-Dropdown ul{list-style:none;margin:0;padding:0}.DocSearch-Label{font-size:.75em;line-height:1.6em}.DocSearch-Help,.DocSearch-Label{color:var(--docsearch-muted-color)}.DocSearch-Help{font-size:.9em;margin:0;user-select:none}.DocSearch-Title{font-size:1.2em}.DocSearch-Logo a{display:flex}.DocSearch-Logo svg{color:var(--docsearch-logo-color);margin-left:8px}.DocSearch-Hits:last-of-type{margin-bottom:24px}.DocSearch-Hits mark{background:none;color:var(--docsearch-highlight-color)}.DocSearch-HitsFooter{color:var(--docsearch-muted-color);display:flex;font-size:.85em;justify-content:center;margin-bottom:var(--docsearch-spacing);padding:var(--docsearch-spacing)}.DocSearch-HitsFooter a{border-bottom:1px solid;color:inherit}.DocSearch-Hit{border-radius:4px;display:flex;padding-bottom:4px;position:relative}@media screen and (prefers-reduced-motion:reduce){.DocSearch-Hit--deleting{transition:none}}.DocSearch-Hit--deleting{opacity:0;transition:all .25s linear}@media screen and (prefers-reduced-motion:reduce){.DocSearch-Hit--favoriting{transition:none}}.DocSearch-Hit--favoriting{transform:scale(0);transform-origin:top center;transition:all .25s linear;transition-delay:.25s}.DocSearch-Hit a{background:var(--docsearch-hit-background);border-radius:4px;box-shadow:var(--docsearch-hit-shadow);display:block;padding-left:var(--docsearch-spacing);width:100%}.DocSearch-Hit-source{background:var(--docsearch-modal-background);color:var(--docsearch-highlight-color);font-size:.85em;font-weight:600;line-height:32px;margin:0 -4px;padding:8px 4px 0;position:sticky;top:0;z-index:10}.DocSearch-Hit-Tree{color:var(--docsearch-muted-color);height:var(--docsearch-hit-height);opacity:.5;stroke-width:var(--docsearch-icon-stroke-width);width:24px}.DocSearch-Hit[aria-selected=true] a{background-color:var(--docsearch-highlight-color)}.DocSearch-Hit[aria-selected=true] mark{text-decoration:underline}.DocSearch-Hit-Container{align-items:center;color:var(--docsearch-hit-color);display:flex;flex-direction:row;height:var(--docsearch-hit-height);padding:0 var(--docsearch-spacing) 0 0}.DocSearch-Hit-icon{height:20px;width:20px}.DocSearch-Hit-action,.DocSearch-Hit-icon{color:var(--docsearch-muted-color);stroke-width:var(--docsearch-icon-stroke-width)}.DocSearch-Hit-action{align-items:center;display:flex;height:22px;width:22px}.DocSearch-Hit-action svg{display:block;height:18px;width:18px}.DocSearch-Hit-action+.DocSearch-Hit-action{margin-left:6px}.DocSearch-Hit-action-button{appearance:none;background:none;border:0;border-radius:50%;color:inherit;cursor:pointer;padding:2px}svg.DocSearch-Hit-Select-Icon{display:none}.DocSearch-Hit[aria-selected=true] .DocSearch-Hit-Select-Icon{display:block}.DocSearch-Hit-action-button:focus,.DocSearch-Hit-action-button:hover{background:rgba(0,0,0,.2);transition:background-color .1s ease-in}@media screen and (prefers-reduced-motion:reduce){.DocSearch-Hit-action-button:focus,.DocSearch-Hit-action-button:hover{transition:none}}.DocSearch-Hit-action-button:focus path,.DocSearch-Hit-action-button:hover path{fill:#fff}.DocSearch-Hit-content-wrapper{display:flex;flex:1 1 auto;flex-direction:column;font-weight:500;justify-content:center;line-height:1.2em;margin:0 8px;overflow-x:hidden;position:relative;text-overflow:ellipsis;white-space:nowrap;width:80%}.DocSearch-Hit-title{font-size:.9em}.DocSearch-Hit-path{color:var(--docsearch-muted-color);font-size:.75em}.DocSearch-Hit[aria-selected=true] .DocSearch-Hit-action,.DocSearch-Hit[aria-selected=true] .DocSearch-Hit-icon,.DocSearch-Hit[aria-selected=true] .DocSearch-Hit-path,.DocSearch-Hit[aria-selected=true] .DocSearch-Hit-text,.DocSearch-Hit[aria-selected=true] .DocSearch-Hit-title,.DocSearch-Hit[aria-selected=true] .DocSearch-Hit-Tree,.DocSearch-Hit[aria-selected=true] mark{color:var(--docsearch-hit-active-color)!important}@media screen and (prefers-reduced-motion:reduce){.DocSearch-Hit-action-button:focus,.DocSearch-Hit-action-button:hover{background:rgba(0,0,0,.2);transition:none}}.DocSearch-ErrorScreen,.DocSearch-NoResults,.DocSearch-StartScreen{font-size:.9em;margin:0 auto;padding:36px 0;text-align:center;width:80%}.DocSearch-Screen-Icon{color:var(--docsearch-muted-color);padding-bottom:12px}.DocSearch-NoResults-Prefill-List{display:inline-block;padding-bottom:24px;text-align:left}.DocSearch-NoResults-Prefill-List ul{display:inline-block;padding:8px 0 0}.DocSearch-NoResults-Prefill-List li{list-style-position:inside;list-style-type:"» "}.DocSearch-Prefill{appearance:none;background:none;border:0;border-radius:1em;color:var(--docsearch-highlight-color);cursor:pointer;display:inline-block;font-size:1em;font-weight:700;padding:0}.DocSearch-Prefill:focus,.DocSearch-Prefill:hover{outline:none;text-decoration:underline}.DocSearch-Footer{align-items:center;background:var(--docsearch-footer-background);border-radius:0 0 8px 8px;box-shadow:var(--docsearch-footer-shadow);display:flex;flex-direction:row-reverse;flex-shrink:0;height:var(--docsearch-footer-height);justify-content:space-between;padding:0 var(--docsearch-spacing);position:relative;user-select:none;width:100%;z-index:300}.DocSearch-Commands{color:var(--docsearch-muted-color);display:flex;list-style:none;margin:0;padding:0}.DocSearch-Commands li{align-items:center;display:flex}.DocSearch-Commands li:not(:last-of-type){margin-right:.8em}.DocSearch-Commands-Key{align-items:center;background:var(--docsearch-key-gradient);border-radius:2px;box-shadow:var(--docsearch-key-shadow);display:flex;height:18px;justify-content:center;margin-right:.4em;padding:0 0 1px;color:var(--docsearch-muted-color);border:0;width:20px}@media (max-width:768px){:root{--docsearch-spacing:10px;--docsearch-footer-height:40px}.DocSearch-Dropdown{height:100%}.DocSearch-Container{height:100vh;height:-webkit-fill-available;height:calc(var(--docsearch-vh, 1vh)*100);position:absolute}.DocSearch-Footer{border-radius:0;bottom:0;position:absolute}.DocSearch-Hit-content-wrapper{display:flex;position:relative;width:80%}.DocSearch-Modal{border-radius:0;box-shadow:none;height:100vh;height:-webkit-fill-available;height:calc(var(--docsearch-vh, 1vh)*100);margin:0;max-width:100%;width:100%}.DocSearch-Dropdown{max-height:calc(var(--docsearch-vh, 1vh)*100 - var(--docsearch-searchbox-height) - var(--docsearch-spacing) - var(--docsearch-footer-height))}.DocSearch-Cancel{appearance:none;background:none;border:0;color:var(--docsearch-highlight-color);cursor:pointer;display:inline-block;flex:none;font:inherit;font-size:1em;font-weight:500;margin-left:var(--docsearch-spacing);outline:none;overflow:hidden;padding:0;user-select:none;white-space:nowrap}.DocSearch-Commands,.DocSearch-Hit-Tree{display:none}}@keyframes fade-in{0%{opacity:0}to{opacity:1}}.DocSearch{--docsearch-primary-color: var(--vp-c-brand);--docsearch-highlight-color: var(--docsearch-primary-color);--docsearch-text-color: var(--vp-c-text-1);--docsearch-muted-color: var(--vp-c-text-2);--docsearch-searchbox-shadow: none;--docsearch-searchbox-focus-background: transparent;--docsearch-key-gradient: transparent;--docsearch-key-shadow: none;--docsearch-modal-background: var(--vp-c-bg-soft);--docsearch-footer-background: var(--vp-c-bg)}.dark .DocSearch{--docsearch-modal-shadow: none;--docsearch-footer-shadow: none;--docsearch-logo-color: var(--vp-c-text-2);--docsearch-hit-background: var(--vp-c-bg-soft-mute);--docsearch-hit-color: var(--vp-c-text-2);--docsearch-hit-shadow: none}.DocSearch-Button{display:flex;justify-content:center;align-items:center;margin:0;padding:0;width:32px;height:55px;background:transparent;transition:border-color .25s}.DocSearch-Button:hover{background:transparent}.DocSearch-Button:focus{outline:1px dotted;outline:5px auto -webkit-focus-ring-color}.DocSearch-Button:focus:not(:focus-visible){outline:none!important}@media (min-width: 768px){.DocSearch-Button{justify-content:flex-start;border:1px solid transparent;border-radius:8px;padding:0 10px 0 12px;width:100%;height:40px;background-color:var(--vp-c-bg-alt)}.DocSearch-Button:hover{border-color:var(--vp-c-brand);background:var(--vp-c-bg-alt)}}.DocSearch-Button .DocSearch-Button-Container{display:flex;align-items:center}.DocSearch-Button .DocSearch-Search-Icon{position:relative;width:16px;height:16px;color:var(--vp-c-text-1);fill:currentColor;transition:color .5s}.DocSearch-Button:hover .DocSearch-Search-Icon{color:var(--vp-c-text-1)}@media (min-width: 768px){.DocSearch-Button .DocSearch-Search-Icon{top:1px;margin-right:8px;width:14px;height:14px;color:var(--vp-c-text-2)}}.DocSearch-Button .DocSearch-Button-Placeholder{display:none;margin-top:2px;padding:0 16px 0 0;font-size:13px;font-weight:500;color:var(--vp-c-text-2);transition:color .5s}.DocSearch-Button:hover .DocSearch-Button-Placeholder{color:var(--vp-c-text-1)}@media (min-width: 768px){.DocSearch-Button .DocSearch-Button-Placeholder{display:inline-block}}.DocSearch-Button .DocSearch-Button-Keys{direction:ltr;display:none;min-width:auto}@media (min-width: 768px){.DocSearch-Button .DocSearch-Button-Keys{display:flex;align-items:center}}.DocSearch-Button .DocSearch-Button-Key{display:block;margin:2px 0 0;border:1px solid var(--vp-c-divider);border-right:none;border-radius:4px 0 0 4px;padding-left:6px;min-width:0;width:auto;height:22px;line-height:22px;font-family:var(--vp-font-family-base);font-size:12px;font-weight:500;transition:color .5s,border-color .5s}.DocSearch-Button .DocSearch-Button-Key+.DocSearch-Button-Key{border-right:1px solid var(--vp-c-divider);border-left:none;border-radius:0 4px 4px 0;padding-left:2px;padding-right:6px}.DocSearch-Button .DocSearch-Button-Key:first-child{font-size:1px;letter-spacing:-12px;color:transparent}.DocSearch-Button .DocSearch-Button-Key:first-child:after{content:var(--vp-meta-key);font-size:12px;letter-spacing:normal;color:var(--docsearch-muted-color)}.DocSearch-Button .DocSearch-Button-Key:first-child>*{display:none}.VPNavBarSearch{display:flex;align-items:center}@media (min-width: 768px){.VPNavBarSearch{flex-grow:1;padding-left:24px}}@media (min-width: 960px){.VPNavBarSearch{padding-left:32px}}.dark .DocSearch-Footer{border-top:1px solid var(--vp-c-divider)}.DocSearch-Form{border:1px solid var(--vp-c-brand);background-color:var(--vp-c-white)}.dark .DocSearch-Form{background-color:var(--vp-c-bg-soft-mute)}.DocSearch-Screen-Icon>svg{margin:auto}.icon[data-v-8f4dc553]{display:inline-block;margin-top:-1px;margin-left:4px;width:11px;height:11px;fill:var(--vp-c-text-3);transition:fill .25s;flex-shrink:0}.VPNavBarMenuLink[data-v-37adc828]{display:flex;align-items:center;padding:0 12px;line-height:var(--vp-nav-height);font-size:14px;font-weight:500;color:var(--vp-c-text-1);transition:color .25s}.VPNavBarMenuLink.active[data-v-37adc828],.VPNavBarMenuLink[data-v-37adc828]:hover{color:var(--vp-c-brand)}.VPMenuGroup+.VPMenuLink[data-v-d2c93bab]{margin:12px -12px 0;border-top:1px solid var(--vp-c-divider);padding:12px 12px 0}.link[data-v-d2c93bab]{display:block;border-radius:6px;padding:0 12px;line-height:32px;font-size:14px;font-weight:500;color:var(--vp-c-text-1);white-space:nowrap;transition:background-color .25s,color .25s}.link[data-v-d2c93bab]:hover{color:var(--vp-c-brand);background-color:var(--vp-c-bg-elv-mute)}.link.active[data-v-d2c93bab]{color:var(--vp-c-brand)}.VPMenuGroup[data-v-69e747b5]{margin:12px -12px 0;border-top:1px solid var(--vp-c-divider);padding:12px 12px 0}.VPMenuGroup[data-v-69e747b5]:first-child{margin-top:0;border-top:0;padding-top:0}.VPMenuGroup+.VPMenuGroup[data-v-69e747b5]{margin-top:12px;border-top:1px solid var(--vp-c-divider)}.title[data-v-69e747b5]{padding:0 12px;line-height:32px;font-size:14px;font-weight:600;color:var(--vp-c-text-2);white-space:nowrap;transition:color .25s}.VPMenu[data-v-e7ea1737]{border-radius:12px;padding:12px;min-width:128px;border:1px solid var(--vp-c-divider);background-color:var(--vp-c-bg-elv);box-shadow:var(--vp-shadow-3);transition:background-color .5s;max-height:calc(100vh - var(--vp-nav-height));overflow-y:auto}.VPMenu[data-v-e7ea1737] .group{margin:0 -12px;padding:0 12px 12px}.VPMenu[data-v-e7ea1737] .group+.group{border-top:1px solid var(--vp-c-divider);padding:11px 12px 12px}.VPMenu[data-v-e7ea1737] .group:last-child{padding-bottom:0}.VPMenu[data-v-e7ea1737] .group+.item{border-top:1px solid var(--vp-c-divider);padding:11px 16px 0}.VPMenu[data-v-e7ea1737] .item{padding:0 16px;white-space:nowrap}.VPMenu[data-v-e7ea1737] .label{flex-grow:1;line-height:28px;font-size:12px;font-weight:500;color:var(--vp-c-text-2);transition:color .5s}.VPMenu[data-v-e7ea1737] .action{padding-left:24px}.VPFlyout[data-v-764effdf]{position:relative}.VPFlyout[data-v-764effdf]:hover{color:var(--vp-c-brand);transition:color .25s}.VPFlyout:hover .text[data-v-764effdf]{color:var(--vp-c-text-2)}.VPFlyout:hover .icon[data-v-764effdf]{fill:var(--vp-c-text-2)}.VPFlyout.active .text[data-v-764effdf]{color:var(--vp-c-brand)}.VPFlyout.active:hover .text[data-v-764effdf]{color:var(--vp-c-brand-dark)}.VPFlyout:hover .menu[data-v-764effdf],.button[aria-expanded=true]+.menu[data-v-764effdf]{opacity:1;visibility:visible;transform:translateY(0)}.button[data-v-764effdf]{display:flex;align-items:center;padding:0 12px;height:var(--vp-nav-height);color:var(--vp-c-text-1);transition:color .5s}.text[data-v-764effdf]{display:flex;align-items:center;line-height:var(--vp-nav-height);font-size:14px;font-weight:500;color:var(--vp-c-text-1);transition:color .25s}.option-icon[data-v-764effdf]{margin-right:0;width:16px;height:16px;fill:currentColor}.text-icon[data-v-764effdf]{margin-left:4px;width:14px;height:14px;fill:currentColor}.icon[data-v-764effdf]{width:20px;height:20px;fill:currentColor;transition:fill .25s}.menu[data-v-764effdf]{position:absolute;top:calc(var(--vp-nav-height) / 2 + 20px);right:0;opacity:0;visibility:hidden;transition:opacity .25s,visibility .25s,transform .25s}.VPNavBarMenu[data-v-7f418b0f]{display:none}@media (min-width: 768px){.VPNavBarMenu[data-v-7f418b0f]{display:flex}}.VPNavBarTranslations[data-v-74abcbb9]{display:none}@media (min-width: 1280px){.VPNavBarTranslations[data-v-74abcbb9]{display:flex;align-items:center}}.title[data-v-74abcbb9]{padding:0 24px 0 12px;line-height:32px;font-size:14px;font-weight:700;color:var(--vp-c-text-1)}.VPSwitch[data-v-f3c41672]{position:relative;border-radius:11px;display:block;width:40px;height:22px;flex-shrink:0;border:1px solid var(--vp-input-border-color);background-color:var(--vp-input-switch-bg-color);transition:border-color .25s}.VPSwitch[data-v-f3c41672]:hover{border-color:var(--vp-input-hover-border-color)}.check[data-v-f3c41672]{position:absolute;top:1px;left:1px;width:18px;height:18px;border-radius:50%;background-color:var(--vp-c-neutral-inverse);box-shadow:var(--vp-shadow-1);transition:transform .25s}.icon[data-v-f3c41672]{position:relative;display:block;width:18px;height:18px;border-radius:50%;overflow:hidden}.icon[data-v-f3c41672] svg{position:absolute;top:3px;left:3px;width:12px;height:12px;fill:var(--vp-c-text-2)}.dark .icon[data-v-f3c41672] svg{fill:var(--vp-c-text-1);transition:opacity .25s}.sun[data-v-a9c8afb8]{opacity:1}.moon[data-v-a9c8afb8],.dark .sun[data-v-a9c8afb8]{opacity:0}.dark .moon[data-v-a9c8afb8]{opacity:1}.dark .VPSwitchAppearance[data-v-a9c8afb8] .check{transform:translate(18px)}.VPNavBarAppearance[data-v-f6a63727]{display:none}@media (min-width: 1280px){.VPNavBarAppearance[data-v-f6a63727]{display:flex;align-items:center}}.VPSocialLink[data-v-c530cc0a]{display:flex;justify-content:center;align-items:center;width:36px;height:36px;color:var(--vp-c-text-2);transition:color .5s}.VPSocialLink[data-v-c530cc0a]:hover{color:var(--vp-c-text-1);transition:color .25s}.VPSocialLink[data-v-c530cc0a]>svg{width:20px;height:20px;fill:currentColor}.VPSocialLinks[data-v-f6988cfb]{display:flex;flex-wrap:wrap;justify-content:center}.VPNavBarSocialLinks[data-v-0394ad82]{display:none}@media (min-width: 1280px){.VPNavBarSocialLinks[data-v-0394ad82]{display:flex;align-items:center}}.VPNavBarExtra[data-v-40855f84]{display:none;margin-right:-12px}@media (min-width: 768px){.VPNavBarExtra[data-v-40855f84]{display:block}}@media (min-width: 1280px){.VPNavBarExtra[data-v-40855f84]{display:none}}.trans-title[data-v-40855f84]{padding:0 24px 0 12px;line-height:32px;font-size:14px;font-weight:700;color:var(--vp-c-text-1)}.item.appearance[data-v-40855f84],.item.social-links[data-v-40855f84]{display:flex;align-items:center;padding:0 12px}.item.appearance[data-v-40855f84]{min-width:176px}.appearance-action[data-v-40855f84]{margin-right:-2px}.social-links-list[data-v-40855f84]{margin:-4px -8px}.VPNavBarHamburger[data-v-e5dd9c1c]{display:flex;justify-content:center;align-items:center;width:48px;height:var(--vp-nav-height)}@media (min-width: 768px){.VPNavBarHamburger[data-v-e5dd9c1c]{display:none}}.container[data-v-e5dd9c1c]{position:relative;width:16px;height:14px;overflow:hidden}.VPNavBarHamburger:hover .top[data-v-e5dd9c1c]{top:0;left:0;transform:translate(4px)}.VPNavBarHamburger:hover .middle[data-v-e5dd9c1c]{top:6px;left:0;transform:translate(0)}.VPNavBarHamburger:hover .bottom[data-v-e5dd9c1c]{top:12px;left:0;transform:translate(8px)}.VPNavBarHamburger.active .top[data-v-e5dd9c1c]{top:6px;transform:translate(0) rotate(225deg)}.VPNavBarHamburger.active .middle[data-v-e5dd9c1c]{top:6px;transform:translate(16px)}.VPNavBarHamburger.active .bottom[data-v-e5dd9c1c]{top:6px;transform:translate(0) rotate(135deg)}.VPNavBarHamburger.active:hover .top[data-v-e5dd9c1c],.VPNavBarHamburger.active:hover .middle[data-v-e5dd9c1c],.VPNavBarHamburger.active:hover .bottom[data-v-e5dd9c1c]{background-color:var(--vp-c-text-2);transition:top .25s,background-color .25s,transform .25s}.top[data-v-e5dd9c1c],.middle[data-v-e5dd9c1c],.bottom[data-v-e5dd9c1c]{position:absolute;width:16px;height:2px;background-color:var(--vp-c-text-1);transition:top .25s,background-color .5s,transform .25s}.top[data-v-e5dd9c1c]{top:0;left:0;transform:translate(0)}.middle[data-v-e5dd9c1c]{top:6px;left:0;transform:translate(8px)}.bottom[data-v-e5dd9c1c]{top:12px;left:0;transform:translate(4px)}.VPNavBar[data-v-94c81dcc]{position:relative;border-bottom:1px solid transparent;padding:0 8px 0 24px;height:var(--vp-nav-height);transition:border-color .5s,background-color .5s;pointer-events:none;white-space:nowrap}.VPNavBar.has-sidebar[data-v-94c81dcc]{border-bottom-color:var(--vp-c-gutter)}@media (min-width: 768px){.VPNavBar[data-v-94c81dcc]{padding:0 32px}}@media (min-width: 960px){.VPNavBar.has-sidebar[data-v-94c81dcc]{border-bottom-color:transparent;padding:0}.VPNavBar.fill[data-v-94c81dcc]:not(.has-sidebar){border-bottom-color:var(--vp-c-gutter);background-color:var(--vp-nav-bg-color)}}.container[data-v-94c81dcc]{display:flex;justify-content:space-between;margin:0 auto;max-width:calc(var(--vp-layout-max-width) - 64px);height:var(--vp-nav-height);pointer-events:none}.container>.title[data-v-94c81dcc],.container>.content[data-v-94c81dcc]{pointer-events:none}.container[data-v-94c81dcc] *{pointer-events:auto}@media (min-width: 960px){.VPNavBar.has-sidebar .container[data-v-94c81dcc]{max-width:100%}}.title[data-v-94c81dcc]{flex-shrink:0;height:calc(var(--vp-nav-height) - 1px);transition:background-color .5s}@media (min-width: 960px){.VPNavBar.has-sidebar .title[data-v-94c81dcc]{position:absolute;top:0;left:0;z-index:2;padding:0 32px;width:var(--vp-sidebar-width);height:var(--vp-nav-height);background-color:transparent}}@media (min-width: 1440px){.VPNavBar.has-sidebar .title[data-v-94c81dcc]{padding-left:max(32px,calc((100% - (var(--vp-layout-max-width) - 64px)) / 2));width:calc((100% - (var(--vp-layout-max-width) - 64px)) / 2 + var(--vp-sidebar-width) - 32px)}}.content[data-v-94c81dcc]{flex-grow:1}@media (min-width: 960px){.VPNavBar.has-sidebar .content[data-v-94c81dcc]{position:relative;z-index:1;padding-right:32px;padding-left:var(--vp-sidebar-width)}}@media (min-width: 1440px){.VPNavBar.has-sidebar .content[data-v-94c81dcc]{padding-right:calc((100vw - var(--vp-layout-max-width)) / 2 + 32px);padding-left:calc((100vw - var(--vp-layout-max-width)) / 2 + var(--vp-sidebar-width))}}.content-body[data-v-94c81dcc]{display:flex;justify-content:flex-end;align-items:center;height:calc(var(--vp-nav-height) - 1px);transition:background-color .5s}@media (min-width: 960px){.VPNavBar.has-sidebar .content-body[data-v-94c81dcc],.VPNavBar.fill .content-body[data-v-94c81dcc]{position:relative;background-color:var(--vp-nav-bg-color)}}.menu+.translations[data-v-94c81dcc]:before,.menu+.appearance[data-v-94c81dcc]:before,.menu+.social-links[data-v-94c81dcc]:before,.translations+.appearance[data-v-94c81dcc]:before,.appearance+.social-links[data-v-94c81dcc]:before{margin-right:8px;margin-left:8px;width:1px;height:24px;background-color:var(--vp-c-divider);content:""}.menu+.appearance[data-v-94c81dcc]:before,.translations+.appearance[data-v-94c81dcc]:before{margin-right:16px}.appearance+.social-links[data-v-94c81dcc]:before{margin-left:16px}.social-links[data-v-94c81dcc]{margin-right:-8px}@media (min-width: 960px){.VPNavBar.has-sidebar .curtain[data-v-94c81dcc]{position:absolute;right:0;bottom:-31px;width:calc(100% - var(--vp-sidebar-width));height:32px}.VPNavBar.has-sidebar .curtain[data-v-94c81dcc]:before{display:block;width:100%;height:32px;background:linear-gradient(var(--vp-c-bg),transparent 70%);content:""}}@media (min-width: 1440px){.VPNavBar.has-sidebar .curtain[data-v-94c81dcc]{width:calc(100% - ((100vw - var(--vp-layout-max-width)) / 2 + var(--vp-sidebar-width)))}}.VPNavScreenMenuLink[data-v-c328f34f]{display:block;border-bottom:1px solid var(--vp-c-divider);padding:12px 0 11px;line-height:24px;font-size:14px;font-weight:500;color:var(--vp-c-text-1);transition:border-color .25s,color .25s}.VPNavScreenMenuLink[data-v-c328f34f]:hover{color:var(--vp-c-brand)}.VPNavScreenMenuGroupLink[data-v-3d20956d]{display:block;margin-left:12px;line-height:32px;font-size:14px;font-weight:400;color:var(--vp-c-text-1);transition:color .25s}.VPNavScreenMenuGroupLink[data-v-3d20956d]:hover{color:var(--vp-c-brand)}.VPNavScreenMenuGroupSection[data-v-7478538b]{display:block}.title[data-v-7478538b]{line-height:32px;font-size:13px;font-weight:700;color:var(--vp-c-text-2);transition:color .25s}.VPNavScreenMenuGroup[data-v-a9a19324]{border-bottom:1px solid var(--vp-c-divider);height:48px;overflow:hidden;transition:border-color .5s}.VPNavScreenMenuGroup .items[data-v-a9a19324]{visibility:hidden}.VPNavScreenMenuGroup.open .items[data-v-a9a19324]{visibility:visible}.VPNavScreenMenuGroup.open[data-v-a9a19324]{padding-bottom:10px;height:auto}.VPNavScreenMenuGroup.open .button[data-v-a9a19324]{padding-bottom:6px;color:var(--vp-c-brand)}.VPNavScreenMenuGroup.open .button-icon[data-v-a9a19324]{transform:rotate(45deg)}.button[data-v-a9a19324]{display:flex;justify-content:space-between;align-items:center;padding:12px 4px 11px 0;width:100%;line-height:24px;font-size:14px;font-weight:500;color:var(--vp-c-text-1);transition:color .25s}.button[data-v-a9a19324]:hover{color:var(--vp-c-brand)}.button-icon[data-v-a9a19324]{width:14px;height:14px;fill:var(--vp-c-text-2);transition:fill .5s,transform .25s}.group[data-v-a9a19324]:first-child{padding-top:0}.group+.group[data-v-a9a19324],.group+.item[data-v-a9a19324]{padding-top:4px}.VPNavScreenAppearance[data-v-add8f686]{display:flex;justify-content:space-between;align-items:center;border-radius:8px;padding:12px 14px 12px 16px;background-color:var(--vp-c-bg-soft)}.text[data-v-add8f686]{line-height:24px;font-size:12px;font-weight:500;color:var(--vp-c-text-2)}.VPNavScreenTranslations[data-v-d72aa483]{height:24px;overflow:hidden}.VPNavScreenTranslations.open[data-v-d72aa483]{height:auto}.title[data-v-d72aa483]{display:flex;align-items:center;font-size:14px;font-weight:500;color:var(--vp-c-text-1)}.icon[data-v-d72aa483]{width:16px;height:16px;fill:currentColor}.icon.lang[data-v-d72aa483]{margin-right:8px}.icon.chevron[data-v-d72aa483]{margin-left:4px}.list[data-v-d72aa483]{padding:4px 0 0 24px}.link[data-v-d72aa483]{line-height:32px;font-size:13px;color:var(--vp-c-text-1)}.VPNavScreen[data-v-724636ae]{position:fixed;top:calc(var(--vp-nav-height) + var(--vp-layout-top-height, 0px) + 1px);right:0;bottom:0;left:0;padding:0 32px;width:100%;background-color:var(--vp-nav-screen-bg-color);overflow-y:auto;transition:background-color .5s;pointer-events:auto}.VPNavScreen.fade-enter-active[data-v-724636ae],.VPNavScreen.fade-leave-active[data-v-724636ae]{transition:opacity .25s}.VPNavScreen.fade-enter-active .container[data-v-724636ae],.VPNavScreen.fade-leave-active .container[data-v-724636ae]{transition:transform .25s ease}.VPNavScreen.fade-enter-from[data-v-724636ae],.VPNavScreen.fade-leave-to[data-v-724636ae]{opacity:0}.VPNavScreen.fade-enter-from .container[data-v-724636ae],.VPNavScreen.fade-leave-to .container[data-v-724636ae]{transform:translateY(-8px)}@media (min-width: 768px){.VPNavScreen[data-v-724636ae]{display:none}}.container[data-v-724636ae]{margin:0 auto;padding:24px 0 96px;max-width:288px}.menu+.translations[data-v-724636ae],.menu+.appearance[data-v-724636ae],.translations+.appearance[data-v-724636ae]{margin-top:24px}.menu+.social-links[data-v-724636ae]{margin-top:16px}.appearance+.social-links[data-v-724636ae]{margin-top:16px}.VPNav[data-v-7e5bc4a5]{position:relative;top:var(--vp-layout-top-height, 0px);left:0;z-index:var(--vp-z-index-nav);width:100%;pointer-events:none;transition:background-color .5s}@media (min-width: 960px){.VPNav[data-v-7e5bc4a5]{position:fixed}}.root[data-v-9a431c33]{position:relative;z-index:1}.nested[data-v-9a431c33]{padding-left:13px}.outline-link[data-v-9a431c33]{display:block;line-height:28px;color:var(--vp-c-text-2);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;transition:color .5s;font-weight:500}.outline-link[data-v-9a431c33]:hover,.outline-link.active[data-v-9a431c33]{color:var(--vp-c-text-1);transition:color .25s}.outline-link.nested[data-v-9a431c33]{padding-left:13px}.VPLocalNavOutlineDropdown[data-v-079b16a8]{padding:12px 20px 11px}.VPLocalNavOutlineDropdown button[data-v-079b16a8]{display:block;font-size:12px;font-weight:500;line-height:24px;color:var(--vp-c-text-2);transition:color .5s;position:relative}.VPLocalNavOutlineDropdown button[data-v-079b16a8]:hover{color:var(--vp-c-text-1);transition:color .25s}.VPLocalNavOutlineDropdown button.open[data-v-079b16a8]{color:var(--vp-c-text-1)}.icon[data-v-079b16a8]{display:inline-block;vertical-align:middle;margin-left:2px;width:14px;height:14px;fill:currentColor}[data-v-079b16a8] .outline-link{font-size:14px;padding:2px 0}.open>.icon[data-v-079b16a8]{transform:rotate(90deg)}.items[data-v-079b16a8]{position:absolute;left:20px;right:20px;top:64px;background-color:var(--vp-local-nav-bg-color);padding:4px 10px 16px;border:1px solid var(--vp-c-divider);border-radius:8px;max-height:calc(var(--vp-vh, 100vh) - 86px);overflow:hidden auto;box-shadow:var(--vp-shadow-3)}.top-link[data-v-079b16a8]{display:block;color:var(--vp-c-brand);font-size:13px;font-weight:500;padding:6px 0;margin:0 13px 10px;border-bottom:1px solid var(--vp-c-divider)}.flyout-enter-active[data-v-079b16a8]{transition:all .2s ease-out}.flyout-leave-active[data-v-079b16a8]{transition:all .15s ease-in}.flyout-enter-from[data-v-079b16a8],.flyout-leave-to[data-v-079b16a8]{opacity:0;transform:translateY(-16px)}.VPLocalNav[data-v-392e1bf8]{position:sticky;top:0;left:0;z-index:var(--vp-z-index-local-nav);display:flex;justify-content:space-between;align-items:center;border-bottom:1px solid var(--vp-c-gutter);padding-top:var(--vp-layout-top-height, 0px);width:100%;background-color:var(--vp-local-nav-bg-color);transition:border-color .5s,background-color .5s}@media (min-width: 960px){.VPLocalNav[data-v-392e1bf8]{display:none}}.menu[data-v-392e1bf8]{display:flex;align-items:center;padding:12px 24px 11px;line-height:24px;font-size:12px;font-weight:500;color:var(--vp-c-text-2);transition:color .5s}.menu[data-v-392e1bf8]:hover{color:var(--vp-c-text-1);transition:color .25s}@media (min-width: 768px){.menu[data-v-392e1bf8]{padding:0 32px}}.menu-icon[data-v-392e1bf8]{margin-right:8px;width:16px;height:16px;fill:currentColor}.VPOutlineDropdown[data-v-392e1bf8]{padding:12px 24px 11px}@media (min-width: 768px){.VPOutlineDropdown[data-v-392e1bf8]{padding:12px 32px 11px}}.VPSidebarItem.level-0[data-v-c4656e6d]{padding-bottom:24px}.VPSidebarItem.collapsed.level-0[data-v-c4656e6d]{padding-bottom:10px}.item[data-v-c4656e6d]{position:relative;display:flex;width:100%}.VPSidebarItem.collapsible>.item[data-v-c4656e6d]{cursor:pointer}.indicator[data-v-c4656e6d]{position:absolute;top:6px;bottom:6px;left:-17px;width:1px;transition:background-color .25s}.VPSidebarItem.level-2.is-active>.item>.indicator[data-v-c4656e6d],.VPSidebarItem.level-3.is-active>.item>.indicator[data-v-c4656e6d],.VPSidebarItem.level-4.is-active>.item>.indicator[data-v-c4656e6d],.VPSidebarItem.level-5.is-active>.item>.indicator[data-v-c4656e6d]{background-color:var(--vp-c-brand)}.link[data-v-c4656e6d]{display:flex;align-items:center;flex-grow:1}.text[data-v-c4656e6d]{flex-grow:1;padding:4px 0;line-height:24px;font-size:14px;transition:color .25s}.VPSidebarItem.level-0 .text[data-v-c4656e6d]{font-weight:700;color:var(--vp-c-text-1)}.VPSidebarItem.level-1 .text[data-v-c4656e6d],.VPSidebarItem.level-2 .text[data-v-c4656e6d],.VPSidebarItem.level-3 .text[data-v-c4656e6d],.VPSidebarItem.level-4 .text[data-v-c4656e6d],.VPSidebarItem.level-5 .text[data-v-c4656e6d]{font-weight:500;color:var(--vp-c-text-2)}.VPSidebarItem.level-0.is-link>.item>.link:hover .text[data-v-c4656e6d],.VPSidebarItem.level-1.is-link>.item>.link:hover .text[data-v-c4656e6d],.VPSidebarItem.level-2.is-link>.item>.link:hover .text[data-v-c4656e6d],.VPSidebarItem.level-3.is-link>.item>.link:hover .text[data-v-c4656e6d],.VPSidebarItem.level-4.is-link>.item>.link:hover .text[data-v-c4656e6d],.VPSidebarItem.level-5.is-link>.item>.link:hover .text[data-v-c4656e6d]{color:var(--vp-c-brand)}.VPSidebarItem.level-0.has-active>.item>.link>.text[data-v-c4656e6d],.VPSidebarItem.level-1.has-active>.item>.link>.text[data-v-c4656e6d],.VPSidebarItem.level-2.has-active>.item>.link>.text[data-v-c4656e6d],.VPSidebarItem.level-3.has-active>.item>.link>.text[data-v-c4656e6d],.VPSidebarItem.level-4.has-active>.item>.link>.text[data-v-c4656e6d],.VPSidebarItem.level-5.has-active>.item>.link>.text[data-v-c4656e6d]{color:var(--vp-c-text-1)}.VPSidebarItem.level-0.is-active>.item .link>.text[data-v-c4656e6d],.VPSidebarItem.level-1.is-active>.item .link>.text[data-v-c4656e6d],.VPSidebarItem.level-2.is-active>.item .link>.text[data-v-c4656e6d],.VPSidebarItem.level-3.is-active>.item .link>.text[data-v-c4656e6d],.VPSidebarItem.level-4.is-active>.item .link>.text[data-v-c4656e6d],.VPSidebarItem.level-5.is-active>.item .link>.text[data-v-c4656e6d]{color:var(--vp-c-brand)}.caret[data-v-c4656e6d]{display:flex;justify-content:center;align-items:center;margin-right:-7px;width:32px;height:32px;color:var(--vp-c-text-3);cursor:pointer;transition:color .25s}.item:hover .caret[data-v-c4656e6d]{color:var(--vp-c-text-2)}.item:hover .caret[data-v-c4656e6d]:hover{color:var(--vp-c-text-1)}.caret-icon[data-v-c4656e6d]{width:18px;height:18px;fill:currentColor;transform:rotate(90deg);transition:transform .25s}.VPSidebarItem.collapsed .caret-icon[data-v-c4656e6d]{transform:rotate(0)}.VPSidebarItem.level-1 .items[data-v-c4656e6d],.VPSidebarItem.level-2 .items[data-v-c4656e6d],.VPSidebarItem.level-3 .items[data-v-c4656e6d],.VPSidebarItem.level-4 .items[data-v-c4656e6d],.VPSidebarItem.level-5 .items[data-v-c4656e6d]{border-left:1px solid var(--vp-c-divider);padding-left:16px}.VPSidebarItem.collapsed .items[data-v-c4656e6d]{display:none}.VPSidebar[data-v-af16598e]{position:fixed;top:var(--vp-layout-top-height, 0px);bottom:0;left:0;z-index:var(--vp-z-index-sidebar);padding:32px 32px 96px;width:calc(100vw - 64px);max-width:320px;background-color:var(--vp-sidebar-bg-color);opacity:0;box-shadow:var(--vp-c-shadow-3);overflow-x:hidden;overflow-y:auto;transform:translate(-100%);transition:opacity .5s,transform .25s ease;overscroll-behavior:contain}.VPSidebar.open[data-v-af16598e]{opacity:1;visibility:visible;transform:translate(0);transition:opacity .25s,transform .5s cubic-bezier(.19,1,.22,1)}.dark .VPSidebar[data-v-af16598e]{box-shadow:var(--vp-shadow-1)}@media (min-width: 960px){.VPSidebar[data-v-af16598e]{z-index:1;padding-top:var(--vp-nav-height);padding-bottom:128px;width:var(--vp-sidebar-width);max-width:100%;background-color:var(--vp-sidebar-bg-color);opacity:1;visibility:visible;box-shadow:none;transform:translate(0)}}@media (min-width: 1440px){.VPSidebar[data-v-af16598e]{padding-left:max(32px,calc((100% - (var(--vp-layout-max-width) - 64px)) / 2));width:calc((100% - (var(--vp-layout-max-width) - 64px)) / 2 + var(--vp-sidebar-width) - 32px)}}@media (min-width: 960px){.curtain[data-v-af16598e]{position:sticky;top:-64px;left:0;z-index:1;margin-top:calc(var(--vp-nav-height) * -1);margin-right:-32px;margin-left:-32px;height:var(--vp-nav-height);background-color:var(--vp-sidebar-bg-color)}}.nav[data-v-af16598e]{outline:0}.group+.group[data-v-af16598e]{border-top:1px solid var(--vp-c-divider);padding-top:10px}@media (min-width: 960px){.group[data-v-af16598e]{padding-top:10px;width:calc(var(--vp-sidebar-width) - 64px)}}.VPButton[data-v-567ba664]{display:inline-block;border:1px solid transparent;text-align:center;font-weight:600;white-space:nowrap;transition:color .25s,border-color .25s,background-color .25s}.VPButton[data-v-567ba664]:active{transition:color .1s,border-color .1s,background-color .1s}.VPButton.medium[data-v-567ba664]{border-radius:20px;padding:0 20px;line-height:38px;font-size:14px}.VPButton.big[data-v-567ba664]{border-radius:24px;padding:0 24px;line-height:46px;font-size:16px}.VPButton.brand[data-v-567ba664]{border-color:var(--vp-button-brand-border);color:var(--vp-button-brand-text);background-color:var(--vp-button-brand-bg)}.VPButton.brand[data-v-567ba664]:hover{border-color:var(--vp-button-brand-hover-border);color:var(--vp-button-brand-hover-text);background-color:var(--vp-button-brand-hover-bg)}.VPButton.brand[data-v-567ba664]:active{border-color:var(--vp-button-brand-active-border);color:var(--vp-button-brand-active-text);background-color:var(--vp-button-brand-active-bg)}.VPButton.alt[data-v-567ba664]{border-color:var(--vp-button-alt-border);color:var(--vp-button-alt-text);background-color:var(--vp-button-alt-bg)}.VPButton.alt[data-v-567ba664]:hover{border-color:var(--vp-button-alt-hover-border);color:var(--vp-button-alt-hover-text);background-color:var(--vp-button-alt-hover-bg)}.VPButton.alt[data-v-567ba664]:active{border-color:var(--vp-button-alt-active-border);color:var(--vp-button-alt-active-text);background-color:var(--vp-button-alt-active-bg)}.VPButton.sponsor[data-v-567ba664]{border-color:var(--vp-button-sponsor-border);color:var(--vp-button-sponsor-text);background-color:var(--vp-button-sponsor-bg)}.VPButton.sponsor[data-v-567ba664]:hover{border-color:var(--vp-button-sponsor-hover-border);color:var(--vp-button-sponsor-hover-text);background-color:var(--vp-button-sponsor-hover-bg)}.VPButton.sponsor[data-v-567ba664]:active{border-color:var(--vp-button-sponsor-active-border);color:var(--vp-button-sponsor-active-text);background-color:var(--vp-button-sponsor-active-bg)}.VPHero[data-v-fd2650d5]{margin-top:calc((var(--vp-nav-height) + var(--vp-layout-top-height, 0px)) * -1);padding:calc(var(--vp-nav-height) + var(--vp-layout-top-height, 0px) + 48px) 24px 48px}@media (min-width: 640px){.VPHero[data-v-fd2650d5]{padding:calc(var(--vp-nav-height) + var(--vp-layout-top-height, 0px) + 80px) 48px 64px}}@media (min-width: 960px){.VPHero[data-v-fd2650d5]{padding:calc(var(--vp-nav-height) + var(--vp-layout-top-height, 0px) + 80px) 64px 64px}}.container[data-v-fd2650d5]{display:flex;flex-direction:column;margin:0 auto;max-width:1152px}@media (min-width: 960px){.container[data-v-fd2650d5]{flex-direction:row}}.main[data-v-fd2650d5]{position:relative;z-index:10;order:2;flex-grow:1;flex-shrink:0}.VPHero.has-image .container[data-v-fd2650d5]{text-align:center}@media (min-width: 960px){.VPHero.has-image .container[data-v-fd2650d5]{text-align:left}}@media (min-width: 960px){.main[data-v-fd2650d5]{order:1;width:calc((100% / 3) * 2)}.VPHero.has-image .main[data-v-fd2650d5]{max-width:592px}}.name[data-v-fd2650d5],.text[data-v-fd2650d5]{max-width:392px;letter-spacing:-.4px;line-height:40px;font-size:32px;font-weight:700;white-space:pre-wrap}.VPHero.has-image .name[data-v-fd2650d5],.VPHero.has-image .text[data-v-fd2650d5]{margin:0 auto}.name[data-v-fd2650d5]{color:var(--vp-home-hero-name-color)}.clip[data-v-fd2650d5]{background:var(--vp-home-hero-name-background);-webkit-background-clip:text;background-clip:text;-webkit-text-fill-color:var(--vp-home-hero-name-color)}@media (min-width: 640px){.name[data-v-fd2650d5],.text[data-v-fd2650d5]{max-width:576px;line-height:56px;font-size:48px}}@media (min-width: 960px){.name[data-v-fd2650d5],.text[data-v-fd2650d5]{line-height:64px;font-size:56px}.VPHero.has-image .name[data-v-fd2650d5],.VPHero.has-image .text[data-v-fd2650d5]{margin:0}}.tagline[data-v-fd2650d5]{padding-top:8px;max-width:392px;line-height:28px;font-size:18px;font-weight:500;white-space:pre-wrap;color:var(--vp-c-text-2)}.VPHero.has-image .tagline[data-v-fd2650d5]{margin:0 auto}@media (min-width: 640px){.tagline[data-v-fd2650d5]{padding-top:12px;max-width:576px;line-height:32px;font-size:20px}}@media (min-width: 960px){.tagline[data-v-fd2650d5]{line-height:36px;font-size:24px}.VPHero.has-image .tagline[data-v-fd2650d5]{margin:0}}.actions[data-v-fd2650d5]{display:flex;flex-wrap:wrap;margin:-6px;padding-top:24px}.VPHero.has-image .actions[data-v-fd2650d5]{justify-content:center}@media (min-width: 640px){.actions[data-v-fd2650d5]{padding-top:32px}}@media (min-width: 960px){.VPHero.has-image .actions[data-v-fd2650d5]{justify-content:flex-start}}.action[data-v-fd2650d5]{flex-shrink:0;padding:6px}.image[data-v-fd2650d5]{order:1;margin:-76px -24px -48px}@media (min-width: 640px){.image[data-v-fd2650d5]{margin:-108px -24px -48px}}@media (min-width: 960px){.image[data-v-fd2650d5]{flex-grow:1;order:2;margin:0;min-height:100%}}.image-container[data-v-fd2650d5]{position:relative;margin:0 auto;width:320px;height:320px}@media (min-width: 640px){.image-container[data-v-fd2650d5]{width:392px;height:392px}}@media (min-width: 960px){.image-container[data-v-fd2650d5]{display:flex;justify-content:center;align-items:center;width:100%;height:100%;transform:translate(-32px,-32px)}}.image-bg[data-v-fd2650d5]{position:absolute;top:50%;left:50%;border-radius:50%;width:192px;height:192px;background-image:var(--vp-home-hero-image-background-image);filter:var(--vp-home-hero-image-filter);transform:translate(-50%,-50%)}@media (min-width: 640px){.image-bg[data-v-fd2650d5]{width:256px;height:256px}}@media (min-width: 960px){.image-bg[data-v-fd2650d5]{width:320px;height:320px}}[data-v-fd2650d5] .image-src{position:absolute;top:50%;left:50%;max-width:192px;max-height:192px;transform:translate(-50%,-50%)}@media (min-width: 640px){[data-v-fd2650d5] .image-src{max-width:256px;max-height:256px}}@media (min-width: 960px){[data-v-fd2650d5] .image-src{max-width:320px;max-height:320px}}.VPFeature[data-v-837f6cca]{display:block;border:1px solid var(--vp-c-bg-soft);border-radius:12px;height:100%;background-color:var(--vp-c-bg-soft);transition:border-color .25s,background-color .25s}.VPFeature.link[data-v-837f6cca]:hover{border-color:var(--vp-c-brand);background-color:var(--vp-c-bg-soft-up)}.box[data-v-837f6cca]{display:flex;flex-direction:column;padding:24px;height:100%}.VPFeature[data-v-837f6cca] .VPImage{width:48px;height:48px;margin-bottom:20px}.icon[data-v-837f6cca]{display:flex;justify-content:center;align-items:center;margin-bottom:20px;border-radius:6px;background-color:var(--vp-c-bg-soft-down);width:48px;height:48px;font-size:24px;transition:background-color .25s}.title[data-v-837f6cca]{line-height:24px;font-size:16px;font-weight:600}.details[data-v-837f6cca]{flex-grow:1;padding-top:8px;line-height:24px;font-size:14px;font-weight:500;color:var(--vp-c-text-2)}.link-text[data-v-837f6cca]{padding-top:8px}.link-text-value[data-v-837f6cca]{display:flex;align-items:center;font-size:14px;font-weight:500;color:var(--vp-c-brand)}.link-text-icon[data-v-837f6cca]{display:inline-block;margin-left:6px;width:14px;height:14px;fill:currentColor}.VPFeatures[data-v-6816157f]{position:relative;padding:0 24px}@media (min-width: 640px){.VPFeatures[data-v-6816157f]{padding:0 48px}}@media (min-width: 960px){.VPFeatures[data-v-6816157f]{padding:0 64px}}.container[data-v-6816157f]{margin:0 auto;max-width:1152px}.items[data-v-6816157f]{display:flex;flex-wrap:wrap;margin:-8px}.item[data-v-6816157f]{padding:8px;width:100%}@media (min-width: 640px){.item.grid-2[data-v-6816157f],.item.grid-4[data-v-6816157f],.item.grid-6[data-v-6816157f]{width:50%}}@media (min-width: 768px){.item.grid-2[data-v-6816157f],.item.grid-4[data-v-6816157f]{width:50%}.item.grid-3[data-v-6816157f],.item.grid-6[data-v-6816157f]{width:calc(100% / 3)}}@media (min-width: 960px){.item.grid-4[data-v-6816157f]{width:25%}}.VPHome[data-v-d82743a8]{padding-bottom:96px}.VPHome[data-v-d82743a8] .VPHomeSponsors{margin-top:112px;margin-bottom:-128px}@media (min-width: 768px){.VPHome[data-v-d82743a8]{padding-bottom:128px}}.VPDocAsideOutline[data-v-ff0f39c8]{display:none}.VPDocAsideOutline.has-outline[data-v-ff0f39c8]{display:block}.content[data-v-ff0f39c8]{position:relative;border-left:1px solid var(--vp-c-divider);padding-left:16px;font-size:13px;font-weight:500}.outline-marker[data-v-ff0f39c8]{position:absolute;top:32px;left:-1px;z-index:0;opacity:0;width:1px;height:18px;background-color:var(--vp-c-brand);transition:top .25s cubic-bezier(0,1,.5,1),background-color .5s,opacity .25s}.outline-title[data-v-ff0f39c8]{letter-spacing:.4px;line-height:28px;font-size:13px;font-weight:600}.VPDocAside[data-v-3f215769]{display:flex;flex-direction:column;flex-grow:1}.spacer[data-v-3f215769]{flex-grow:1}.VPDocAside[data-v-3f215769] .spacer+.VPDocAsideSponsors,.VPDocAside[data-v-3f215769] .spacer+.VPDocAsideCarbonAds{margin-top:24px}.VPDocAside[data-v-3f215769] .VPDocAsideSponsors+.VPDocAsideCarbonAds{margin-top:16px}.VPLastUpdated[data-v-7b3ebfe1]{line-height:24px;font-size:14px;font-weight:500;color:var(--vp-c-text-2)}@media (min-width: 640px){.VPLastUpdated[data-v-7b3ebfe1]{line-height:32px;font-size:14px;font-weight:500}}.VPDocFooter[data-v-face870a]{margin-top:64px}.edit-info[data-v-face870a]{padding-bottom:18px}@media (min-width: 640px){.edit-info[data-v-face870a]{display:flex;justify-content:space-between;align-items:center;padding-bottom:14px}}.edit-link-button[data-v-face870a]{display:flex;align-items:center;border:0;line-height:32px;font-size:14px;font-weight:500;color:var(--vp-c-brand);transition:color .25s}.edit-link-button[data-v-face870a]:hover{color:var(--vp-c-brand-dark)}.edit-link-icon[data-v-face870a]{margin-right:8px;width:14px;height:14px;fill:currentColor}.prev-next[data-v-face870a]{border-top:1px solid var(--vp-c-divider);padding-top:24px}@media (min-width: 640px){.prev-next[data-v-face870a]{display:flex}}.pager.has-prev[data-v-face870a]{padding-top:8px}@media (min-width: 640px){.pager[data-v-face870a]{display:flex;flex-direction:column;flex-shrink:0;width:50%}.pager.has-prev[data-v-face870a]{padding-top:0;padding-left:16px}}.pager-link[data-v-face870a]{display:block;border:1px solid var(--vp-c-divider);border-radius:8px;padding:11px 16px 13px;width:100%;height:100%;transition:border-color .25s}.pager-link[data-v-face870a]:hover{border-color:var(--vp-c-brand)}.pager-link.next[data-v-face870a]{margin-left:auto;text-align:right}.desc[data-v-face870a]{display:block;line-height:20px;font-size:12px;font-weight:500;color:var(--vp-c-text-2)}.title[data-v-face870a]{display:block;line-height:20px;font-size:14px;font-weight:500;color:var(--vp-c-brand);transition:color .25s}.VPDocOutlineDropdown[data-v-2edece88]{margin-bottom:42px}.VPDocOutlineDropdown button[data-v-2edece88]{display:block;font-size:14px;font-weight:500;line-height:24px;color:var(--vp-c-text-2);transition:color .5s;border:1px solid var(--vp-c-border);padding:4px 12px;border-radius:8px}.VPDocOutlineDropdown button[data-v-2edece88]:hover{color:var(--vp-c-text-1);transition:color .25s}.VPDocOutlineDropdown button.open[data-v-2edece88]{color:var(--vp-c-text-1)}.icon[data-v-2edece88]{display:inline-block;vertical-align:middle;margin-left:2px;width:14px;height:14px;fill:currentColor}[data-v-2edece88] .outline-link{font-size:13px}.open>.icon[data-v-2edece88]{transform:rotate(90deg)}.items[data-v-2edece88]{margin-top:10px;border-left:1px solid var(--vp-c-divider)}.VPDoc[data-v-c4b0d3cf]{padding:32px 24px 96px;width:100%}.VPDoc .VPDocOutlineDropdown[data-v-c4b0d3cf]{display:none}@media (min-width: 960px) and (max-width: 1280px){.VPDoc .VPDocOutlineDropdown[data-v-c4b0d3cf]{display:block}}@media (min-width: 768px){.VPDoc[data-v-c4b0d3cf]{padding:48px 32px 128px}}@media (min-width: 960px){.VPDoc[data-v-c4b0d3cf]{padding:32px 32px 0}.VPDoc:not(.has-sidebar) .container[data-v-c4b0d3cf]{display:flex;justify-content:center;max-width:992px}.VPDoc:not(.has-sidebar) .content[data-v-c4b0d3cf]{max-width:752px}}@media (min-width: 1280px){.VPDoc .container[data-v-c4b0d3cf]{display:flex;justify-content:center}.VPDoc .aside[data-v-c4b0d3cf]{display:block}}@media (min-width: 1440px){.VPDoc:not(.has-sidebar) .content[data-v-c4b0d3cf]{max-width:784px}.VPDoc:not(.has-sidebar) .container[data-v-c4b0d3cf]{max-width:1104px}}.container[data-v-c4b0d3cf]{margin:0 auto;width:100%}.aside[data-v-c4b0d3cf]{position:relative;display:none;order:2;flex-grow:1;padding-left:32px;width:100%;max-width:256px}.left-aside[data-v-c4b0d3cf]{order:1;padding-left:unset;padding-right:32px}.aside-container[data-v-c4b0d3cf]{position:fixed;top:0;padding-top:calc(var(--vp-nav-height) + var(--vp-layout-top-height, 0px) + var(--vp-doc-top-height, 0px) + 32px);width:224px;height:100vh;overflow-x:hidden;overflow-y:auto;scrollbar-width:none}.aside-container[data-v-c4b0d3cf]::-webkit-scrollbar{display:none}.aside-curtain[data-v-c4b0d3cf]{position:fixed;bottom:0;z-index:10;width:224px;height:32px;background:linear-gradient(transparent,var(--vp-c-bg) 70%)}.aside-content[data-v-c4b0d3cf]{display:flex;flex-direction:column;min-height:calc(100vh - (var(--vp-nav-height) + var(--vp-layout-top-height, 0px) + 32px));padding-bottom:32px}.content[data-v-c4b0d3cf]{position:relative;margin:0 auto;width:100%}@media (min-width: 960px){.content[data-v-c4b0d3cf]{padding:0 32px 128px}}@media (min-width: 1280px){.content[data-v-c4b0d3cf]{order:1;margin:0;min-width:640px}}.content-container[data-v-c4b0d3cf]{margin:0 auto}.VPDoc.has-aside .content-container[data-v-c4b0d3cf]{max-width:688px}.NotFound[data-v-c70503b8]{padding:64px 24px 96px;text-align:center}@media (min-width: 768px){.NotFound[data-v-c70503b8]{padding:96px 32px 168px}}.code[data-v-c70503b8]{line-height:64px;font-size:64px;font-weight:600}.title[data-v-c70503b8]{padding-top:12px;letter-spacing:2px;line-height:20px;font-size:20px;font-weight:700}.divider[data-v-c70503b8]{margin:24px auto 18px;width:64px;height:1px;background-color:var(--vp-c-divider)}.quote[data-v-c70503b8]{margin:0 auto;max-width:256px;font-size:14px;font-weight:500;color:var(--vp-c-text-2)}.action[data-v-c70503b8]{padding-top:20px}.link[data-v-c70503b8]{display:inline-block;border:1px solid var(--vp-c-brand);border-radius:16px;padding:3px 16px;font-size:14px;font-weight:500;color:var(--vp-c-brand);transition:border-color .25s,color .25s}.link[data-v-c70503b8]:hover{border-color:var(--vp-c-brand-dark);color:var(--vp-c-brand-dark)}.VPContent[data-v-a494bd1d]{flex-grow:1;flex-shrink:0;margin:var(--vp-layout-top-height, 0px) auto 0;width:100%}.VPContent.is-home[data-v-a494bd1d]{width:100%;max-width:100%}.VPContent.has-sidebar[data-v-a494bd1d]{margin:0}@media (min-width: 960px){.VPContent[data-v-a494bd1d]{padding-top:var(--vp-nav-height)}.VPContent.has-sidebar[data-v-a494bd1d]{margin:var(--vp-layout-top-height, 0px) 0 0;padding-left:var(--vp-sidebar-width)}}@media (min-width: 1440px){.VPContent.has-sidebar[data-v-a494bd1d]{padding-right:calc((100vw - var(--vp-layout-max-width)) / 2);padding-left:calc((100vw - var(--vp-layout-max-width)) / 2 + var(--vp-sidebar-width))}}.VPFooter[data-v-2f86ebd2]{position:relative;z-index:var(--vp-z-index-footer);border-top:1px solid var(--vp-c-gutter);padding:32px 24px;background-color:var(--vp-c-bg)}.VPFooter.has-sidebar[data-v-2f86ebd2]{display:none}@media (min-width: 768px){.VPFooter[data-v-2f86ebd2]{padding:32px}}.container[data-v-2f86ebd2]{margin:0 auto;max-width:var(--vp-layout-max-width);text-align:center}.message[data-v-2f86ebd2],.copyright[data-v-2f86ebd2]{line-height:24px;font-size:14px;font-weight:500;color:var(--vp-c-text-2)}.message[data-v-2f86ebd2]{order:2}.copyright[data-v-2f86ebd2]{order:1}.Layout[data-v-b2cf3e0b]{display:flex;flex-direction:column;min-height:100vh}.VPHomeSponsors[data-v-3c6e61c2]{border-top:1px solid var(--vp-c-gutter);padding:88px 24px 96px;background-color:var(--vp-c-bg)}.container[data-v-3c6e61c2]{margin:0 auto;max-width:1152px}.love[data-v-3c6e61c2]{margin:0 auto;width:28px;height:28px;color:var(--vp-c-text-3)}.icon[data-v-3c6e61c2]{width:28px;height:28px;fill:currentColor}.message[data-v-3c6e61c2]{margin:0 auto;padding-top:10px;max-width:320px;text-align:center;line-height:24px;font-size:16px;font-weight:500;color:var(--vp-c-text-2)}.sponsors[data-v-3c6e61c2]{padding-top:32px}.action[data-v-3c6e61c2]{padding-top:40px;text-align:center}.VPTeamPage[data-v-10b00018]{padding-bottom:96px}@media (min-width: 768px){.VPTeamPage[data-v-10b00018]{padding-bottom:128px}}.VPTeamPageSection+.VPTeamPageSection[data-v-10b00018-s],.VPTeamMembers+.VPTeamPageSection[data-v-10b00018-s]{margin-top:64px}.VPTeamMembers+.VPTeamMembers[data-v-10b00018-s]{margin-top:24px}@media (min-width: 768px){.VPTeamPageTitle+.VPTeamPageSection[data-v-10b00018-s]{margin-top:16px}.VPTeamPageSection+.VPTeamPageSection[data-v-10b00018-s],.VPTeamMembers+.VPTeamPageSection[data-v-10b00018-s]{margin-top:96px}}.VPTeamMembers[data-v-10b00018-s]{padding:0 24px}@media (min-width: 768px){.VPTeamMembers[data-v-10b00018-s]{padding:0 48px}}@media (min-width: 960px){.VPTeamMembers[data-v-10b00018-s]{padding:0 64px}}.VPTeamPageTitle[data-v-bf2cbdac]{padding:48px 32px;text-align:center}@media (min-width: 768px){.VPTeamPageTitle[data-v-bf2cbdac]{padding:64px 48px 48px}}@media (min-width: 960px){.VPTeamPageTitle[data-v-bf2cbdac]{padding:80px 64px 48px}}.title[data-v-bf2cbdac]{letter-spacing:0;line-height:44px;font-size:36px;font-weight:500}@media (min-width: 768px){.title[data-v-bf2cbdac]{letter-spacing:-.5px;line-height:56px;font-size:48px}}.lead[data-v-bf2cbdac]{margin:0 auto;max-width:512px;padding-top:12px;line-height:24px;font-size:16px;font-weight:500;color:var(--vp-c-text-2)}@media (min-width: 768px){.lead[data-v-bf2cbdac]{max-width:592px;letter-spacing:.15px;line-height:28px;font-size:20px}}.VPTeamPageSection[data-v-b1a88750]{padding:0 32px}@media (min-width: 768px){.VPTeamPageSection[data-v-b1a88750]{padding:0 48px}}@media (min-width: 960px){.VPTeamPageSection[data-v-b1a88750]{padding:0 64px}}.title[data-v-b1a88750]{position:relative;margin:0 auto;max-width:1152px;text-align:center;color:var(--vp-c-text-2)}.title-line[data-v-b1a88750]{position:absolute;top:16px;left:0;width:100%;height:1px;background-color:var(--vp-c-divider)}.title-text[data-v-b1a88750]{position:relative;display:inline-block;padding:0 24px;letter-spacing:0;line-height:32px;font-size:20px;font-weight:500;background-color:var(--vp-c-bg)}.lead[data-v-b1a88750]{margin:0 auto;max-width:480px;padding-top:12px;text-align:center;line-height:24px;font-size:16px;font-weight:500;color:var(--vp-c-text-2)}.members[data-v-b1a88750]{padding-top:40px}.VPTeamMembersItem[data-v-a3462077]{display:flex;flex-direction:column;gap:2px;border-radius:12px;width:100%;height:100%;overflow:hidden}.VPTeamMembersItem.small .profile[data-v-a3462077]{padding:32px}.VPTeamMembersItem.small .data[data-v-a3462077]{padding-top:20px}.VPTeamMembersItem.small .avatar[data-v-a3462077]{width:64px;height:64px}.VPTeamMembersItem.small .name[data-v-a3462077]{line-height:24px;font-size:16px}.VPTeamMembersItem.small .affiliation[data-v-a3462077]{padding-top:4px;line-height:20px;font-size:14px}.VPTeamMembersItem.small .desc[data-v-a3462077]{padding-top:12px;line-height:20px;font-size:14px}.VPTeamMembersItem.small .links[data-v-a3462077]{margin:0 -16px -20px;padding:10px 0 0}.VPTeamMembersItem.medium .profile[data-v-a3462077]{padding:48px 32px}.VPTeamMembersItem.medium .data[data-v-a3462077]{padding-top:24px;text-align:center}.VPTeamMembersItem.medium .avatar[data-v-a3462077]{width:96px;height:96px}.VPTeamMembersItem.medium .name[data-v-a3462077]{letter-spacing:.15px;line-height:28px;font-size:20px}.VPTeamMembersItem.medium .affiliation[data-v-a3462077]{padding-top:4px;font-size:16px}.VPTeamMembersItem.medium .desc[data-v-a3462077]{padding-top:16px;max-width:288px;font-size:16px}.VPTeamMembersItem.medium .links[data-v-a3462077]{margin:0 -16px -12px;padding:16px 12px 0}.profile[data-v-a3462077]{flex-grow:1;background-color:var(--vp-c-bg-soft)}.data[data-v-a3462077]{text-align:center}.avatar[data-v-a3462077]{position:relative;flex-shrink:0;margin:0 auto;border-radius:50%;box-shadow:var(--vp-shadow-3)}.avatar-img[data-v-a3462077]{position:absolute;top:0;right:0;bottom:0;left:0;border-radius:50%;object-fit:cover}.name[data-v-a3462077]{margin:0;font-weight:600}.affiliation[data-v-a3462077]{margin:0;font-weight:500;color:var(--vp-c-text-2)}.org.link[data-v-a3462077]{color:var(--vp-c-text-2);transition:color .25s}.org.link[data-v-a3462077]:hover{color:var(--vp-c-brand)}.desc[data-v-a3462077]{margin:0 auto}.desc[data-v-a3462077] a{font-weight:500;color:var(--vp-c-brand);text-decoration-style:dotted;transition:color .25s}.links[data-v-a3462077]{display:flex;justify-content:center;height:56px}.sp-link[data-v-a3462077]{display:flex;justify-content:center;align-items:center;text-align:center;padding:16px;font-size:14px;font-weight:500;color:var(--vp-c-sponsor);background-color:var(--vp-c-bg-soft);transition:color .25s,background-color .25s}.sp .sp-link.link[data-v-a3462077]:hover,.sp .sp-link.link[data-v-a3462077]:focus{outline:none;color:var(--vp-c-white);background-color:var(--vp-c-sponsor)}.sp-icon[data-v-a3462077]{margin-right:8px;width:16px;height:16px;fill:currentColor}.VPTeamMembers.small .container[data-v-04685dce]{grid-template-columns:repeat(auto-fit,minmax(224px,1fr))}.VPTeamMembers.small.count-1 .container[data-v-04685dce]{max-width:276px}.VPTeamMembers.small.count-2 .container[data-v-04685dce]{max-width:576px}.VPTeamMembers.small.count-3 .container[data-v-04685dce]{max-width:876px}.VPTeamMembers.medium .container[data-v-04685dce]{grid-template-columns:repeat(auto-fit,minmax(256px,1fr))}@media (min-width: 375px){.VPTeamMembers.medium .container[data-v-04685dce]{grid-template-columns:repeat(auto-fit,minmax(288px,1fr))}}.VPTeamMembers.medium.count-1 .container[data-v-04685dce]{max-width:368px}.VPTeamMembers.medium.count-2 .container[data-v-04685dce]{max-width:760px}.container[data-v-04685dce]{display:grid;gap:24px;margin:0 auto;max-width:1152px} diff --git a/assets/style.aaa90101.css b/assets/style.aaa90101.css new file mode 100644 index 000000000000..d06c7fe95b89 --- /dev/null +++ b/assets/style.aaa90101.css @@ -0,0 +1 @@ +@font-face{font-family:Inter var;font-weight:100 900;font-display:swap;font-style:normal;font-named-instance:"Regular";src:url(/assets/inter-roman-cyrillic.5f2c6c8c.woff2) format("woff2");unicode-range:U+0301,U+0400-045F,U+0490-0491,U+04B0-04B1,U+2116}@font-face{font-family:Inter var;font-weight:100 900;font-display:swap;font-style:normal;font-named-instance:"Regular";src:url(/assets/inter-roman-cyrillic-ext.e75737ce.woff2) format("woff2");unicode-range:U+0460-052F,U+1C80-1C88,U+20B4,U+2DE0-2DFF,U+A640-A69F,U+FE2E-FE2F}@font-face{font-family:Inter var;font-weight:100 900;font-display:swap;font-style:normal;font-named-instance:"Regular";src:url(/assets/inter-roman-greek.d5a6d92a.woff2) format("woff2");unicode-range:U+0370-03FF}@font-face{font-family:Inter var;font-weight:100 900;font-display:swap;font-style:normal;font-named-instance:"Regular";src:url(/assets/inter-roman-greek-ext.ab0619bc.woff2) format("woff2");unicode-range:U+1F00-1FFF}@font-face{font-family:Inter var;font-weight:100 900;font-display:swap;font-style:normal;font-named-instance:"Regular";src:url(/assets/inter-roman-latin.2ed14f66.woff2) format("woff2");unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+2000-206F,U+2074,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}@font-face{font-family:Inter var;font-weight:100 900;font-display:swap;font-style:normal;font-named-instance:"Regular";src:url(/assets/inter-roman-latin-ext.0030eebd.woff2) format("woff2");unicode-range:U+0100-024F,U+0259,U+1E00-1EFF,U+2020,U+20A0-20AB,U+20AD-20CF,U+2113,U+2C60-2C7F,U+A720-A7FF}@font-face{font-family:Inter var;font-weight:100 900;font-display:swap;font-style:normal;font-named-instance:"Regular";src:url(/assets/inter-roman-vietnamese.14ce25a6.woff2) format("woff2");unicode-range:U+0102-0103,U+0110-0111,U+0128-0129,U+0168-0169,U+01A0-01A1,U+01AF-01B0,U+1EA0-1EF9,U+20AB}@font-face{font-family:Inter var;font-weight:100 900;font-display:swap;font-style:italic;font-named-instance:"Italic";src:url(/assets/inter-italic-cyrillic.ea42a392.woff2) format("woff2");unicode-range:U+0301,U+0400-045F,U+0490-0491,U+04B0-04B1,U+2116}@font-face{font-family:Inter var;font-weight:100 900;font-display:swap;font-style:italic;font-named-instance:"Italic";src:url(/assets/inter-italic-cyrillic-ext.33bd5a8e.woff2) format("woff2");unicode-range:U+0460-052F,U+1C80-1C88,U+20B4,U+2DE0-2DFF,U+A640-A69F,U+FE2E-FE2F}@font-face{font-family:Inter var;font-weight:100 900;font-display:swap;font-style:italic;font-named-instance:"Italic";src:url(/assets/inter-italic-greek.8f4463c4.woff2) format("woff2");unicode-range:U+0370-03FF}@font-face{font-family:Inter var;font-weight:100 900;font-display:swap;font-style:italic;font-named-instance:"Italic";src:url(/assets/inter-italic-greek-ext.4fbe9427.woff2) format("woff2");unicode-range:U+1F00-1FFF}@font-face{font-family:Inter var;font-weight:100 900;font-display:swap;font-style:italic;font-named-instance:"Italic";src:url(/assets/inter-italic-latin.bd3b6f56.woff2) format("woff2");unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+2000-206F,U+2074,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}@font-face{font-family:Inter var;font-weight:100 900;font-display:swap;font-style:italic;font-named-instance:"Italic";src:url(/assets/inter-italic-latin-ext.bd8920cc.woff2) format("woff2");unicode-range:U+0100-024F,U+0259,U+1E00-1EFF,U+2020,U+20A0-20AB,U+20AD-20CF,U+2113,U+2C60-2C7F,U+A720-A7FF}@font-face{font-family:Inter var;font-weight:100 900;font-display:swap;font-style:italic;font-named-instance:"Italic";src:url(/assets/inter-italic-vietnamese.6ce511fb.woff2) format("woff2");unicode-range:U+0102-0103,U+0110-0111,U+0128-0129,U+0168-0169,U+01A0-01A1,U+01AF-01B0,U+1EA0-1EF9,U+20AB}@font-face{font-family:Chinese Quotes;src:local("PingFang SC Regular"),local("PingFang SC"),local("SimHei"),local("Source Han Sans SC");unicode-range:U+2018,U+2019,U+201C,U+201D}:root{--vp-c-white: #ffffff;--vp-c-black: #000000;--vp-c-neutral: var(--vp-c-black);--vp-c-neutral-inverse: var(--vp-c-white)}.dark{--vp-c-neutral: var(--vp-c-white);--vp-c-neutral-inverse: var(--vp-c-black)}:root{--vp-c-gray-1: #dddde3;--vp-c-gray-2: #e4e4e9;--vp-c-gray-3: #ebebef;--vp-c-gray-soft: rgba(142, 150, 170, .14);--vp-c-indigo-1: #3451b2;--vp-c-indigo-2: #3a5ccc;--vp-c-indigo-3: #5672cd;--vp-c-indigo-soft: rgba(100, 108, 255, .14);--vp-c-green-1: #18794e;--vp-c-green-2: #299764;--vp-c-green-3: #30a46c;--vp-c-green-soft: rgba(16, 185, 129, .14);--vp-c-yellow-1: #915930;--vp-c-yellow-2: #946300;--vp-c-yellow-3: #9f6a00;--vp-c-yellow-soft: rgba(234, 179, 8, .14);--vp-c-red-1: #b8272c;--vp-c-red-2: #d5393e;--vp-c-red-3: #e0575b;--vp-c-red-soft: rgba(244, 63, 94, .14);--vp-c-sponsor: #db2777}.dark{--vp-c-gray-1: #515c67;--vp-c-gray-2: #414853;--vp-c-gray-3: #32363f;--vp-c-gray-soft: rgba(101, 117, 133, .16);--vp-c-indigo-1: #a8b1ff;--vp-c-indigo-2: #5c73e7;--vp-c-indigo-3: #3e63dd;--vp-c-indigo-soft: rgba(100, 108, 255, .16);--vp-c-green-1: #3dd68c;--vp-c-green-2: #30a46c;--vp-c-green-3: #298459;--vp-c-green-soft: rgba(16, 185, 129, .16);--vp-c-yellow-1: #f9b44e;--vp-c-yellow-2: #da8b17;--vp-c-yellow-3: #a46a0a;--vp-c-yellow-soft: rgba(234, 179, 8, .16);--vp-c-red-1: #f66f81;--vp-c-red-2: #f14158;--vp-c-red-3: #b62a3c;--vp-c-red-soft: rgba(244, 63, 94, .16)}:root{--vp-c-bg: #ffffff;--vp-c-bg-alt: #f6f6f7;--vp-c-bg-elv: #ffffff;--vp-c-bg-soft: #f6f6f7}.dark{--vp-c-bg: #1b1b1f;--vp-c-bg-alt: #161618;--vp-c-bg-elv: #202127;--vp-c-bg-soft: #202127}:root{--vp-c-border: #c2c2c4;--vp-c-divider: #e2e2e3;--vp-c-gutter: #e2e2e3}.dark{--vp-c-border: #3c3f44;--vp-c-divider: #2e2e32;--vp-c-gutter: #000000}:root{--vp-c-text-1: rgba(60, 60, 67);--vp-c-text-2: rgba(60, 60, 67, .78);--vp-c-text-3: rgba(60, 60, 67, .56)}.dark{--vp-c-text-1: rgba(255, 255, 245, .86);--vp-c-text-2: rgba(235, 235, 245, .6);--vp-c-text-3: rgba(235, 235, 245, .38)}:root{--vp-c-default-1: var(--vp-c-gray-1);--vp-c-default-2: var(--vp-c-gray-2);--vp-c-default-3: var(--vp-c-gray-3);--vp-c-default-soft: var(--vp-c-gray-soft);--vp-c-brand-1: var(--vp-c-indigo-1);--vp-c-brand-2: var(--vp-c-indigo-2);--vp-c-brand-3: var(--vp-c-indigo-3);--vp-c-brand-soft: var(--vp-c-indigo-soft);--vp-c-brand: var(--vp-c-brand-1);--vp-c-tip-1: var(--vp-c-brand-1);--vp-c-tip-2: var(--vp-c-brand-2);--vp-c-tip-3: var(--vp-c-brand-3);--vp-c-tip-soft: var(--vp-c-brand-soft);--vp-c-warning-1: var(--vp-c-yellow-1);--vp-c-warning-2: var(--vp-c-yellow-2);--vp-c-warning-3: var(--vp-c-yellow-3);--vp-c-warning-soft: var(--vp-c-yellow-soft);--vp-c-danger-1: var(--vp-c-red-1);--vp-c-danger-2: var(--vp-c-red-2);--vp-c-danger-3: var(--vp-c-red-3);--vp-c-danger-soft: var(--vp-c-red-soft)}:root{--vp-font-family-base: "Chinese Quotes", "Inter var", "Inter", ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Helvetica, Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";--vp-font-family-mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace}:root{--vp-shadow-1: 0 1px 2px rgba(0, 0, 0, .04), 0 1px 2px rgba(0, 0, 0, .06);--vp-shadow-2: 0 3px 12px rgba(0, 0, 0, .07), 0 1px 4px rgba(0, 0, 0, .07);--vp-shadow-3: 0 12px 32px rgba(0, 0, 0, .1), 0 2px 6px rgba(0, 0, 0, .08);--vp-shadow-4: 0 14px 44px rgba(0, 0, 0, .12), 0 3px 9px rgba(0, 0, 0, .12);--vp-shadow-5: 0 18px 56px rgba(0, 0, 0, .16), 0 4px 12px rgba(0, 0, 0, .16)}:root{--vp-z-index-footer: 10;--vp-z-index-local-nav: 20;--vp-z-index-nav: 30;--vp-z-index-layout-top: 40;--vp-z-index-backdrop: 50;--vp-z-index-sidebar: 60}:root{--vp-icon-copy: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' height='20' width='20' stroke='rgba(128,128,128,1)' stroke-width='2' viewBox='0 0 24 24'%3E%3Cpath stroke-linecap='round' stroke-linejoin='round' d='M9 5H7a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V7a2 2 0 0 0-2-2h-2M9 5a2 2 0 0 0 2 2h2a2 2 0 0 0 2-2M9 5a2 2 0 0 1 2-2h2a2 2 0 0 1 2 2'/%3E%3C/svg%3E");--vp-icon-copied: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' height='20' width='20' stroke='rgba(128,128,128,1)' stroke-width='2' viewBox='0 0 24 24'%3E%3Cpath stroke-linecap='round' stroke-linejoin='round' d='M9 5H7a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V7a2 2 0 0 0-2-2h-2M9 5a2 2 0 0 0 2 2h2a2 2 0 0 0 2-2M9 5a2 2 0 0 1 2-2h2a2 2 0 0 1 2 2m-6 9 2 2 4-4'/%3E%3C/svg%3E")}:root{--vp-layout-max-width: 1440px}:root{--vp-header-anchor-symbol: "#"}:root{--vp-code-line-height: 1.7;--vp-code-font-size: .875em;--vp-code-color: var(--vp-c-brand-1);--vp-code-link-color: var(--vp-c-brand-1);--vp-code-link-hover-color: var(--vp-c-brand-2);--vp-code-bg: var(--vp-c-default-soft);--vp-code-block-color: var(--vp-c-text-2);--vp-code-block-bg: var(--vp-c-bg-alt);--vp-code-block-divider-color: var(--vp-c-gutter);--vp-code-lang-color: var(--vp-c-text-3);--vp-code-line-highlight-color: var(--vp-c-default-soft);--vp-code-line-number-color: var(--vp-c-text-3);--vp-code-line-diff-add-color: var(--vp-c-green-soft);--vp-code-line-diff-add-symbol-color: var(--vp-c-green-1);--vp-code-line-diff-remove-color: var(--vp-c-red-soft);--vp-code-line-diff-remove-symbol-color: var(--vp-c-red-1);--vp-code-line-warning-color: var(--vp-c-yellow-soft);--vp-code-line-error-color: var(--vp-c-red-soft);--vp-code-copy-code-border-color: var(--vp-c-divider);--vp-code-copy-code-bg: var(--vp-c-bg-soft);--vp-code-copy-code-hover-border-color: var(--vp-c-divider);--vp-code-copy-code-hover-bg: var(--vp-c-bg);--vp-code-copy-code-active-text: var(--vp-c-text-2);--vp-code-copy-copied-text-content: "Copied";--vp-code-tab-divider: var(--vp-code-block-divider-color);--vp-code-tab-text-color: var(--vp-c-text-2);--vp-code-tab-bg: var(--vp-code-block-bg);--vp-code-tab-hover-text-color: var(--vp-c-text-1);--vp-code-tab-active-text-color: var(--vp-c-text-1);--vp-code-tab-active-bar-color: var(--vp-c-brand-1)}:root{--vp-button-brand-border: transparent;--vp-button-brand-text: var(--vp-c-white);--vp-button-brand-bg: var(--vp-c-brand-3);--vp-button-brand-hover-border: transparent;--vp-button-brand-hover-text: var(--vp-c-white);--vp-button-brand-hover-bg: var(--vp-c-brand-2);--vp-button-brand-active-border: transparent;--vp-button-brand-active-text: var(--vp-c-white);--vp-button-brand-active-bg: var(--vp-c-brand-1);--vp-button-alt-border: transparent;--vp-button-alt-text: var(--vp-c-text-1);--vp-button-alt-bg: var(--vp-c-default-3);--vp-button-alt-hover-border: transparent;--vp-button-alt-hover-text: var(--vp-c-text-1);--vp-button-alt-hover-bg: var(--vp-c-default-2);--vp-button-alt-active-border: transparent;--vp-button-alt-active-text: var(--vp-c-text-1);--vp-button-alt-active-bg: var(--vp-c-default-1);--vp-button-sponsor-border: var(--vp-c-text-2);--vp-button-sponsor-text: var(--vp-c-text-2);--vp-button-sponsor-bg: transparent;--vp-button-sponsor-hover-border: var(--vp-c-sponsor);--vp-button-sponsor-hover-text: var(--vp-c-sponsor);--vp-button-sponsor-hover-bg: transparent;--vp-button-sponsor-active-border: var(--vp-c-sponsor);--vp-button-sponsor-active-text: var(--vp-c-sponsor);--vp-button-sponsor-active-bg: transparent}:root{--vp-custom-block-font-size: 14px;--vp-custom-block-code-font-size: 13px;--vp-custom-block-info-border: transparent;--vp-custom-block-info-text: var(--vp-c-text-1);--vp-custom-block-info-bg: var(--vp-c-default-soft);--vp-custom-block-info-code-bg: var(--vp-c-default-soft);--vp-custom-block-tip-border: transparent;--vp-custom-block-tip-text: var(--vp-c-text-1);--vp-custom-block-tip-bg: var(--vp-c-brand-soft);--vp-custom-block-tip-code-bg: var(--vp-c-brand-soft);--vp-custom-block-warning-border: transparent;--vp-custom-block-warning-text: var(--vp-c-text-1);--vp-custom-block-warning-bg: var(--vp-c-warning-soft);--vp-custom-block-warning-code-bg: var(--vp-c-warning-soft);--vp-custom-block-danger-border: transparent;--vp-custom-block-danger-text: var(--vp-c-text-1);--vp-custom-block-danger-bg: var(--vp-c-danger-soft);--vp-custom-block-danger-code-bg: var(--vp-c-danger-soft);--vp-custom-block-details-border: var(--vp-custom-block-info-border);--vp-custom-block-details-text: var(--vp-custom-block-info-text);--vp-custom-block-details-bg: var(--vp-custom-block-info-bg);--vp-custom-block-details-code-bg: var(--vp-custom-block-info-code-bg)}:root{--vp-input-border-color: var(--vp-c-border);--vp-input-bg-color: var(--vp-c-bg-alt);--vp-input-switch-bg-color: var(--vp-c-gray-soft)}:root{--vp-nav-height: 64px;--vp-nav-bg-color: var(--vp-c-bg);--vp-nav-screen-bg-color: var(--vp-c-bg);--vp-nav-logo-height: 24px}.hide-nav{--vp-nav-height: 0px}.hide-nav .VPSidebar{--vp-nav-height: 22px}:root{--vp-local-nav-bg-color: var(--vp-c-bg)}:root{--vp-sidebar-width: 272px;--vp-sidebar-bg-color: var(--vp-c-bg-alt)}:root{--vp-backdrop-bg-color: rgba(0, 0, 0, .6)}:root{--vp-home-hero-name-color: var(--vp-c-brand-1);--vp-home-hero-name-background: transparent;--vp-home-hero-image-background-image: none;--vp-home-hero-image-filter: none}:root{--vp-badge-info-border: transparent;--vp-badge-info-text: var(--vp-c-text-2);--vp-badge-info-bg: var(--vp-c-default-soft);--vp-badge-tip-border: transparent;--vp-badge-tip-text: var(--vp-c-brand-1);--vp-badge-tip-bg: var(--vp-c-brand-soft);--vp-badge-warning-border: transparent;--vp-badge-warning-text: var(--vp-c-warning-1);--vp-badge-warning-bg: var(--vp-c-warning-soft);--vp-badge-danger-border: transparent;--vp-badge-danger-text: var(--vp-c-danger-1);--vp-badge-danger-bg: var(--vp-c-danger-soft)}:root{--vp-carbon-ads-text-color: var(--vp-c-text-1);--vp-carbon-ads-poweredby-color: var(--vp-c-text-2);--vp-carbon-ads-bg-color: var(--vp-c-bg-soft);--vp-carbon-ads-hover-text-color: var(--vp-c-brand-1);--vp-carbon-ads-hover-poweredby-color: var(--vp-c-text-1)}:root{--vp-local-search-bg: var(--vp-c-bg);--vp-local-search-result-bg: var(--vp-c-bg);--vp-local-search-result-border: var(--vp-c-divider);--vp-local-search-result-selected-bg: var(--vp-c-bg);--vp-local-search-result-selected-border: var(--vp-c-brand-1);--vp-local-search-highlight-bg: var(--vp-c-brand-1);--vp-local-search-highlight-text: var(--vp-c-neutral-inverse)}@media (prefers-reduced-motion: reduce){*,:before,:after{animation-delay:-1ms!important;animation-duration:1ms!important;animation-iteration-count:1!important;background-attachment:initial!important;scroll-behavior:auto!important;transition-duration:0s!important;transition-delay:0s!important}}*,:before,:after{box-sizing:border-box}html{line-height:1.4;font-size:16px;-webkit-text-size-adjust:100%}html.dark{color-scheme:dark}body{margin:0;width:100%;min-width:320px;min-height:100vh;line-height:24px;font-family:var(--vp-font-family-base);font-size:16px;font-weight:400;color:var(--vp-c-text-1);background-color:var(--vp-c-bg);direction:ltr;font-synthesis:style;text-rendering:optimizeLegibility;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}main{display:block}h1,h2,h3,h4,h5,h6{margin:0;line-height:24px;font-size:16px;font-weight:400}p{margin:0}strong,b{font-weight:600}a,area,button,[role=button],input,label,select,summary,textarea{touch-action:manipulation}a{color:inherit;text-decoration:inherit}ol,ul{list-style:none;margin:0;padding:0}blockquote{margin:0}pre,code,kbd,samp{font-family:var(--vp-font-family-mono)}img,svg,video,canvas,audio,iframe,embed,object{display:block}figure{margin:0}img,video{max-width:100%;height:auto}button,input,optgroup,select,textarea{border:0;padding:0;line-height:inherit;color:inherit}button{padding:0;font-family:inherit;background-color:transparent;background-image:none}button:enabled,[role=button]:enabled{cursor:pointer}button:focus,button:focus-visible{outline:1px dotted;outline:4px auto -webkit-focus-ring-color}button:focus:not(:focus-visible){outline:none!important}input:focus,textarea:focus,select:focus{outline:none}table{border-collapse:collapse}input{background-color:transparent}input:-ms-input-placeholder,textarea:-ms-input-placeholder{color:var(--vp-c-text-3)}input::-ms-input-placeholder,textarea::-ms-input-placeholder{color:var(--vp-c-text-3)}input::placeholder,textarea::placeholder{color:var(--vp-c-text-3)}input::-webkit-outer-spin-button,input::-webkit-inner-spin-button{-webkit-appearance:none;margin:0}input[type=number]{-moz-appearance:textfield}textarea{resize:vertical}select{-webkit-appearance:none}fieldset{margin:0;padding:0}h1,h2,h3,h4,h5,h6,li,p{overflow-wrap:break-word}vite-error-overlay{z-index:9999}mjx-container{display:inline-block;margin:auto 2px -2px}mjx-container>svg{margin:auto}.visually-hidden{position:absolute;width:1px;height:1px;white-space:nowrap;clip:rect(0 0 0 0);clip-path:inset(50%);overflow:hidden}.custom-block{border:1px solid transparent;border-radius:8px;padding:16px 16px 8px;line-height:24px;font-size:var(--vp-custom-block-font-size);color:var(--vp-c-text-2)}.custom-block.info{border-color:var(--vp-custom-block-info-border);color:var(--vp-custom-block-info-text);background-color:var(--vp-custom-block-info-bg)}.custom-block.info a,.custom-block.info code{color:var(--vp-c-brand-1)}.custom-block.info a:hover{color:var(--vp-c-brand-2)}.custom-block.info code{background-color:var(--vp-custom-block-info-code-bg)}.custom-block.tip{border-color:var(--vp-custom-block-tip-border);color:var(--vp-custom-block-tip-text);background-color:var(--vp-custom-block-tip-bg)}.custom-block.tip a,.custom-block.tip code{color:var(--vp-c-brand-1)}.custom-block.tip a:hover{color:var(--vp-c-brand-2)}.custom-block.tip code{background-color:var(--vp-custom-block-tip-code-bg)}.custom-block.warning{border-color:var(--vp-custom-block-warning-border);color:var(--vp-custom-block-warning-text);background-color:var(--vp-custom-block-warning-bg)}.custom-block.warning a,.custom-block.warning code{color:var(--vp-c-warning-1)}.custom-block.warning a:hover{color:var(--vp-c-warning-2)}.custom-block.warning code{background-color:var(--vp-custom-block-warning-code-bg)}.custom-block.danger{border-color:var(--vp-custom-block-danger-border);color:var(--vp-custom-block-danger-text);background-color:var(--vp-custom-block-danger-bg)}.custom-block.danger a,.custom-block.danger code{color:var(--vp-c-danger-1)}.custom-block.danger a:hover{color:var(--vp-c-danger-2)}.custom-block.danger code{background-color:var(--vp-custom-block-danger-code-bg)}.custom-block.details{border-color:var(--vp-custom-block-details-border);color:var(--vp-custom-block-details-text);background-color:var(--vp-custom-block-details-bg)}.custom-block.details a{color:var(--vp-c-brand-1)}.custom-block.details a:hover{color:var(--vp-c-brand-2)}.custom-block.details code{background-color:var(--vp-custom-block-details-code-bg)}.custom-block-title{font-weight:600}.custom-block p+p{margin:8px 0}.custom-block.details summary{margin:0 0 8px;font-weight:700;cursor:pointer}.custom-block.details summary+p{margin:8px 0}.custom-block a{color:inherit;font-weight:600;text-decoration:underline;text-underline-offset:2px;transition:opacity .25s}.custom-block a:hover{opacity:.75}.custom-block code{font-size:var(--vp-custom-block-code-font-size)}.custom-block.custom-block th,.custom-block.custom-block blockquote>p{font-size:var(--vp-custom-block-font-size);color:inherit}.dark .vp-code-light{display:none}html:not(.dark) .vp-code-dark{display:none}.vp-code-group{margin-top:16px}.vp-code-group .tabs{position:relative;display:flex;margin-right:-24px;margin-left:-24px;padding:0 12px;background-color:var(--vp-code-tab-bg);overflow-x:auto;overflow-y:hidden;box-shadow:inset 0 -1px var(--vp-code-tab-divider)}@media (min-width: 640px){.vp-code-group .tabs{margin-right:0;margin-left:0;border-radius:8px 8px 0 0}}.vp-code-group .tabs input{position:fixed;opacity:0;pointer-events:none}.vp-code-group .tabs label{position:relative;display:inline-block;border-bottom:1px solid transparent;padding:0 12px;line-height:48px;font-size:14px;font-weight:500;color:var(--vp-code-tab-text-color);white-space:nowrap;cursor:pointer;transition:color .25s}.vp-code-group .tabs label:after{position:absolute;right:8px;bottom:-1px;left:8px;z-index:1;height:2px;border-radius:2px;content:"";background-color:transparent;transition:background-color .25s}.vp-code-group label:hover{color:var(--vp-code-tab-hover-text-color)}.vp-code-group input:checked+label{color:var(--vp-code-tab-active-text-color)}.vp-code-group input:checked+label:after{background-color:var(--vp-code-tab-active-bar-color)}.vp-code-group div[class*=language-],.vp-block{display:none;margin-top:0!important;border-top-left-radius:0!important;border-top-right-radius:0!important}.vp-code-group div[class*=language-].active,.vp-block.active{display:block}.vp-block{padding:20px 24px}.vp-doc h1,.vp-doc h2,.vp-doc h3,.vp-doc h4,.vp-doc h5,.vp-doc h6{position:relative;font-weight:600;outline:none}.vp-doc h1{letter-spacing:-.02em;line-height:40px;font-size:28px}.vp-doc h2{margin:48px 0 16px;border-top:1px solid var(--vp-c-divider);padding-top:24px;letter-spacing:-.02em;line-height:32px;font-size:24px}.vp-doc h3{margin:32px 0 0;letter-spacing:-.01em;line-height:28px;font-size:20px}.vp-doc .header-anchor{position:absolute;top:0;left:0;margin-left:-.87em;font-weight:500;-webkit-user-select:none;user-select:none;opacity:0;text-decoration:none;transition:color .25s,opacity .25s}.vp-doc .header-anchor:before{content:var(--vp-header-anchor-symbol)}.vp-doc h1:hover .header-anchor,.vp-doc h1 .header-anchor:focus,.vp-doc h2:hover .header-anchor,.vp-doc h2 .header-anchor:focus,.vp-doc h3:hover .header-anchor,.vp-doc h3 .header-anchor:focus,.vp-doc h4:hover .header-anchor,.vp-doc h4 .header-anchor:focus,.vp-doc h5:hover .header-anchor,.vp-doc h5 .header-anchor:focus,.vp-doc h6:hover .header-anchor,.vp-doc h6 .header-anchor:focus{opacity:1}@media (min-width: 768px){.vp-doc h1{letter-spacing:-.02em;line-height:40px;font-size:32px}}.vp-doc h2 .header-anchor{top:24px}.vp-doc p,.vp-doc summary{margin:16px 0}.vp-doc p{line-height:28px}.vp-doc blockquote{margin:16px 0;border-left:2px solid var(--vp-c-divider);padding-left:16px;transition:border-color .5s}.vp-doc blockquote>p{margin:0;font-size:16px;color:var(--vp-c-text-2);transition:color .5s}.vp-doc a{font-weight:500;color:var(--vp-c-brand-1);text-decoration:underline;text-underline-offset:2px;transition:color .25s,opacity .25s}.vp-doc a:hover{color:var(--vp-c-brand-2)}.vp-doc strong{font-weight:600}.vp-doc ul,.vp-doc ol{padding-left:1.25rem;margin:16px 0}.vp-doc ul{list-style:disc}.vp-doc ol{list-style:decimal}.vp-doc li+li{margin-top:8px}.vp-doc li>ol,.vp-doc li>ul{margin:8px 0 0}.vp-doc table{display:block;border-collapse:collapse;margin:20px 0;overflow-x:auto}.vp-doc tr{background-color:var(--vp-c-bg);border-top:1px solid var(--vp-c-divider);transition:background-color .5s}.vp-doc tr:nth-child(2n){background-color:var(--vp-c-bg-soft)}.vp-doc th,.vp-doc td{border:1px solid var(--vp-c-divider);padding:8px 16px}.vp-doc th{text-align:left;font-size:14px;font-weight:600;color:var(--vp-c-text-2);background-color:var(--vp-c-bg-soft)}.vp-doc td{font-size:14px}.vp-doc hr{margin:16px 0;border:none;border-top:1px solid var(--vp-c-divider)}.vp-doc .custom-block{margin:16px 0}.vp-doc .custom-block p{margin:8px 0;line-height:24px}.vp-doc .custom-block p:first-child{margin:0}.vp-doc .custom-block div[class*=language-]{margin:8px 0;border-radius:8px}.vp-doc .custom-block div[class*=language-] code{font-weight:400;background-color:transparent}.vp-doc .custom-block .vp-code-group .tabs{margin:0;border-radius:8px 8px 0 0}.vp-doc :not(pre,h1,h2,h3,h4,h5,h6)>code{font-size:var(--vp-code-font-size);color:var(--vp-code-color)}.vp-doc :not(pre)>code{border-radius:4px;padding:3px 6px;background-color:var(--vp-code-bg);transition:color .25s,background-color .5s}.vp-doc a>code{color:var(--vp-code-link-color)}.vp-doc a:hover>code{color:var(--vp-code-link-hover-color)}.vp-doc h1>code,.vp-doc h2>code,.vp-doc h3>code{font-size:.9em}.vp-doc div[class*=language-],.vp-block{position:relative;margin:16px -24px;background-color:var(--vp-code-block-bg);overflow-x:auto;transition:background-color .5s}@media (min-width: 640px){.vp-doc div[class*=language-],.vp-block{border-radius:8px;margin:16px 0}}@media (max-width: 639px){.vp-doc li div[class*=language-]{border-radius:8px 0 0 8px}}.vp-doc div[class*=language-]+div[class*=language-],.vp-doc div[class$=-api]+div[class*=language-],.vp-doc div[class*=language-]+div[class$=-api]>div[class*=language-]{margin-top:-8px}.vp-doc [class*=language-] pre,.vp-doc [class*=language-] code{direction:ltr;text-align:left;white-space:pre;word-spacing:normal;word-break:normal;word-wrap:normal;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-hyphens:none;-moz-hyphens:none;-ms-hyphens:none;hyphens:none}.vp-doc [class*=language-] pre{position:relative;z-index:1;margin:0;padding:20px 0;background:transparent;overflow-x:auto}.vp-doc [class*=language-] code{display:block;padding:0 24px;width:fit-content;min-width:100%;line-height:var(--vp-code-line-height);font-size:var(--vp-code-font-size);color:var(--vp-code-block-color);transition:color .5s}.vp-doc [class*=language-] code .highlighted{background-color:var(--vp-code-line-highlight-color);transition:background-color .5s;margin:0 -24px;padding:0 24px;width:calc(100% + 48px);display:inline-block}.vp-doc [class*=language-] code .highlighted.error{background-color:var(--vp-code-line-error-color)}.vp-doc [class*=language-] code .highlighted.warning{background-color:var(--vp-code-line-warning-color)}.vp-doc [class*=language-] code .diff{transition:background-color .5s;margin:0 -24px;padding:0 24px;width:calc(100% + 48px);display:inline-block}.vp-doc [class*=language-] code .diff:before{position:absolute;left:10px}.vp-doc [class*=language-] .has-focused-lines .line:not(.has-focus){filter:blur(.095rem);opacity:.4;transition:filter .35s,opacity .35s}.vp-doc [class*=language-] .has-focused-lines .line:not(.has-focus){opacity:.7;transition:filter .35s,opacity .35s}.vp-doc [class*=language-]:hover .has-focused-lines .line:not(.has-focus){filter:blur(0);opacity:1}.vp-doc [class*=language-] code .diff.remove{background-color:var(--vp-code-line-diff-remove-color);opacity:.7}.vp-doc [class*=language-] code .diff.remove:before{content:"-";color:var(--vp-code-line-diff-remove-symbol-color)}.vp-doc [class*=language-] code .diff.add{background-color:var(--vp-code-line-diff-add-color)}.vp-doc [class*=language-] code .diff.add:before{content:"+";color:var(--vp-code-line-diff-add-symbol-color)}.vp-doc div[class*=language-].line-numbers-mode{padding-left:32px}.vp-doc .line-numbers-wrapper{position:absolute;top:0;bottom:0;left:0;z-index:3;border-right:1px solid var(--vp-code-block-divider-color);padding-top:20px;width:32px;text-align:center;font-family:var(--vp-font-family-mono);line-height:var(--vp-code-line-height);font-size:var(--vp-code-font-size);color:var(--vp-code-line-number-color);transition:border-color .5s,color .5s}.vp-doc [class*=language-]>button.copy{direction:ltr;position:absolute;top:12px;right:12px;z-index:3;border:1px solid var(--vp-code-copy-code-border-color);border-radius:4px;width:40px;height:40px;background-color:var(--vp-code-copy-code-bg);opacity:0;cursor:pointer;background-image:var(--vp-icon-copy);background-position:50%;background-size:20px;background-repeat:no-repeat;transition:border-color .25s,background-color .25s,opacity .25s}.vp-doc [class*=language-]:hover>button.copy,.vp-doc [class*=language-]>button.copy:focus{opacity:1}.vp-doc [class*=language-]>button.copy:hover,.vp-doc [class*=language-]>button.copy.copied{border-color:var(--vp-code-copy-code-hover-border-color);background-color:var(--vp-code-copy-code-hover-bg)}.vp-doc [class*=language-]>button.copy.copied,.vp-doc [class*=language-]>button.copy:hover.copied{border-radius:0 4px 4px 0;background-color:var(--vp-code-copy-code-hover-bg);background-image:var(--vp-icon-copied)}.vp-doc [class*=language-]>button.copy.copied:before,.vp-doc [class*=language-]>button.copy:hover.copied:before{position:relative;top:-1px;transform:translate(calc(-100% - 1px));display:flex;justify-content:center;align-items:center;border:1px solid var(--vp-code-copy-code-hover-border-color);border-right:0;border-radius:4px 0 0 4px;padding:0 10px;width:fit-content;height:40px;text-align:center;font-size:12px;font-weight:500;color:var(--vp-code-copy-code-active-text);background-color:var(--vp-code-copy-code-hover-bg);white-space:nowrap;content:var(--vp-code-copy-copied-text-content)}.vp-doc [class*=language-]>span.lang{position:absolute;top:2px;right:8px;z-index:2;font-size:12px;font-weight:500;color:var(--vp-code-lang-color);transition:color .4s,opacity .4s}.vp-doc [class*=language-]:hover>button.copy+span.lang,.vp-doc [class*=language-]>button.copy:focus+span.lang{opacity:0}.vp-doc .VPTeamMembers{margin-top:24px}.vp-doc .VPTeamMembers.small.count-1 .container{margin:0!important;max-width:calc((100% - 24px)/2)!important}.vp-doc .VPTeamMembers.small.count-2 .container,.vp-doc .VPTeamMembers.small.count-3 .container{max-width:100%!important}.vp-doc .VPTeamMembers.medium.count-1 .container{margin:0!important;max-width:calc((100% - 24px)/2)!important}:is(.vp-external-link-icon,.vp-doc a[href*="://"],.vp-doc a[target=_blank]):not(.no-icon):after{display:inline-block;margin-top:-1px;margin-left:4px;width:11px;height:11px;background:currentColor;color:var(--vp-c-text-3);flex-shrink:0;--icon: url("data:image/svg+xml, %3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' %3E%3Cpath d='M0 0h24v24H0V0z' fill='none' /%3E%3Cpath d='M9 5v2h6.59L4 18.59 5.41 20 17 8.41V15h2V5H9z' /%3E%3C/svg%3E");-webkit-mask-image:var(--icon);mask-image:var(--icon)}.vp-external-link-icon:after{content:""}.vp-sponsor{border-radius:16px;overflow:hidden}.vp-sponsor.aside{border-radius:12px}.vp-sponsor-section+.vp-sponsor-section{margin-top:4px}.vp-sponsor-tier{margin-bottom:4px;text-align:center;letter-spacing:1px;line-height:24px;width:100%;font-weight:600;color:var(--vp-c-text-2);background-color:var(--vp-c-bg-soft)}.vp-sponsor.normal .vp-sponsor-tier{padding:13px 0 11px;font-size:14px}.vp-sponsor.aside .vp-sponsor-tier{padding:9px 0 7px;font-size:12px}.vp-sponsor-grid+.vp-sponsor-tier{margin-top:4px}.vp-sponsor-grid{display:flex;flex-wrap:wrap;gap:4px}.vp-sponsor-grid.xmini .vp-sponsor-grid-link{height:64px}.vp-sponsor-grid.xmini .vp-sponsor-grid-image{max-width:64px;max-height:22px}.vp-sponsor-grid.mini .vp-sponsor-grid-link{height:72px}.vp-sponsor-grid.mini .vp-sponsor-grid-image{max-width:96px;max-height:24px}.vp-sponsor-grid.small .vp-sponsor-grid-link{height:96px}.vp-sponsor-grid.small .vp-sponsor-grid-image{max-width:96px;max-height:24px}.vp-sponsor-grid.medium .vp-sponsor-grid-link{height:112px}.vp-sponsor-grid.medium .vp-sponsor-grid-image{max-width:120px;max-height:36px}.vp-sponsor-grid.big .vp-sponsor-grid-link{height:184px}.vp-sponsor-grid.big .vp-sponsor-grid-image{max-width:192px;max-height:56px}.vp-sponsor-grid[data-vp-grid="2"] .vp-sponsor-grid-item{width:calc((100% - 4px)/2)}.vp-sponsor-grid[data-vp-grid="3"] .vp-sponsor-grid-item{width:calc((100% - 4px * 2) / 3)}.vp-sponsor-grid[data-vp-grid="4"] .vp-sponsor-grid-item{width:calc((100% - 12px)/4)}.vp-sponsor-grid[data-vp-grid="5"] .vp-sponsor-grid-item{width:calc((100% - 16px)/5)}.vp-sponsor-grid[data-vp-grid="6"] .vp-sponsor-grid-item{width:calc((100% - 4px * 5) / 6)}.vp-sponsor-grid-item{flex-shrink:0;width:100%;background-color:var(--vp-c-bg-soft);transition:background-color .25s}.vp-sponsor-grid-item:hover{background-color:var(--vp-c-default-soft)}.vp-sponsor-grid-item:hover .vp-sponsor-grid-image{filter:grayscale(0) invert(0)}.vp-sponsor-grid-item.empty:hover{background-color:var(--vp-c-bg-soft)}.dark .vp-sponsor-grid-item:hover{background-color:var(--vp-c-white)}.dark .vp-sponsor-grid-item.empty:hover{background-color:var(--vp-c-bg-soft)}.vp-sponsor-grid-link{display:flex}.vp-sponsor-grid-box{display:flex;justify-content:center;align-items:center;width:100%}.vp-sponsor-grid-image{max-width:100%;filter:grayscale(1);transition:filter .25s}.dark .vp-sponsor-grid-image{filter:grayscale(1) invert(1)}.VPBadge[data-v-9613cc9f]{display:inline-block;margin-left:2px;border:1px solid transparent;border-radius:12px;padding:0 10px;line-height:22px;font-size:12px;font-weight:500;transform:translateY(-2px)}.vp-doc h1>.VPBadge[data-v-9613cc9f]{margin-top:4px;vertical-align:top}.vp-doc h2>.VPBadge[data-v-9613cc9f]{margin-top:3px;padding:0 8px;vertical-align:top}.vp-doc h3>.VPBadge[data-v-9613cc9f]{vertical-align:middle}.vp-doc h4>.VPBadge[data-v-9613cc9f],.vp-doc h5>.VPBadge[data-v-9613cc9f],.vp-doc h6>.VPBadge[data-v-9613cc9f]{vertical-align:middle;line-height:18px}.VPBadge.info[data-v-9613cc9f]{border-color:var(--vp-badge-info-border);color:var(--vp-badge-info-text);background-color:var(--vp-badge-info-bg)}.VPBadge.tip[data-v-9613cc9f]{border-color:var(--vp-badge-tip-border);color:var(--vp-badge-tip-text);background-color:var(--vp-badge-tip-bg)}.VPBadge.warning[data-v-9613cc9f]{border-color:var(--vp-badge-warning-border);color:var(--vp-badge-warning-text);background-color:var(--vp-badge-warning-bg)}.VPBadge.danger[data-v-9613cc9f]{border-color:var(--vp-badge-danger-border);color:var(--vp-badge-danger-text);background-color:var(--vp-badge-danger-bg)}.VPBackdrop[data-v-c79a1216]{position:fixed;top:0;right:0;bottom:0;left:0;z-index:var(--vp-z-index-backdrop);background:var(--vp-backdrop-bg-color);transition:opacity .5s}.VPBackdrop.fade-enter-from[data-v-c79a1216],.VPBackdrop.fade-leave-to[data-v-c79a1216]{opacity:0}.VPBackdrop.fade-leave-active[data-v-c79a1216]{transition-duration:.25s}@media (min-width: 1280px){.VPBackdrop[data-v-c79a1216]{display:none}}.NotFound[data-v-f87ff6e4]{padding:64px 24px 96px;text-align:center}@media (min-width: 768px){.NotFound[data-v-f87ff6e4]{padding:96px 32px 168px}}.code[data-v-f87ff6e4]{line-height:64px;font-size:64px;font-weight:600}.title[data-v-f87ff6e4]{padding-top:12px;letter-spacing:2px;line-height:20px;font-size:20px;font-weight:700}.divider[data-v-f87ff6e4]{margin:24px auto 18px;width:64px;height:1px;background-color:var(--vp-c-divider)}.quote[data-v-f87ff6e4]{margin:0 auto;max-width:256px;font-size:14px;font-weight:500;color:var(--vp-c-text-2)}.action[data-v-f87ff6e4]{padding-top:20px}.link[data-v-f87ff6e4]{display:inline-block;border:1px solid var(--vp-c-brand-1);border-radius:16px;padding:3px 16px;font-size:14px;font-weight:500;color:var(--vp-c-brand-1);transition:border-color .25s,color .25s}.link[data-v-f87ff6e4]:hover{border-color:var(--vp-c-brand-2);color:var(--vp-c-brand-2)}.root[data-v-d0ee3533]{position:relative;z-index:1}.nested[data-v-d0ee3533]{padding-left:16px}.outline-link[data-v-d0ee3533]{display:block;line-height:28px;color:var(--vp-c-text-2);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;transition:color .5s;font-weight:400}.outline-link[data-v-d0ee3533]:hover,.outline-link.active[data-v-d0ee3533]{color:var(--vp-c-text-1);transition:color .25s}.outline-link.nested[data-v-d0ee3533]{padding-left:13px}.VPDocAsideOutline[data-v-d330b1bb]{display:none}.VPDocAsideOutline.has-outline[data-v-d330b1bb]{display:block}.content[data-v-d330b1bb]{position:relative;border-left:1px solid var(--vp-c-divider);padding-left:16px;font-size:13px;font-weight:500}.outline-marker[data-v-d330b1bb]{position:absolute;top:32px;left:-1px;z-index:0;opacity:0;width:2px;border-radius:2px;height:18px;background-color:var(--vp-c-brand-1);transition:top .25s cubic-bezier(0,1,.5,1),background-color .5s,opacity .25s}.outline-title[data-v-d330b1bb]{letter-spacing:.4px;line-height:28px;font-size:13px;font-weight:600}.VPDocAside[data-v-3f215769]{display:flex;flex-direction:column;flex-grow:1}.spacer[data-v-3f215769]{flex-grow:1}.VPDocAside[data-v-3f215769] .spacer+.VPDocAsideSponsors,.VPDocAside[data-v-3f215769] .spacer+.VPDocAsideCarbonAds{margin-top:24px}.VPDocAside[data-v-3f215769] .VPDocAsideSponsors+.VPDocAsideCarbonAds{margin-top:16px}.VPLastUpdated[data-v-7e05ebdb]{line-height:24px;font-size:14px;font-weight:500;color:var(--vp-c-text-2)}@media (min-width: 640px){.VPLastUpdated[data-v-7e05ebdb]{line-height:32px;font-size:14px;font-weight:500}}.VPDocFooter[data-v-ef5dee53]{margin-top:64px}.edit-info[data-v-ef5dee53]{padding-bottom:18px}@media (min-width: 640px){.edit-info[data-v-ef5dee53]{display:flex;justify-content:space-between;align-items:center;padding-bottom:14px}}.edit-link-button[data-v-ef5dee53]{display:flex;align-items:center;border:0;line-height:32px;font-size:14px;font-weight:500;color:var(--vp-c-brand-1);transition:color .25s}.edit-link-button[data-v-ef5dee53]:hover{color:var(--vp-c-brand-2)}.edit-link-icon[data-v-ef5dee53]{margin-right:8px;width:14px;height:14px;fill:currentColor}.prev-next[data-v-ef5dee53]{border-top:1px solid var(--vp-c-divider);padding-top:24px;display:grid;grid-row-gap:8px}@media (min-width: 640px){.prev-next[data-v-ef5dee53]{grid-template-columns:repeat(2,1fr);grid-column-gap:16px}}.pager-link[data-v-ef5dee53]{display:block;border:1px solid var(--vp-c-divider);border-radius:8px;padding:11px 16px 13px;width:100%;height:100%;transition:border-color .25s}.pager-link[data-v-ef5dee53]:hover{border-color:var(--vp-c-brand-1)}.pager-link.next[data-v-ef5dee53]{margin-left:auto;text-align:right}.desc[data-v-ef5dee53]{display:block;line-height:20px;font-size:12px;font-weight:500;color:var(--vp-c-text-2)}.title[data-v-ef5dee53]{display:block;line-height:20px;font-size:14px;font-weight:500;color:var(--vp-c-brand-1);transition:color .25s}.VPDocOutlineDropdown[data-v-eadfb36b]{margin-bottom:48px}.VPDocOutlineDropdown button[data-v-eadfb36b]{display:block;font-size:14px;font-weight:500;line-height:24px;border:1px solid var(--vp-c-border);padding:4px 12px;color:var(--vp-c-text-2);background-color:var(--vp-c-default-soft);border-radius:8px;transition:color .5s}.VPDocOutlineDropdown button[data-v-eadfb36b]:hover{color:var(--vp-c-text-1);transition:color .25s}.VPDocOutlineDropdown button.open[data-v-eadfb36b]{color:var(--vp-c-text-1)}.icon[data-v-eadfb36b]{display:inline-block;vertical-align:middle;width:16px;height:16px;fill:currentColor}[data-v-eadfb36b] .outline-link{font-size:14px;font-weight:400}.open>.icon[data-v-eadfb36b]{transform:rotate(90deg)}.items[data-v-eadfb36b]{margin-top:12px;border-left:1px solid var(--vp-c-divider)}.VPDoc[data-v-6b87e69f]{padding:32px 24px 96px;width:100%}.VPDoc .VPDocOutlineDropdown[data-v-6b87e69f]{display:none}@media (min-width: 960px) and (max-width: 1279px){.VPDoc .VPDocOutlineDropdown[data-v-6b87e69f]{display:block}}@media (min-width: 768px){.VPDoc[data-v-6b87e69f]{padding:48px 32px 128px}}@media (min-width: 960px){.VPDoc[data-v-6b87e69f]{padding:32px 32px 0}.VPDoc:not(.has-sidebar) .container[data-v-6b87e69f]{display:flex;justify-content:center;max-width:992px}.VPDoc:not(.has-sidebar) .content[data-v-6b87e69f]{max-width:752px}}@media (min-width: 1280px){.VPDoc .container[data-v-6b87e69f]{display:flex;justify-content:center}.VPDoc .aside[data-v-6b87e69f]{display:block}}@media (min-width: 1440px){.VPDoc:not(.has-sidebar) .content[data-v-6b87e69f]{max-width:784px}.VPDoc:not(.has-sidebar) .container[data-v-6b87e69f]{max-width:1104px}}.container[data-v-6b87e69f]{margin:0 auto;width:100%}.aside[data-v-6b87e69f]{position:relative;display:none;order:2;flex-grow:1;padding-left:32px;width:100%;max-width:256px}.left-aside[data-v-6b87e69f]{order:1;padding-left:unset;padding-right:32px}.aside-container[data-v-6b87e69f]{position:fixed;top:0;padding-top:calc(var(--vp-nav-height) + var(--vp-layout-top-height, 0px) + var(--vp-doc-top-height, 0px) + 32px);width:224px;height:100vh;overflow-x:hidden;overflow-y:auto;scrollbar-width:none}.aside-container[data-v-6b87e69f]::-webkit-scrollbar{display:none}.aside-curtain[data-v-6b87e69f]{position:fixed;bottom:0;z-index:10;width:224px;height:32px;background:linear-gradient(transparent,var(--vp-c-bg) 70%)}.aside-content[data-v-6b87e69f]{display:flex;flex-direction:column;min-height:calc(100vh - (var(--vp-nav-height) + var(--vp-layout-top-height, 0px) + 32px));padding-bottom:32px}.content[data-v-6b87e69f]{position:relative;margin:0 auto;width:100%}@media (min-width: 960px){.content[data-v-6b87e69f]{padding:0 32px 128px}}@media (min-width: 1280px){.content[data-v-6b87e69f]{order:1;margin:0;min-width:640px}}.content-container[data-v-6b87e69f]{margin:0 auto}.VPDoc.has-aside .content-container[data-v-6b87e69f]{max-width:688px}.external-link-icon-enabled[data-v-6b87e69f] :is(.vp-doc a[href*="://"],.vp-doc a[target=_blank]):after{content:"";color:currentColor}.VPButton[data-v-c1c5efc1]{display:inline-block;border:1px solid transparent;text-align:center;font-weight:600;white-space:nowrap;transition:color .25s,border-color .25s,background-color .25s}.VPButton[data-v-c1c5efc1]:active{transition:color .1s,border-color .1s,background-color .1s}.VPButton.medium[data-v-c1c5efc1]{border-radius:20px;padding:0 20px;line-height:38px;font-size:14px}.VPButton.big[data-v-c1c5efc1]{border-radius:24px;padding:0 24px;line-height:46px;font-size:16px}.VPButton.brand[data-v-c1c5efc1]{border-color:var(--vp-button-brand-border);color:var(--vp-button-brand-text);background-color:var(--vp-button-brand-bg)}.VPButton.brand[data-v-c1c5efc1]:hover{border-color:var(--vp-button-brand-hover-border);color:var(--vp-button-brand-hover-text);background-color:var(--vp-button-brand-hover-bg)}.VPButton.brand[data-v-c1c5efc1]:active{border-color:var(--vp-button-brand-active-border);color:var(--vp-button-brand-active-text);background-color:var(--vp-button-brand-active-bg)}.VPButton.alt[data-v-c1c5efc1]{border-color:var(--vp-button-alt-border);color:var(--vp-button-alt-text);background-color:var(--vp-button-alt-bg)}.VPButton.alt[data-v-c1c5efc1]:hover{border-color:var(--vp-button-alt-hover-border);color:var(--vp-button-alt-hover-text);background-color:var(--vp-button-alt-hover-bg)}.VPButton.alt[data-v-c1c5efc1]:active{border-color:var(--vp-button-alt-active-border);color:var(--vp-button-alt-active-text);background-color:var(--vp-button-alt-active-bg)}.VPButton.sponsor[data-v-c1c5efc1]{border-color:var(--vp-button-sponsor-border);color:var(--vp-button-sponsor-text);background-color:var(--vp-button-sponsor-bg)}.VPButton.sponsor[data-v-c1c5efc1]:hover{border-color:var(--vp-button-sponsor-hover-border);color:var(--vp-button-sponsor-hover-text);background-color:var(--vp-button-sponsor-hover-bg)}.VPButton.sponsor[data-v-c1c5efc1]:active{border-color:var(--vp-button-sponsor-active-border);color:var(--vp-button-sponsor-active-text);background-color:var(--vp-button-sponsor-active-bg)}html:not(.dark) .VPImage.dark[data-v-8426fc1a]{display:none}.dark .VPImage.light[data-v-8426fc1a]{display:none}.VPHero[data-v-da5d1713]{margin-top:calc((var(--vp-nav-height) + var(--vp-layout-top-height, 0px)) * -1);padding:calc(var(--vp-nav-height) + var(--vp-layout-top-height, 0px) + 48px) 24px 48px}@media (min-width: 640px){.VPHero[data-v-da5d1713]{padding:calc(var(--vp-nav-height) + var(--vp-layout-top-height, 0px) + 80px) 48px 64px}}@media (min-width: 960px){.VPHero[data-v-da5d1713]{padding:calc(var(--vp-nav-height) + var(--vp-layout-top-height, 0px) + 80px) 64px 64px}}.container[data-v-da5d1713]{display:flex;flex-direction:column;margin:0 auto;max-width:1152px}@media (min-width: 960px){.container[data-v-da5d1713]{flex-direction:row}}.main[data-v-da5d1713]{position:relative;z-index:10;order:2;flex-grow:1;flex-shrink:0}.VPHero.has-image .container[data-v-da5d1713]{text-align:center}@media (min-width: 960px){.VPHero.has-image .container[data-v-da5d1713]{text-align:left}}@media (min-width: 960px){.main[data-v-da5d1713]{order:1;width:calc((100% / 3) * 2)}.VPHero.has-image .main[data-v-da5d1713]{max-width:592px}}.name[data-v-da5d1713],.text[data-v-da5d1713]{max-width:392px;letter-spacing:-.4px;line-height:40px;font-size:32px;font-weight:700;white-space:pre-wrap}.VPHero.has-image .name[data-v-da5d1713],.VPHero.has-image .text[data-v-da5d1713]{margin:0 auto}.name[data-v-da5d1713]{color:var(--vp-home-hero-name-color)}.clip[data-v-da5d1713]{background:var(--vp-home-hero-name-background);-webkit-background-clip:text;background-clip:text;-webkit-text-fill-color:var(--vp-home-hero-name-color)}@media (min-width: 640px){.name[data-v-da5d1713],.text[data-v-da5d1713]{max-width:576px;line-height:56px;font-size:48px}}@media (min-width: 960px){.name[data-v-da5d1713],.text[data-v-da5d1713]{line-height:64px;font-size:56px}.VPHero.has-image .name[data-v-da5d1713],.VPHero.has-image .text[data-v-da5d1713]{margin:0}}.tagline[data-v-da5d1713]{padding-top:8px;max-width:392px;line-height:28px;font-size:18px;font-weight:500;white-space:pre-wrap;color:var(--vp-c-text-2)}.VPHero.has-image .tagline[data-v-da5d1713]{margin:0 auto}@media (min-width: 640px){.tagline[data-v-da5d1713]{padding-top:12px;max-width:576px;line-height:32px;font-size:20px}}@media (min-width: 960px){.tagline[data-v-da5d1713]{line-height:36px;font-size:24px}.VPHero.has-image .tagline[data-v-da5d1713]{margin:0}}.actions[data-v-da5d1713]{display:flex;flex-wrap:wrap;margin:-6px;padding-top:24px}.VPHero.has-image .actions[data-v-da5d1713]{justify-content:center}@media (min-width: 640px){.actions[data-v-da5d1713]{padding-top:32px}}@media (min-width: 960px){.VPHero.has-image .actions[data-v-da5d1713]{justify-content:flex-start}}.action[data-v-da5d1713]{flex-shrink:0;padding:6px}.image[data-v-da5d1713]{order:1;margin:-76px -24px -48px}@media (min-width: 640px){.image[data-v-da5d1713]{margin:-108px -24px -48px}}@media (min-width: 960px){.image[data-v-da5d1713]{flex-grow:1;order:2;margin:0;min-height:100%}}.image-container[data-v-da5d1713]{position:relative;margin:0 auto;width:320px;height:320px}@media (min-width: 640px){.image-container[data-v-da5d1713]{width:392px;height:392px}}@media (min-width: 960px){.image-container[data-v-da5d1713]{display:flex;justify-content:center;align-items:center;width:100%;height:100%;transform:translate(-32px,-32px)}}.image-bg[data-v-da5d1713]{position:absolute;top:50%;left:50%;border-radius:50%;width:192px;height:192px;background-image:var(--vp-home-hero-image-background-image);filter:var(--vp-home-hero-image-filter);transform:translate(-50%,-50%)}@media (min-width: 640px){.image-bg[data-v-da5d1713]{width:256px;height:256px}}@media (min-width: 960px){.image-bg[data-v-da5d1713]{width:320px;height:320px}}[data-v-da5d1713] .image-src{position:absolute;top:50%;left:50%;max-width:192px;max-height:192px;transform:translate(-50%,-50%)}@media (min-width: 640px){[data-v-da5d1713] .image-src{max-width:256px;max-height:256px}}@media (min-width: 960px){[data-v-da5d1713] .image-src{max-width:320px;max-height:320px}}.VPFeature[data-v-33204567]{display:block;border:1px solid var(--vp-c-bg-soft);border-radius:12px;height:100%;background-color:var(--vp-c-bg-soft);transition:border-color .25s,background-color .25s}.VPFeature.link[data-v-33204567]:hover{border-color:var(--vp-c-brand-1)}.box[data-v-33204567]{display:flex;flex-direction:column;padding:24px;height:100%}.box[data-v-33204567]>.VPImage{margin-bottom:20px}.icon[data-v-33204567]{display:flex;justify-content:center;align-items:center;margin-bottom:20px;border-radius:6px;background-color:var(--vp-c-default-soft);width:48px;height:48px;font-size:24px;transition:background-color .25s}.title[data-v-33204567]{line-height:24px;font-size:16px;font-weight:600}.details[data-v-33204567]{flex-grow:1;padding-top:8px;line-height:24px;font-size:14px;font-weight:500;color:var(--vp-c-text-2)}.link-text[data-v-33204567]{padding-top:8px}.link-text-value[data-v-33204567]{display:flex;align-items:center;font-size:14px;font-weight:500;color:var(--vp-c-brand-1)}.link-text-icon[data-v-33204567]{display:inline-block;margin-left:6px;width:14px;height:14px;fill:currentColor}.VPFeatures[data-v-a6181336]{position:relative;padding:0 24px}@media (min-width: 640px){.VPFeatures[data-v-a6181336]{padding:0 48px}}@media (min-width: 960px){.VPFeatures[data-v-a6181336]{padding:0 64px}}.container[data-v-a6181336]{margin:0 auto;max-width:1152px}.items[data-v-a6181336]{display:flex;flex-wrap:wrap;margin:-8px}.item[data-v-a6181336]{padding:8px;width:100%}@media (min-width: 640px){.item.grid-2[data-v-a6181336],.item.grid-4[data-v-a6181336],.item.grid-6[data-v-a6181336]{width:50%}}@media (min-width: 768px){.item.grid-2[data-v-a6181336],.item.grid-4[data-v-a6181336]{width:50%}.item.grid-3[data-v-a6181336],.item.grid-6[data-v-a6181336]{width:calc(100% / 3)}}@media (min-width: 960px){.item.grid-4[data-v-a6181336]{width:25%}}.VPHome[data-v-d82743a8]{padding-bottom:96px}.VPHome[data-v-d82743a8] .VPHomeSponsors{margin-top:112px;margin-bottom:-128px}@media (min-width: 768px){.VPHome[data-v-d82743a8]{padding-bottom:128px}}.VPContent[data-v-669faec9]{flex-grow:1;flex-shrink:0;margin:var(--vp-layout-top-height, 0px) auto 0;width:100%}.VPContent.is-home[data-v-669faec9]{width:100%;max-width:100%}.VPContent.has-sidebar[data-v-669faec9]{margin:0}@media (min-width: 960px){.VPContent[data-v-669faec9]{padding-top:var(--vp-nav-height)}.VPContent.has-sidebar[data-v-669faec9]{margin:var(--vp-layout-top-height, 0px) 0 0;padding-left:var(--vp-sidebar-width)}}@media (min-width: 1440px){.VPContent.has-sidebar[data-v-669faec9]{padding-right:calc((100vw - var(--vp-layout-max-width)) / 2);padding-left:calc((100vw - var(--vp-layout-max-width)) / 2 + var(--vp-sidebar-width))}}.VPFooter[data-v-e03eb2e1]{position:relative;z-index:var(--vp-z-index-footer);border-top:1px solid var(--vp-c-gutter);padding:32px 24px;background-color:var(--vp-c-bg)}.VPFooter.has-sidebar[data-v-e03eb2e1]{display:none}@media (min-width: 768px){.VPFooter[data-v-e03eb2e1]{padding:32px}}.container[data-v-e03eb2e1]{margin:0 auto;max-width:var(--vp-layout-max-width);text-align:center}.message[data-v-e03eb2e1],.copyright[data-v-e03eb2e1]{line-height:24px;font-size:14px;font-weight:500;color:var(--vp-c-text-2)}.VPLocalNavOutlineDropdown[data-v-1c15a60a]{padding:12px 20px 11px}.VPLocalNavOutlineDropdown button[data-v-1c15a60a]{display:block;font-size:12px;font-weight:500;line-height:24px;color:var(--vp-c-text-2);transition:color .5s;position:relative}.VPLocalNavOutlineDropdown button[data-v-1c15a60a]:hover{color:var(--vp-c-text-1);transition:color .25s}.VPLocalNavOutlineDropdown button.open[data-v-1c15a60a]{color:var(--vp-c-text-1)}.icon[data-v-1c15a60a]{display:inline-block;vertical-align:middle;margin-left:2px;width:14px;height:14px;fill:currentColor}[data-v-1c15a60a] .outline-link{font-size:14px;padding:2px 0}.open>.icon[data-v-1c15a60a]{transform:rotate(90deg)}.items[data-v-1c15a60a]{position:absolute;top:64px;right:16px;left:16px;display:grid;gap:1px;border:1px solid var(--vp-c-border);border-radius:8px;background-color:var(--vp-c-gutter);max-height:calc(var(--vp-vh, 100vh) - 86px);overflow:hidden auto;box-shadow:var(--vp-shadow-3)}.header[data-v-1c15a60a]{background-color:var(--vp-c-bg-soft)}.top-link[data-v-1c15a60a]{display:block;padding:0 16px;line-height:48px;font-size:14px;font-weight:500;color:var(--vp-c-brand-1)}.outline[data-v-1c15a60a]{padding:8px 0;background-color:var(--vp-c-bg-soft)}.flyout-enter-active[data-v-1c15a60a]{transition:all .2s ease-out}.flyout-leave-active[data-v-1c15a60a]{transition:all .15s ease-in}.flyout-enter-from[data-v-1c15a60a],.flyout-leave-to[data-v-1c15a60a]{opacity:0;transform:translateY(-16px)}.VPLocalNav[data-v-79c8c1df]{position:sticky;top:0;left:0;z-index:var(--vp-z-index-local-nav);display:flex;justify-content:space-between;align-items:center;border-top:1px solid var(--vp-c-gutter);border-bottom:1px solid var(--vp-c-gutter);padding-top:var(--vp-layout-top-height, 0px);width:100%;background-color:var(--vp-local-nav-bg-color)}.VPLocalNav.fixed[data-v-79c8c1df]{position:fixed}.VPLocalNav.reached-top[data-v-79c8c1df]{border-top-color:transparent}@media (min-width: 960px){.VPLocalNav[data-v-79c8c1df]{display:none}}.menu[data-v-79c8c1df]{display:flex;align-items:center;padding:12px 24px 11px;line-height:24px;font-size:12px;font-weight:500;color:var(--vp-c-text-2);transition:color .5s}.menu[data-v-79c8c1df]:hover{color:var(--vp-c-text-1);transition:color .25s}@media (min-width: 768px){.menu[data-v-79c8c1df]{padding:0 32px}}.menu-icon[data-v-79c8c1df]{margin-right:8px;width:16px;height:16px;fill:currentColor}.VPOutlineDropdown[data-v-79c8c1df]{padding:12px 24px 11px}@media (min-width: 768px){.VPOutlineDropdown[data-v-79c8c1df]{padding:12px 32px 11px}}.VPSwitch[data-v-b1685198]{position:relative;border-radius:11px;display:block;width:40px;height:22px;flex-shrink:0;border:1px solid var(--vp-input-border-color);background-color:var(--vp-input-switch-bg-color);transition:border-color .25s!important}.VPSwitch[data-v-b1685198]:hover{border-color:var(--vp-c-brand-1)}.check[data-v-b1685198]{position:absolute;top:1px;left:1px;width:18px;height:18px;border-radius:50%;background-color:var(--vp-c-neutral-inverse);box-shadow:var(--vp-shadow-1);transition:transform .25s!important}.icon[data-v-b1685198]{position:relative;display:block;width:18px;height:18px;border-radius:50%;overflow:hidden}.icon[data-v-b1685198] svg{position:absolute;top:3px;left:3px;width:12px;height:12px;fill:var(--vp-c-text-2)}.dark .icon[data-v-b1685198] svg{fill:var(--vp-c-text-1);transition:opacity .25s!important}.sun[data-v-ce54a7d1]{opacity:1}.moon[data-v-ce54a7d1],.dark .sun[data-v-ce54a7d1]{opacity:0}.dark .moon[data-v-ce54a7d1]{opacity:1}.dark .VPSwitchAppearance[data-v-ce54a7d1] .check{transform:translate(18px)}.VPNavBarAppearance[data-v-e6aabb21]{display:none}@media (min-width: 1280px){.VPNavBarAppearance[data-v-e6aabb21]{display:flex;align-items:center}}.VPMenuGroup+.VPMenuLink[data-v-43f1e123]{margin:12px -12px 0;border-top:1px solid var(--vp-c-divider);padding:12px 12px 0}.link[data-v-43f1e123]{display:block;border-radius:6px;padding:0 12px;line-height:32px;font-size:14px;font-weight:500;color:var(--vp-c-text-1);white-space:nowrap;transition:background-color .25s,color .25s}.link[data-v-43f1e123]:hover{color:var(--vp-c-brand-1);background-color:var(--vp-c-default-soft)}.link.active[data-v-43f1e123]{color:var(--vp-c-brand-1)}.VPMenuGroup[data-v-69e747b5]{margin:12px -12px 0;border-top:1px solid var(--vp-c-divider);padding:12px 12px 0}.VPMenuGroup[data-v-69e747b5]:first-child{margin-top:0;border-top:0;padding-top:0}.VPMenuGroup+.VPMenuGroup[data-v-69e747b5]{margin-top:12px;border-top:1px solid var(--vp-c-divider)}.title[data-v-69e747b5]{padding:0 12px;line-height:32px;font-size:14px;font-weight:600;color:var(--vp-c-text-2);white-space:nowrap;transition:color .25s}.VPMenu[data-v-e7ea1737]{border-radius:12px;padding:12px;min-width:128px;border:1px solid var(--vp-c-divider);background-color:var(--vp-c-bg-elv);box-shadow:var(--vp-shadow-3);transition:background-color .5s;max-height:calc(100vh - var(--vp-nav-height));overflow-y:auto}.VPMenu[data-v-e7ea1737] .group{margin:0 -12px;padding:0 12px 12px}.VPMenu[data-v-e7ea1737] .group+.group{border-top:1px solid var(--vp-c-divider);padding:11px 12px 12px}.VPMenu[data-v-e7ea1737] .group:last-child{padding-bottom:0}.VPMenu[data-v-e7ea1737] .group+.item{border-top:1px solid var(--vp-c-divider);padding:11px 16px 0}.VPMenu[data-v-e7ea1737] .item{padding:0 16px;white-space:nowrap}.VPMenu[data-v-e7ea1737] .label{flex-grow:1;line-height:28px;font-size:12px;font-weight:500;color:var(--vp-c-text-2);transition:color .5s}.VPMenu[data-v-e7ea1737] .action{padding-left:24px}.VPFlyout[data-v-9c007e85]{position:relative}.VPFlyout[data-v-9c007e85]:hover{color:var(--vp-c-brand-1);transition:color .25s}.VPFlyout:hover .text[data-v-9c007e85]{color:var(--vp-c-text-2)}.VPFlyout:hover .icon[data-v-9c007e85]{fill:var(--vp-c-text-2)}.VPFlyout.active .text[data-v-9c007e85]{color:var(--vp-c-brand-1)}.VPFlyout.active:hover .text[data-v-9c007e85]{color:var(--vp-c-brand-2)}.VPFlyout:hover .menu[data-v-9c007e85],.button[aria-expanded=true]+.menu[data-v-9c007e85]{opacity:1;visibility:visible;transform:translateY(0)}.button[aria-expanded=false]+.menu[data-v-9c007e85]{opacity:0;visibility:hidden;transform:translateY(0)}.button[data-v-9c007e85]{display:flex;align-items:center;padding:0 12px;height:var(--vp-nav-height);color:var(--vp-c-text-1);transition:color .5s}.text[data-v-9c007e85]{display:flex;align-items:center;line-height:var(--vp-nav-height);font-size:14px;font-weight:500;color:var(--vp-c-text-1);transition:color .25s}.option-icon[data-v-9c007e85]{margin-right:0;width:16px;height:16px;fill:currentColor}.text-icon[data-v-9c007e85]{margin-left:4px;width:14px;height:14px;fill:currentColor}.icon[data-v-9c007e85]{width:20px;height:20px;fill:currentColor;transition:fill .25s}.menu[data-v-9c007e85]{position:absolute;top:calc(var(--vp-nav-height) / 2 + 20px);right:0;opacity:0;visibility:hidden;transition:opacity .25s,visibility .25s,transform .25s}.VPSocialLink[data-v-f80f8133]{display:flex;justify-content:center;align-items:center;width:36px;height:36px;color:var(--vp-c-text-2);transition:color .5s}.VPSocialLink[data-v-f80f8133]:hover{color:var(--vp-c-text-1);transition:color .25s}.VPSocialLink[data-v-f80f8133]>svg{width:20px;height:20px;fill:currentColor}.VPSocialLinks[data-v-7bc22406]{display:flex;justify-content:center}.VPNavBarExtra[data-v-d0bd9dde]{display:none;margin-right:-12px}@media (min-width: 768px){.VPNavBarExtra[data-v-d0bd9dde]{display:block}}@media (min-width: 1280px){.VPNavBarExtra[data-v-d0bd9dde]{display:none}}.trans-title[data-v-d0bd9dde]{padding:0 24px 0 12px;line-height:32px;font-size:14px;font-weight:700;color:var(--vp-c-text-1)}.item.appearance[data-v-d0bd9dde],.item.social-links[data-v-d0bd9dde]{display:flex;align-items:center;padding:0 12px}.item.appearance[data-v-d0bd9dde]{min-width:176px}.appearance-action[data-v-d0bd9dde]{margin-right:-2px}.social-links-list[data-v-d0bd9dde]{margin:-4px -8px}.VPNavBarHamburger[data-v-e5dd9c1c]{display:flex;justify-content:center;align-items:center;width:48px;height:var(--vp-nav-height)}@media (min-width: 768px){.VPNavBarHamburger[data-v-e5dd9c1c]{display:none}}.container[data-v-e5dd9c1c]{position:relative;width:16px;height:14px;overflow:hidden}.VPNavBarHamburger:hover .top[data-v-e5dd9c1c]{top:0;left:0;transform:translate(4px)}.VPNavBarHamburger:hover .middle[data-v-e5dd9c1c]{top:6px;left:0;transform:translate(0)}.VPNavBarHamburger:hover .bottom[data-v-e5dd9c1c]{top:12px;left:0;transform:translate(8px)}.VPNavBarHamburger.active .top[data-v-e5dd9c1c]{top:6px;transform:translate(0) rotate(225deg)}.VPNavBarHamburger.active .middle[data-v-e5dd9c1c]{top:6px;transform:translate(16px)}.VPNavBarHamburger.active .bottom[data-v-e5dd9c1c]{top:6px;transform:translate(0) rotate(135deg)}.VPNavBarHamburger.active:hover .top[data-v-e5dd9c1c],.VPNavBarHamburger.active:hover .middle[data-v-e5dd9c1c],.VPNavBarHamburger.active:hover .bottom[data-v-e5dd9c1c]{background-color:var(--vp-c-text-2);transition:top .25s,background-color .25s,transform .25s}.top[data-v-e5dd9c1c],.middle[data-v-e5dd9c1c],.bottom[data-v-e5dd9c1c]{position:absolute;width:16px;height:2px;background-color:var(--vp-c-text-1);transition:top .25s,background-color .5s,transform .25s}.top[data-v-e5dd9c1c]{top:0;left:0;transform:translate(0)}.middle[data-v-e5dd9c1c]{top:6px;left:0;transform:translate(8px)}.bottom[data-v-e5dd9c1c]{top:12px;left:0;transform:translate(4px)}.VPNavBarMenuLink[data-v-42ef59de]{display:flex;align-items:center;padding:0 12px;line-height:var(--vp-nav-height);font-size:14px;font-weight:500;color:var(--vp-c-text-1);transition:color .25s}.VPNavBarMenuLink.active[data-v-42ef59de],.VPNavBarMenuLink[data-v-42ef59de]:hover{color:var(--vp-c-brand-1)}.VPNavBarMenu[data-v-7f418b0f]{display:none}@media (min-width: 768px){.VPNavBarMenu[data-v-7f418b0f]{display:flex}}/*! @docsearch/css 3.5.2 | MIT License | © Algolia, Inc. and contributors | https://docsearch.algolia.com */:root{--docsearch-primary-color:#5468ff;--docsearch-text-color:#1c1e21;--docsearch-spacing:12px;--docsearch-icon-stroke-width:1.4;--docsearch-highlight-color:var(--docsearch-primary-color);--docsearch-muted-color:#969faf;--docsearch-container-background:rgba(101,108,133,.8);--docsearch-logo-color:#5468ff;--docsearch-modal-width:560px;--docsearch-modal-height:600px;--docsearch-modal-background:#f5f6f7;--docsearch-modal-shadow:inset 1px 1px 0 0 hsla(0,0%,100%,.5),0 3px 8px 0 #555a64;--docsearch-searchbox-height:56px;--docsearch-searchbox-background:#ebedf0;--docsearch-searchbox-focus-background:#fff;--docsearch-searchbox-shadow:inset 0 0 0 2px var(--docsearch-primary-color);--docsearch-hit-height:56px;--docsearch-hit-color:#444950;--docsearch-hit-active-color:#fff;--docsearch-hit-background:#fff;--docsearch-hit-shadow:0 1px 3px 0 #d4d9e1;--docsearch-key-gradient:linear-gradient(-225deg,#d5dbe4,#f8f8f8);--docsearch-key-shadow:inset 0 -2px 0 0 #cdcde6,inset 0 0 1px 1px #fff,0 1px 2px 1px rgba(30,35,90,.4);--docsearch-footer-height:44px;--docsearch-footer-background:#fff;--docsearch-footer-shadow:0 -1px 0 0 #e0e3e8,0 -3px 6px 0 rgba(69,98,155,.12)}html[data-theme=dark]{--docsearch-text-color:#f5f6f7;--docsearch-container-background:rgba(9,10,17,.8);--docsearch-modal-background:#15172a;--docsearch-modal-shadow:inset 1px 1px 0 0 #2c2e40,0 3px 8px 0 #000309;--docsearch-searchbox-background:#090a11;--docsearch-searchbox-focus-background:#000;--docsearch-hit-color:#bec3c9;--docsearch-hit-shadow:none;--docsearch-hit-background:#090a11;--docsearch-key-gradient:linear-gradient(-26.5deg,#565872,#31355b);--docsearch-key-shadow:inset 0 -2px 0 0 #282d55,inset 0 0 1px 1px #51577d,0 2px 2px 0 rgba(3,4,9,.3);--docsearch-footer-background:#1e2136;--docsearch-footer-shadow:inset 0 1px 0 0 rgba(73,76,106,.5),0 -4px 8px 0 rgba(0,0,0,.2);--docsearch-logo-color:#fff;--docsearch-muted-color:#7f8497}.DocSearch-Button{align-items:center;background:var(--docsearch-searchbox-background);border:0;border-radius:40px;color:var(--docsearch-muted-color);cursor:pointer;display:flex;font-weight:500;height:36px;justify-content:space-between;margin:0 0 0 16px;padding:0 8px;-webkit-user-select:none;user-select:none}.DocSearch-Button:active,.DocSearch-Button:focus,.DocSearch-Button:hover{background:var(--docsearch-searchbox-focus-background);box-shadow:var(--docsearch-searchbox-shadow);color:var(--docsearch-text-color);outline:none}.DocSearch-Button-Container{align-items:center;display:flex}.DocSearch-Search-Icon{stroke-width:1.6}.DocSearch-Button .DocSearch-Search-Icon{color:var(--docsearch-text-color)}.DocSearch-Button-Placeholder{font-size:1rem;padding:0 12px 0 6px}.DocSearch-Button-Keys{display:flex;min-width:calc(40px + .8em)}.DocSearch-Button-Key{align-items:center;background:var(--docsearch-key-gradient);border-radius:3px;box-shadow:var(--docsearch-key-shadow);color:var(--docsearch-muted-color);display:flex;height:18px;justify-content:center;margin-right:.4em;position:relative;padding:0 0 2px;border:0;top:-1px;width:20px}@media (max-width:768px){.DocSearch-Button-Keys,.DocSearch-Button-Placeholder{display:none}}.DocSearch--active{overflow:hidden!important}.DocSearch-Container,.DocSearch-Container *{box-sizing:border-box}.DocSearch-Container{background-color:var(--docsearch-container-background);height:100vh;left:0;position:fixed;top:0;width:100vw;z-index:200}.DocSearch-Container a{text-decoration:none}.DocSearch-Link{-webkit-appearance:none;-moz-appearance:none;appearance:none;background:none;border:0;color:var(--docsearch-highlight-color);cursor:pointer;font:inherit;margin:0;padding:0}.DocSearch-Modal{background:var(--docsearch-modal-background);border-radius:6px;box-shadow:var(--docsearch-modal-shadow);flex-direction:column;margin:60px auto auto;max-width:var(--docsearch-modal-width);position:relative}.DocSearch-SearchBar{display:flex;padding:var(--docsearch-spacing) var(--docsearch-spacing) 0}.DocSearch-Form{align-items:center;background:var(--docsearch-searchbox-focus-background);border-radius:4px;box-shadow:var(--docsearch-searchbox-shadow);display:flex;height:var(--docsearch-searchbox-height);margin:0;padding:0 var(--docsearch-spacing);position:relative;width:100%}.DocSearch-Input{-webkit-appearance:none;-moz-appearance:none;appearance:none;background:transparent;border:0;color:var(--docsearch-text-color);flex:1;font:inherit;font-size:1.2em;height:100%;outline:none;padding:0 0 0 8px;width:80%}.DocSearch-Input::placeholder{color:var(--docsearch-muted-color);opacity:1}.DocSearch-Input::-webkit-search-cancel-button,.DocSearch-Input::-webkit-search-decoration,.DocSearch-Input::-webkit-search-results-button,.DocSearch-Input::-webkit-search-results-decoration{display:none}.DocSearch-LoadingIndicator,.DocSearch-MagnifierLabel,.DocSearch-Reset{margin:0;padding:0}.DocSearch-MagnifierLabel,.DocSearch-Reset{align-items:center;color:var(--docsearch-highlight-color);display:flex;justify-content:center}.DocSearch-Container--Stalled .DocSearch-MagnifierLabel,.DocSearch-LoadingIndicator{display:none}.DocSearch-Container--Stalled .DocSearch-LoadingIndicator{align-items:center;color:var(--docsearch-highlight-color);display:flex;justify-content:center}@media screen and (prefers-reduced-motion:reduce){.DocSearch-Reset{animation:none;-webkit-appearance:none;-moz-appearance:none;appearance:none;background:none;border:0;border-radius:50%;color:var(--docsearch-icon-color);cursor:pointer;right:0;stroke-width:var(--docsearch-icon-stroke-width)}}.DocSearch-Reset{animation:fade-in .1s ease-in forwards;-webkit-appearance:none;-moz-appearance:none;appearance:none;background:none;border:0;border-radius:50%;color:var(--docsearch-icon-color);cursor:pointer;padding:2px;right:0;stroke-width:var(--docsearch-icon-stroke-width)}.DocSearch-Reset[hidden]{display:none}.DocSearch-Reset:hover{color:var(--docsearch-highlight-color)}.DocSearch-LoadingIndicator svg,.DocSearch-MagnifierLabel svg{height:24px;width:24px}.DocSearch-Cancel{display:none}.DocSearch-Dropdown{max-height:calc(var(--docsearch-modal-height) - var(--docsearch-searchbox-height) - var(--docsearch-spacing) - var(--docsearch-footer-height));min-height:var(--docsearch-spacing);overflow-y:auto;overflow-y:overlay;padding:0 var(--docsearch-spacing);scrollbar-color:var(--docsearch-muted-color) var(--docsearch-modal-background);scrollbar-width:thin}.DocSearch-Dropdown::-webkit-scrollbar{width:12px}.DocSearch-Dropdown::-webkit-scrollbar-track{background:transparent}.DocSearch-Dropdown::-webkit-scrollbar-thumb{background-color:var(--docsearch-muted-color);border:3px solid var(--docsearch-modal-background);border-radius:20px}.DocSearch-Dropdown ul{list-style:none;margin:0;padding:0}.DocSearch-Label{font-size:.75em;line-height:1.6em}.DocSearch-Help,.DocSearch-Label{color:var(--docsearch-muted-color)}.DocSearch-Help{font-size:.9em;margin:0;-webkit-user-select:none;user-select:none}.DocSearch-Title{font-size:1.2em}.DocSearch-Logo a{display:flex}.DocSearch-Logo svg{color:var(--docsearch-logo-color);margin-left:8px}.DocSearch-Hits:last-of-type{margin-bottom:24px}.DocSearch-Hits mark{background:none;color:var(--docsearch-highlight-color)}.DocSearch-HitsFooter{color:var(--docsearch-muted-color);display:flex;font-size:.85em;justify-content:center;margin-bottom:var(--docsearch-spacing);padding:var(--docsearch-spacing)}.DocSearch-HitsFooter a{border-bottom:1px solid;color:inherit}.DocSearch-Hit{border-radius:4px;display:flex;padding-bottom:4px;position:relative}@media screen and (prefers-reduced-motion:reduce){.DocSearch-Hit--deleting{transition:none}}.DocSearch-Hit--deleting{opacity:0;transition:all .25s linear}@media screen and (prefers-reduced-motion:reduce){.DocSearch-Hit--favoriting{transition:none}}.DocSearch-Hit--favoriting{transform:scale(0);transform-origin:top center;transition:all .25s linear;transition-delay:.25s}.DocSearch-Hit a{background:var(--docsearch-hit-background);border-radius:4px;box-shadow:var(--docsearch-hit-shadow);display:block;padding-left:var(--docsearch-spacing);width:100%}.DocSearch-Hit-source{background:var(--docsearch-modal-background);color:var(--docsearch-highlight-color);font-size:.85em;font-weight:600;line-height:32px;margin:0 -4px;padding:8px 4px 0;position:sticky;top:0;z-index:10}.DocSearch-Hit-Tree{color:var(--docsearch-muted-color);height:var(--docsearch-hit-height);opacity:.5;stroke-width:var(--docsearch-icon-stroke-width);width:24px}.DocSearch-Hit[aria-selected=true] a{background-color:var(--docsearch-highlight-color)}.DocSearch-Hit[aria-selected=true] mark{text-decoration:underline}.DocSearch-Hit-Container{align-items:center;color:var(--docsearch-hit-color);display:flex;flex-direction:row;height:var(--docsearch-hit-height);padding:0 var(--docsearch-spacing) 0 0}.DocSearch-Hit-icon{height:20px;width:20px}.DocSearch-Hit-action,.DocSearch-Hit-icon{color:var(--docsearch-muted-color);stroke-width:var(--docsearch-icon-stroke-width)}.DocSearch-Hit-action{align-items:center;display:flex;height:22px;width:22px}.DocSearch-Hit-action svg{display:block;height:18px;width:18px}.DocSearch-Hit-action+.DocSearch-Hit-action{margin-left:6px}.DocSearch-Hit-action-button{-webkit-appearance:none;-moz-appearance:none;appearance:none;background:none;border:0;border-radius:50%;color:inherit;cursor:pointer;padding:2px}svg.DocSearch-Hit-Select-Icon{display:none}.DocSearch-Hit[aria-selected=true] .DocSearch-Hit-Select-Icon{display:block}.DocSearch-Hit-action-button:focus,.DocSearch-Hit-action-button:hover{background:rgba(0,0,0,.2);transition:background-color .1s ease-in}@media screen and (prefers-reduced-motion:reduce){.DocSearch-Hit-action-button:focus,.DocSearch-Hit-action-button:hover{transition:none}}.DocSearch-Hit-action-button:focus path,.DocSearch-Hit-action-button:hover path{fill:#fff}.DocSearch-Hit-content-wrapper{display:flex;flex:1 1 auto;flex-direction:column;font-weight:500;justify-content:center;line-height:1.2em;margin:0 8px;overflow-x:hidden;position:relative;text-overflow:ellipsis;white-space:nowrap;width:80%}.DocSearch-Hit-title{font-size:.9em}.DocSearch-Hit-path{color:var(--docsearch-muted-color);font-size:.75em}.DocSearch-Hit[aria-selected=true] .DocSearch-Hit-action,.DocSearch-Hit[aria-selected=true] .DocSearch-Hit-icon,.DocSearch-Hit[aria-selected=true] .DocSearch-Hit-path,.DocSearch-Hit[aria-selected=true] .DocSearch-Hit-text,.DocSearch-Hit[aria-selected=true] .DocSearch-Hit-title,.DocSearch-Hit[aria-selected=true] .DocSearch-Hit-Tree,.DocSearch-Hit[aria-selected=true] mark{color:var(--docsearch-hit-active-color)!important}@media screen and (prefers-reduced-motion:reduce){.DocSearch-Hit-action-button:focus,.DocSearch-Hit-action-button:hover{background:rgba(0,0,0,.2);transition:none}}.DocSearch-ErrorScreen,.DocSearch-NoResults,.DocSearch-StartScreen{font-size:.9em;margin:0 auto;padding:36px 0;text-align:center;width:80%}.DocSearch-Screen-Icon{color:var(--docsearch-muted-color);padding-bottom:12px}.DocSearch-NoResults-Prefill-List{display:inline-block;padding-bottom:24px;text-align:left}.DocSearch-NoResults-Prefill-List ul{display:inline-block;padding:8px 0 0}.DocSearch-NoResults-Prefill-List li{list-style-position:inside;list-style-type:"» "}.DocSearch-Prefill{-webkit-appearance:none;-moz-appearance:none;appearance:none;background:none;border:0;border-radius:1em;color:var(--docsearch-highlight-color);cursor:pointer;display:inline-block;font-size:1em;font-weight:700;padding:0}.DocSearch-Prefill:focus,.DocSearch-Prefill:hover{outline:none;text-decoration:underline}.DocSearch-Footer{align-items:center;background:var(--docsearch-footer-background);border-radius:0 0 8px 8px;box-shadow:var(--docsearch-footer-shadow);display:flex;flex-direction:row-reverse;flex-shrink:0;height:var(--docsearch-footer-height);justify-content:space-between;padding:0 var(--docsearch-spacing);position:relative;-webkit-user-select:none;user-select:none;width:100%;z-index:300}.DocSearch-Commands{color:var(--docsearch-muted-color);display:flex;list-style:none;margin:0;padding:0}.DocSearch-Commands li{align-items:center;display:flex}.DocSearch-Commands li:not(:last-of-type){margin-right:.8em}.DocSearch-Commands-Key{align-items:center;background:var(--docsearch-key-gradient);border-radius:2px;box-shadow:var(--docsearch-key-shadow);display:flex;height:18px;justify-content:center;margin-right:.4em;padding:0 0 1px;color:var(--docsearch-muted-color);border:0;width:20px}@media (max-width:768px){:root{--docsearch-spacing:10px;--docsearch-footer-height:40px}.DocSearch-Dropdown{height:100%}.DocSearch-Container{height:100vh;height:-webkit-fill-available;height:calc(var(--docsearch-vh, 1vh)*100);position:absolute}.DocSearch-Footer{border-radius:0;bottom:0;position:absolute}.DocSearch-Hit-content-wrapper{display:flex;position:relative;width:80%}.DocSearch-Modal{border-radius:0;box-shadow:none;height:100vh;height:-webkit-fill-available;height:calc(var(--docsearch-vh, 1vh)*100);margin:0;max-width:100%;width:100%}.DocSearch-Dropdown{max-height:calc(var(--docsearch-vh, 1vh)*100 - var(--docsearch-searchbox-height) - var(--docsearch-spacing) - var(--docsearch-footer-height))}.DocSearch-Cancel{-webkit-appearance:none;-moz-appearance:none;appearance:none;background:none;border:0;color:var(--docsearch-highlight-color);cursor:pointer;display:inline-block;flex:none;font:inherit;font-size:1em;font-weight:500;margin-left:var(--docsearch-spacing);outline:none;overflow:hidden;padding:0;-webkit-user-select:none;user-select:none;white-space:nowrap}.DocSearch-Commands,.DocSearch-Hit-Tree{display:none}}@keyframes fade-in{0%{opacity:0}to{opacity:1}}[class*=DocSearch]{--docsearch-primary-color: var(--vp-c-brand-1);--docsearch-highlight-color: var(--docsearch-primary-color);--docsearch-text-color: var(--vp-c-text-1);--docsearch-muted-color: var(--vp-c-text-2);--docsearch-searchbox-shadow: none;--docsearch-searchbox-background: transparent;--docsearch-searchbox-focus-background: transparent;--docsearch-key-gradient: transparent;--docsearch-key-shadow: none;--docsearch-modal-background: var(--vp-c-bg-soft);--docsearch-footer-background: var(--vp-c-bg)}.dark [class*=DocSearch]{--docsearch-modal-shadow: none;--docsearch-footer-shadow: none;--docsearch-logo-color: var(--vp-c-text-2);--docsearch-hit-background: var(--vp-c-default-soft);--docsearch-hit-color: var(--vp-c-text-2);--docsearch-hit-shadow: none}.DocSearch-Button{display:flex;justify-content:center;align-items:center;margin:0;padding:0;width:48px;height:55px;background:transparent;transition:border-color .25s}.DocSearch-Button:hover{background:transparent}.DocSearch-Button:focus{outline:1px dotted;outline:5px auto -webkit-focus-ring-color}.DocSearch-Button:focus:not(:focus-visible){outline:none!important}@media (min-width: 768px){.DocSearch-Button{justify-content:flex-start;border:1px solid transparent;border-radius:8px;padding:0 10px 0 12px;width:100%;height:40px;background-color:var(--vp-c-bg-alt)}.DocSearch-Button:hover{border-color:var(--vp-c-brand-1);background:var(--vp-c-bg-alt)}}.DocSearch-Button .DocSearch-Button-Container{display:flex;align-items:center}.DocSearch-Button .DocSearch-Search-Icon{position:relative;width:16px;height:16px;color:var(--vp-c-text-1);fill:currentColor;transition:color .5s}.DocSearch-Button:hover .DocSearch-Search-Icon{color:var(--vp-c-text-1)}@media (min-width: 768px){.DocSearch-Button .DocSearch-Search-Icon{top:1px;margin-right:8px;width:14px;height:14px;color:var(--vp-c-text-2)}}.DocSearch-Button .DocSearch-Button-Placeholder{display:none;margin-top:2px;padding:0 16px 0 0;font-size:13px;font-weight:500;color:var(--vp-c-text-2);transition:color .5s}.DocSearch-Button:hover .DocSearch-Button-Placeholder{color:var(--vp-c-text-1)}@media (min-width: 768px){.DocSearch-Button .DocSearch-Button-Placeholder{display:inline-block}}.DocSearch-Button .DocSearch-Button-Keys{direction:ltr;display:none;min-width:auto}@media (min-width: 768px){.DocSearch-Button .DocSearch-Button-Keys{display:flex;align-items:center}}.DocSearch-Button .DocSearch-Button-Key{display:block;margin:2px 0 0;border:1px solid var(--vp-c-divider);border-right:none;border-radius:4px 0 0 4px;padding-left:6px;min-width:0;width:auto;height:22px;line-height:22px;font-family:var(--vp-font-family-base);font-size:12px;font-weight:500;transition:color .5s,border-color .5s}.DocSearch-Button .DocSearch-Button-Key+.DocSearch-Button-Key{border-right:1px solid var(--vp-c-divider);border-left:none;border-radius:0 4px 4px 0;padding-left:2px;padding-right:6px}.DocSearch-Button .DocSearch-Button-Key:first-child{font-size:0!important}.DocSearch-Button .DocSearch-Button-Key:first-child:after{content:"Ctrl";font-size:12px;letter-spacing:normal;color:var(--docsearch-muted-color)}.mac .DocSearch-Button .DocSearch-Button-Key:first-child:after{content:"⌘"}.DocSearch-Button .DocSearch-Button-Key:first-child>*{display:none}.VPNavBarSearch{display:flex;align-items:center}@media (min-width: 768px){.VPNavBarSearch{flex-grow:1;padding-left:24px}}@media (min-width: 960px){.VPNavBarSearch{padding-left:32px}}.dark .DocSearch-Footer{border-top:1px solid var(--vp-c-divider)}.DocSearch-Form{border:1px solid var(--vp-c-brand-1);background-color:var(--vp-c-white)}.dark .DocSearch-Form{background-color:var(--vp-c-default-soft)}.DocSearch-Screen-Icon>svg{margin:auto}.VPNavBarSocialLinks[data-v-0394ad82]{display:none}@media (min-width: 1280px){.VPNavBarSocialLinks[data-v-0394ad82]{display:flex;align-items:center}}.title[data-v-86d1bed8]{display:flex;align-items:center;border-bottom:1px solid transparent;width:100%;height:var(--vp-nav-height);font-size:16px;font-weight:600;color:var(--vp-c-text-1);transition:opacity .25s}@media (min-width: 960px){.title[data-v-86d1bed8]{flex-shrink:0}.VPNavBarTitle.has-sidebar .title[data-v-86d1bed8]{border-bottom-color:var(--vp-c-divider)}}[data-v-86d1bed8] .logo{margin-right:8px;height:var(--vp-nav-logo-height)}.VPNavBarTranslations[data-v-74abcbb9]{display:none}@media (min-width: 1280px){.VPNavBarTranslations[data-v-74abcbb9]{display:flex;align-items:center}}.title[data-v-74abcbb9]{padding:0 24px 0 12px;line-height:32px;font-size:14px;font-weight:700;color:var(--vp-c-text-1)}.VPNavBar[data-v-a0fd61f4]{position:relative;border-bottom:1px solid transparent;padding:0 8px 0 24px;height:var(--vp-nav-height);pointer-events:none;white-space:nowrap}@media (min-width: 768px){.VPNavBar[data-v-a0fd61f4]{padding:0 32px}}@media (min-width: 960px){.VPNavBar.has-sidebar[data-v-a0fd61f4]{padding:0}.VPNavBar[data-v-a0fd61f4]:not(.has-sidebar):not(.top){border-bottom-color:var(--vp-c-gutter);background-color:var(--vp-nav-bg-color)}}.container[data-v-a0fd61f4]{display:flex;justify-content:space-between;margin:0 auto;max-width:calc(var(--vp-layout-max-width) - 64px);height:var(--vp-nav-height);pointer-events:none}.container>.title[data-v-a0fd61f4],.container>.content[data-v-a0fd61f4]{pointer-events:none}.container[data-v-a0fd61f4] *{pointer-events:auto}@media (min-width: 960px){.VPNavBar.has-sidebar .container[data-v-a0fd61f4]{max-width:100%}}.title[data-v-a0fd61f4]{flex-shrink:0;height:calc(var(--vp-nav-height) - 1px);transition:background-color .5s}@media (min-width: 960px){.VPNavBar.has-sidebar .title[data-v-a0fd61f4]{position:absolute;top:0;left:0;z-index:2;padding:0 32px;width:var(--vp-sidebar-width);height:var(--vp-nav-height);background-color:transparent}}@media (min-width: 1440px){.VPNavBar.has-sidebar .title[data-v-a0fd61f4]{padding-left:max(32px,calc((100% - (var(--vp-layout-max-width) - 64px)) / 2));width:calc((100% - (var(--vp-layout-max-width) - 64px)) / 2 + var(--vp-sidebar-width) - 32px)}}.content[data-v-a0fd61f4]{flex-grow:1}@media (min-width: 960px){.VPNavBar.has-sidebar .content[data-v-a0fd61f4]{position:relative;z-index:1;padding-right:32px;padding-left:var(--vp-sidebar-width)}}@media (min-width: 1440px){.VPNavBar.has-sidebar .content[data-v-a0fd61f4]{padding-right:calc((100vw - var(--vp-layout-max-width)) / 2 + 32px);padding-left:calc((100vw - var(--vp-layout-max-width)) / 2 + var(--vp-sidebar-width))}}.content-body[data-v-a0fd61f4]{display:flex;justify-content:flex-end;align-items:center;height:calc(var(--vp-nav-height) - 1px);transition:background-color .5s}@media (min-width: 960px){.VPNavBar:not(.top) .content-body[data-v-a0fd61f4]{position:relative;background-color:var(--vp-nav-bg-color)}}@media (max-width: 767px){.content-body[data-v-a0fd61f4]{column-gap:.5rem}}.menu+.translations[data-v-a0fd61f4]:before,.menu+.appearance[data-v-a0fd61f4]:before,.menu+.social-links[data-v-a0fd61f4]:before,.translations+.appearance[data-v-a0fd61f4]:before,.appearance+.social-links[data-v-a0fd61f4]:before{margin-right:8px;margin-left:8px;width:1px;height:24px;background-color:var(--vp-c-divider);content:""}.menu+.appearance[data-v-a0fd61f4]:before,.translations+.appearance[data-v-a0fd61f4]:before{margin-right:16px}.appearance+.social-links[data-v-a0fd61f4]:before{margin-left:16px}.social-links[data-v-a0fd61f4]{margin-right:-8px}@media (min-width: 960px){.VPNavBar.has-sidebar .curtain[data-v-a0fd61f4]{position:absolute;right:0;bottom:-31px;width:calc(100% - var(--vp-sidebar-width));height:32px}.VPNavBar.has-sidebar .curtain[data-v-a0fd61f4]:before{display:block;width:100%;height:32px;background:linear-gradient(var(--vp-c-bg),transparent 70%);content:""}}@media (min-width: 1440px){.VPNavBar.has-sidebar .curtain[data-v-a0fd61f4]{width:calc(100% - ((100vw - var(--vp-layout-max-width)) / 2 + var(--vp-sidebar-width)))}}.VPNavScreenAppearance[data-v-2d7af913]{display:flex;justify-content:space-between;align-items:center;border-radius:8px;padding:12px 14px 12px 16px;background-color:var(--vp-c-bg-soft)}.text[data-v-2d7af913]{line-height:24px;font-size:12px;font-weight:500;color:var(--vp-c-text-2)}.VPNavScreenMenuLink[data-v-05f27b2a]{display:block;border-bottom:1px solid var(--vp-c-divider);padding:12px 0 11px;line-height:24px;font-size:14px;font-weight:500;color:var(--vp-c-text-1);transition:border-color .25s,color .25s}.VPNavScreenMenuLink[data-v-05f27b2a]:hover{color:var(--vp-c-brand-1)}.VPNavScreenMenuGroupLink[data-v-19976ae1]{display:block;margin-left:12px;line-height:32px;font-size:14px;font-weight:400;color:var(--vp-c-text-1);transition:color .25s}.VPNavScreenMenuGroupLink[data-v-19976ae1]:hover{color:var(--vp-c-brand-1)}.VPNavScreenMenuGroupSection[data-v-8133b170]{display:block}.title[data-v-8133b170]{line-height:32px;font-size:13px;font-weight:700;color:var(--vp-c-text-2);transition:color .25s}.VPNavScreenMenuGroup[data-v-1ecb84e7]{border-bottom:1px solid var(--vp-c-divider);height:48px;overflow:hidden;transition:border-color .5s}.VPNavScreenMenuGroup .items[data-v-1ecb84e7]{visibility:hidden}.VPNavScreenMenuGroup.open .items[data-v-1ecb84e7]{visibility:visible}.VPNavScreenMenuGroup.open[data-v-1ecb84e7]{padding-bottom:10px;height:auto}.VPNavScreenMenuGroup.open .button[data-v-1ecb84e7]{padding-bottom:6px;color:var(--vp-c-brand-1)}.VPNavScreenMenuGroup.open .button-icon[data-v-1ecb84e7]{transform:rotate(45deg)}.button[data-v-1ecb84e7]{display:flex;justify-content:space-between;align-items:center;padding:12px 4px 11px 0;width:100%;line-height:24px;font-size:14px;font-weight:500;color:var(--vp-c-text-1);transition:color .25s}.button[data-v-1ecb84e7]:hover{color:var(--vp-c-brand-1)}.button-icon[data-v-1ecb84e7]{width:14px;height:14px;fill:var(--vp-c-text-2);transition:fill .5s,transform .25s}.group[data-v-1ecb84e7]:first-child{padding-top:0}.group+.group[data-v-1ecb84e7],.group+.item[data-v-1ecb84e7]{padding-top:4px}.VPNavScreenTranslations[data-v-d72aa483]{height:24px;overflow:hidden}.VPNavScreenTranslations.open[data-v-d72aa483]{height:auto}.title[data-v-d72aa483]{display:flex;align-items:center;font-size:14px;font-weight:500;color:var(--vp-c-text-1)}.icon[data-v-d72aa483]{width:16px;height:16px;fill:currentColor}.icon.lang[data-v-d72aa483]{margin-right:8px}.icon.chevron[data-v-d72aa483]{margin-left:4px}.list[data-v-d72aa483]{padding:4px 0 0 24px}.link[data-v-d72aa483]{line-height:32px;font-size:13px;color:var(--vp-c-text-1)}.VPNavScreen[data-v-cc5739dd]{position:fixed;top:calc(var(--vp-nav-height) + var(--vp-layout-top-height, 0px) + 1px);right:0;bottom:0;left:0;padding:0 32px;width:100%;background-color:var(--vp-nav-screen-bg-color);overflow-y:auto;transition:background-color .5s;pointer-events:auto}.VPNavScreen.fade-enter-active[data-v-cc5739dd],.VPNavScreen.fade-leave-active[data-v-cc5739dd]{transition:opacity .25s}.VPNavScreen.fade-enter-active .container[data-v-cc5739dd],.VPNavScreen.fade-leave-active .container[data-v-cc5739dd]{transition:transform .25s ease}.VPNavScreen.fade-enter-from[data-v-cc5739dd],.VPNavScreen.fade-leave-to[data-v-cc5739dd]{opacity:0}.VPNavScreen.fade-enter-from .container[data-v-cc5739dd],.VPNavScreen.fade-leave-to .container[data-v-cc5739dd]{transform:translateY(-8px)}@media (min-width: 768px){.VPNavScreen[data-v-cc5739dd]{display:none}}.container[data-v-cc5739dd]{margin:0 auto;padding:24px 0 96px;max-width:288px}.menu+.translations[data-v-cc5739dd],.menu+.appearance[data-v-cc5739dd],.translations+.appearance[data-v-cc5739dd]{margin-top:24px}.menu+.social-links[data-v-cc5739dd]{margin-top:16px}.appearance+.social-links[data-v-cc5739dd]{margin-top:16px}.VPNav[data-v-ae24b3ad]{position:relative;top:var(--vp-layout-top-height, 0px);left:0;z-index:var(--vp-z-index-nav);width:100%;pointer-events:none;transition:background-color .5s}@media (min-width: 960px){.VPNav[data-v-ae24b3ad]{position:fixed}}.VPSidebarItem.level-0[data-v-e31bd47b]{padding-bottom:24px}.VPSidebarItem.collapsed.level-0[data-v-e31bd47b]{padding-bottom:10px}.item[data-v-e31bd47b]{position:relative;display:flex;width:100%}.VPSidebarItem.collapsible>.item[data-v-e31bd47b]{cursor:pointer}.indicator[data-v-e31bd47b]{position:absolute;top:6px;bottom:6px;left:-17px;width:2px;border-radius:2px;transition:background-color .25s}.VPSidebarItem.level-2.is-active>.item>.indicator[data-v-e31bd47b],.VPSidebarItem.level-3.is-active>.item>.indicator[data-v-e31bd47b],.VPSidebarItem.level-4.is-active>.item>.indicator[data-v-e31bd47b],.VPSidebarItem.level-5.is-active>.item>.indicator[data-v-e31bd47b]{background-color:var(--vp-c-brand-1)}.link[data-v-e31bd47b]{display:flex;align-items:center;flex-grow:1}.text[data-v-e31bd47b]{flex-grow:1;padding:4px 0;line-height:24px;font-size:14px;transition:color .25s}.VPSidebarItem.level-0 .text[data-v-e31bd47b]{font-weight:700;color:var(--vp-c-text-1)}.VPSidebarItem.level-1 .text[data-v-e31bd47b],.VPSidebarItem.level-2 .text[data-v-e31bd47b],.VPSidebarItem.level-3 .text[data-v-e31bd47b],.VPSidebarItem.level-4 .text[data-v-e31bd47b],.VPSidebarItem.level-5 .text[data-v-e31bd47b]{font-weight:500;color:var(--vp-c-text-2)}.VPSidebarItem.level-0.is-link>.item>.link:hover .text[data-v-e31bd47b],.VPSidebarItem.level-1.is-link>.item>.link:hover .text[data-v-e31bd47b],.VPSidebarItem.level-2.is-link>.item>.link:hover .text[data-v-e31bd47b],.VPSidebarItem.level-3.is-link>.item>.link:hover .text[data-v-e31bd47b],.VPSidebarItem.level-4.is-link>.item>.link:hover .text[data-v-e31bd47b],.VPSidebarItem.level-5.is-link>.item>.link:hover .text[data-v-e31bd47b]{color:var(--vp-c-brand-1)}.VPSidebarItem.level-0.has-active>.item>.text[data-v-e31bd47b],.VPSidebarItem.level-1.has-active>.item>.text[data-v-e31bd47b],.VPSidebarItem.level-2.has-active>.item>.text[data-v-e31bd47b],.VPSidebarItem.level-3.has-active>.item>.text[data-v-e31bd47b],.VPSidebarItem.level-4.has-active>.item>.text[data-v-e31bd47b],.VPSidebarItem.level-5.has-active>.item>.text[data-v-e31bd47b],.VPSidebarItem.level-0.has-active>.item>.link>.text[data-v-e31bd47b],.VPSidebarItem.level-1.has-active>.item>.link>.text[data-v-e31bd47b],.VPSidebarItem.level-2.has-active>.item>.link>.text[data-v-e31bd47b],.VPSidebarItem.level-3.has-active>.item>.link>.text[data-v-e31bd47b],.VPSidebarItem.level-4.has-active>.item>.link>.text[data-v-e31bd47b],.VPSidebarItem.level-5.has-active>.item>.link>.text[data-v-e31bd47b]{color:var(--vp-c-text-1)}.VPSidebarItem.level-0.is-active>.item .link>.text[data-v-e31bd47b],.VPSidebarItem.level-1.is-active>.item .link>.text[data-v-e31bd47b],.VPSidebarItem.level-2.is-active>.item .link>.text[data-v-e31bd47b],.VPSidebarItem.level-3.is-active>.item .link>.text[data-v-e31bd47b],.VPSidebarItem.level-4.is-active>.item .link>.text[data-v-e31bd47b],.VPSidebarItem.level-5.is-active>.item .link>.text[data-v-e31bd47b]{color:var(--vp-c-brand-1)}.caret[data-v-e31bd47b]{display:flex;justify-content:center;align-items:center;margin-right:-7px;width:32px;height:32px;color:var(--vp-c-text-3);cursor:pointer;transition:color .25s;flex-shrink:0}.item:hover .caret[data-v-e31bd47b]{color:var(--vp-c-text-2)}.item:hover .caret[data-v-e31bd47b]:hover{color:var(--vp-c-text-1)}.caret-icon[data-v-e31bd47b]{width:18px;height:18px;fill:currentColor;transform:rotate(90deg);transition:transform .25s}.VPSidebarItem.collapsed .caret-icon[data-v-e31bd47b]{transform:rotate(0)}.VPSidebarItem.level-1 .items[data-v-e31bd47b],.VPSidebarItem.level-2 .items[data-v-e31bd47b],.VPSidebarItem.level-3 .items[data-v-e31bd47b],.VPSidebarItem.level-4 .items[data-v-e31bd47b],.VPSidebarItem.level-5 .items[data-v-e31bd47b]{border-left:1px solid var(--vp-c-divider);padding-left:16px}.VPSidebarItem.collapsed .items[data-v-e31bd47b]{display:none}.VPSidebar[data-v-b00e2fdd]{position:fixed;top:var(--vp-layout-top-height, 0px);bottom:0;left:0;z-index:var(--vp-z-index-sidebar);padding:32px 32px 96px;width:calc(100vw - 64px);max-width:320px;background-color:var(--vp-sidebar-bg-color);opacity:0;box-shadow:var(--vp-c-shadow-3);overflow-x:hidden;overflow-y:auto;transform:translate(-100%);transition:opacity .5s,transform .25s ease;overscroll-behavior:contain}.VPSidebar.open[data-v-b00e2fdd]{opacity:1;visibility:visible;transform:translate(0);transition:opacity .25s,transform .5s cubic-bezier(.19,1,.22,1)}.dark .VPSidebar[data-v-b00e2fdd]{box-shadow:var(--vp-shadow-1)}@media (min-width: 960px){.VPSidebar[data-v-b00e2fdd]{z-index:1;padding-top:var(--vp-nav-height);padding-bottom:128px;width:var(--vp-sidebar-width);max-width:100%;background-color:var(--vp-sidebar-bg-color);opacity:1;visibility:visible;box-shadow:none;transform:translate(0)}}@media (min-width: 1440px){.VPSidebar[data-v-b00e2fdd]{padding-left:max(32px,calc((100% - (var(--vp-layout-max-width) - 64px)) / 2));width:calc((100% - (var(--vp-layout-max-width) - 64px)) / 2 + var(--vp-sidebar-width) - 32px)}}@media (min-width: 960px){.curtain[data-v-b00e2fdd]{position:sticky;top:-64px;left:0;z-index:1;margin-top:calc(var(--vp-nav-height) * -1);margin-right:-32px;margin-left:-32px;height:var(--vp-nav-height);background-color:var(--vp-sidebar-bg-color)}}.nav[data-v-b00e2fdd]{outline:0}.group+.group[data-v-b00e2fdd]{border-top:1px solid var(--vp-c-divider);padding-top:10px}@media (min-width: 960px){.group[data-v-b00e2fdd]{padding-top:10px;width:calc(var(--vp-sidebar-width) - 64px)}}.VPSkipLink[data-v-0f60ec36]{top:8px;left:8px;padding:8px 16px;z-index:999;border-radius:8px;font-size:12px;font-weight:700;text-decoration:none;color:var(--vp-c-brand-1);box-shadow:var(--vp-shadow-3);background-color:var(--vp-c-bg)}.VPSkipLink[data-v-0f60ec36]:focus{height:auto;width:auto;clip:auto;clip-path:none}@media (min-width: 1280px){.VPSkipLink[data-v-0f60ec36]{top:14px;left:16px}}.Layout[data-v-5a346dfe]{display:flex;flex-direction:column;min-height:100vh}.VPHomeSponsors[data-v-96bd69d5]{border-top:1px solid var(--vp-c-gutter);padding:88px 24px 96px;background-color:var(--vp-c-bg)}.container[data-v-96bd69d5]{margin:0 auto;max-width:1152px}.love[data-v-96bd69d5]{margin:0 auto;width:28px;height:28px;color:var(--vp-c-text-3)}.icon[data-v-96bd69d5]{width:28px;height:28px;fill:currentColor}.message[data-v-96bd69d5]{margin:0 auto;padding-top:10px;max-width:320px;text-align:center;line-height:24px;font-size:16px;font-weight:500;color:var(--vp-c-text-2)}.sponsors[data-v-96bd69d5]{padding-top:32px}.action[data-v-96bd69d5]{padding-top:40px;text-align:center}.VPTeamPage[data-v-10b00018]{padding-bottom:96px}@media (min-width: 768px){.VPTeamPage[data-v-10b00018]{padding-bottom:128px}}.VPTeamPageSection+.VPTeamPageSection[data-v-10b00018-s],.VPTeamMembers+.VPTeamPageSection[data-v-10b00018-s]{margin-top:64px}.VPTeamMembers+.VPTeamMembers[data-v-10b00018-s]{margin-top:24px}@media (min-width: 768px){.VPTeamPageTitle+.VPTeamPageSection[data-v-10b00018-s]{margin-top:16px}.VPTeamPageSection+.VPTeamPageSection[data-v-10b00018-s],.VPTeamMembers+.VPTeamPageSection[data-v-10b00018-s]{margin-top:96px}}.VPTeamMembers[data-v-10b00018-s]{padding:0 24px}@media (min-width: 768px){.VPTeamMembers[data-v-10b00018-s]{padding:0 48px}}@media (min-width: 960px){.VPTeamMembers[data-v-10b00018-s]{padding:0 64px}}.VPTeamPageTitle[data-v-bf2cbdac]{padding:48px 32px;text-align:center}@media (min-width: 768px){.VPTeamPageTitle[data-v-bf2cbdac]{padding:64px 48px 48px}}@media (min-width: 960px){.VPTeamPageTitle[data-v-bf2cbdac]{padding:80px 64px 48px}}.title[data-v-bf2cbdac]{letter-spacing:0;line-height:44px;font-size:36px;font-weight:500}@media (min-width: 768px){.title[data-v-bf2cbdac]{letter-spacing:-.5px;line-height:56px;font-size:48px}}.lead[data-v-bf2cbdac]{margin:0 auto;max-width:512px;padding-top:12px;line-height:24px;font-size:16px;font-weight:500;color:var(--vp-c-text-2)}@media (min-width: 768px){.lead[data-v-bf2cbdac]{max-width:592px;letter-spacing:.15px;line-height:28px;font-size:20px}}.VPTeamPageSection[data-v-b1a88750]{padding:0 32px}@media (min-width: 768px){.VPTeamPageSection[data-v-b1a88750]{padding:0 48px}}@media (min-width: 960px){.VPTeamPageSection[data-v-b1a88750]{padding:0 64px}}.title[data-v-b1a88750]{position:relative;margin:0 auto;max-width:1152px;text-align:center;color:var(--vp-c-text-2)}.title-line[data-v-b1a88750]{position:absolute;top:16px;left:0;width:100%;height:1px;background-color:var(--vp-c-divider)}.title-text[data-v-b1a88750]{position:relative;display:inline-block;padding:0 24px;letter-spacing:0;line-height:32px;font-size:20px;font-weight:500;background-color:var(--vp-c-bg)}.lead[data-v-b1a88750]{margin:0 auto;max-width:480px;padding-top:12px;text-align:center;line-height:24px;font-size:16px;font-weight:500;color:var(--vp-c-text-2)}.members[data-v-b1a88750]{padding-top:40px}.VPTeamMembersItem[data-v-28528e42]{display:flex;flex-direction:column;gap:2px;border-radius:12px;width:100%;height:100%;overflow:hidden}.VPTeamMembersItem.small .profile[data-v-28528e42]{padding:32px}.VPTeamMembersItem.small .data[data-v-28528e42]{padding-top:20px}.VPTeamMembersItem.small .avatar[data-v-28528e42]{width:64px;height:64px}.VPTeamMembersItem.small .name[data-v-28528e42]{line-height:24px;font-size:16px}.VPTeamMembersItem.small .affiliation[data-v-28528e42]{padding-top:4px;line-height:20px;font-size:14px}.VPTeamMembersItem.small .desc[data-v-28528e42]{padding-top:12px;line-height:20px;font-size:14px}.VPTeamMembersItem.small .links[data-v-28528e42]{margin:0 -16px -20px;padding:10px 0 0}.VPTeamMembersItem.medium .profile[data-v-28528e42]{padding:48px 32px}.VPTeamMembersItem.medium .data[data-v-28528e42]{padding-top:24px;text-align:center}.VPTeamMembersItem.medium .avatar[data-v-28528e42]{width:96px;height:96px}.VPTeamMembersItem.medium .name[data-v-28528e42]{letter-spacing:.15px;line-height:28px;font-size:20px}.VPTeamMembersItem.medium .affiliation[data-v-28528e42]{padding-top:4px;font-size:16px}.VPTeamMembersItem.medium .desc[data-v-28528e42]{padding-top:16px;max-width:288px;font-size:16px}.VPTeamMembersItem.medium .links[data-v-28528e42]{margin:0 -16px -12px;padding:16px 12px 0}.profile[data-v-28528e42]{flex-grow:1;background-color:var(--vp-c-bg-soft)}.data[data-v-28528e42]{text-align:center}.avatar[data-v-28528e42]{position:relative;flex-shrink:0;margin:0 auto;border-radius:50%;box-shadow:var(--vp-shadow-3)}.avatar-img[data-v-28528e42]{position:absolute;top:0;right:0;bottom:0;left:0;border-radius:50%;object-fit:cover}.name[data-v-28528e42]{margin:0;font-weight:600}.affiliation[data-v-28528e42]{margin:0;font-weight:500;color:var(--vp-c-text-2)}.org.link[data-v-28528e42]{color:var(--vp-c-text-2);transition:color .25s}.org.link[data-v-28528e42]:hover{color:var(--vp-c-brand-1)}.desc[data-v-28528e42]{margin:0 auto}.desc[data-v-28528e42] a{font-weight:500;color:var(--vp-c-brand-1);text-decoration-style:dotted;transition:color .25s}.links[data-v-28528e42]{display:flex;justify-content:center;height:56px}.sp-link[data-v-28528e42]{display:flex;justify-content:center;align-items:center;text-align:center;padding:16px;font-size:14px;font-weight:500;color:var(--vp-c-sponsor);background-color:var(--vp-c-bg-soft);transition:color .25s,background-color .25s}.sp .sp-link.link[data-v-28528e42]:hover,.sp .sp-link.link[data-v-28528e42]:focus{outline:none;color:var(--vp-c-white);background-color:var(--vp-c-sponsor)}.sp-icon[data-v-28528e42]{margin-right:8px;width:16px;height:16px;fill:currentColor}.VPTeamMembers.small .container[data-v-6cb0dbc4]{grid-template-columns:repeat(auto-fit,minmax(224px,1fr))}.VPTeamMembers.small.count-1 .container[data-v-6cb0dbc4]{max-width:276px}.VPTeamMembers.small.count-2 .container[data-v-6cb0dbc4]{max-width:576px}.VPTeamMembers.small.count-3 .container[data-v-6cb0dbc4]{max-width:876px}.VPTeamMembers.medium .container[data-v-6cb0dbc4]{grid-template-columns:repeat(auto-fit,minmax(256px,1fr))}@media (min-width: 375px){.VPTeamMembers.medium .container[data-v-6cb0dbc4]{grid-template-columns:repeat(auto-fit,minmax(288px,1fr))}}.VPTeamMembers.medium.count-1 .container[data-v-6cb0dbc4]{max-width:368px}.VPTeamMembers.medium.count-2 .container[data-v-6cb0dbc4]{max-width:760px}.container[data-v-6cb0dbc4]{display:grid;gap:24px;margin:0 auto;max-width:1152px} diff --git a/assets/vi_VN_guide_app-profile.md.0b09a7b5.js b/assets/vi_VN_guide_app-profile.md.0b09a7b5.js new file mode 100644 index 000000000000..cd9af8288a98 --- /dev/null +++ b/assets/vi_VN_guide_app-profile.md.0b09a7b5.js @@ -0,0 +1,9 @@ +import{_ as n,o as c,c as o,Q as s}from"./chunks/framework.ec8f7e8e.js";const d=JSON.parse('{"title":"App Profile","description":"","frontmatter":{},"headers":[],"relativePath":"vi_VN/guide/app-profile.md","filePath":"vi_VN/guide/app-profile.md"}'),t={name:"vi_VN/guide/app-profile.md"},a=s(`

App Profile

App Profile là một cơ chế do KernelSU cung cấp để tùy chỉnh cấu hình của các ứng dụng khác nhau.

Đối với các ứng dụng được cấp quyền root (tức là có thể sử dụng su), App Profile cũng có thể được gọi là Root Profile. Nó cho phép tùy chỉnh các quy tắc uid, gid, groups, capabilitiesSELinux của lệnh su, do đó hạn chế các đặc quyền của người dùng root. Ví dụ: nó có thể chỉ cấp quyền mạng cho các ứng dụng tường lửa trong khi từ chối quyền truy cập tệp hoặc có thể cấp quyền shell thay vì quyền truy cập root đầy đủ cho các ứng dụng đóng băng: giữ nguồn điện theo nguyên tắc đặc quyền tối thiểu.

Đối với các ứng dụng thông thường không có quyền root, App Profile có thể kiểm soát hành vi của hệ thống kernel và mô-đun đối với các ứng dụng này. Ví dụ, nó có thể xác định liệu các sửa đổi do mô-đun tạo ra có nên được giải quyết hay không. Hệ thống kernel và mô-đun có thể đưa ra quyết định dựa trên cấu hình này, chẳng hạn như thực hiện các hoạt động tương tự như "hiding"

Root Profile

UID, GID, và Groups

Hệ thống Linux có hai khái niệm: người dùng (user) và nhóm (group). Mỗi người dùng có một user ID (UID) và một người dùng có thể thuộc nhiều nhóm, mỗi nhóm có group ID (GID) riêng. Những ID này được sử dụng để xác định người dùng trong hệ thống và xác định tài nguyên hệ thống nào họ có thể truy cập.

Người dùng có UID bằng 0 được gọi là người dùng root và các nhóm có GID bằng 0 được gọi là nhóm root. Nhóm người dùng root thường giữ các đặc quyền hệ thống cao nhất.

Trong trường hợp hệ thống Android, mỗi ứng dụng là một người dùng riêng biệt (không bao gồm các trường hợp UID dùng chung) với một UID duy nhất. Ví dụ: 0 đại diện cho người dùng root, 1000 đại diện cho system, 2000 đại diện cho ADB shell và các UID từ 10000 đến 19999 đại diện cho các ứng dụng thông thường.

INFO

Ở đây, UID được đề cập không giống với khái niệm nhiều người dùng hoặc hồ sơ công việc (Work profile) trong hệ thống Android. Hồ sơ công việc thực sự được triển khai bằng cách phân vùng phạm vi UID. Ví dụ: 10000-19999 đại diện cho người dùng chính, trong khi 110000-119999 đại diện cho hồ sơ công việc. Mỗi ứng dụng thông thường trong số đó đều có UID riêng.

Mỗi ứng dụng có thể có nhiều nhóm, với GID đại diện cho nhóm chính, thường khớp với UID. Các nhóm khác được gọi là nhóm bổ sung. Một số quyền nhất định được kiểm soát thông qua các nhóm, chẳng hạn như quyền truy cập mạng hoặc truy cập Bluetooth.

Ví dụ: nếu chúng ta thực thi lệnh id trong shell ADB, kết quả đầu ra có thể trông như thế này:

sh
oriole:/ $ id
+uid=2000(shell) gid=2000(shell) groups=2000(shell),1004(input),1007(log),1011(adb),1015(sdcard_rw),1028(sdcard_r),1078(ext_data_rw),1079(ext_obb_rw),3001(net_bt_admin),3002(net_bt),3003(inet),3006(net_bw_stats),3009(readproc),3011(uhid),3012(readtracefs) context=u:r:shell:s0
oriole:/ $ id
+uid=2000(shell) gid=2000(shell) groups=2000(shell),1004(input),1007(log),1011(adb),1015(sdcard_rw),1028(sdcard_r),1078(ext_data_rw),1079(ext_obb_rw),3001(net_bt_admin),3002(net_bt),3003(inet),3006(net_bw_stats),3009(readproc),3011(uhid),3012(readtracefs) context=u:r:shell:s0

Ở đây, UID là 2000 và GID (ID nhóm chính) cũng là 2000. Ngoài ra, nó thuộc một số nhóm bổ sung, chẳng hạn như inet (biểu thị khả năng tạo ổ cắm AF_INETAF_INET6) và sdcard_rw (biểu thị quyền đọc/ghi đối với thẻ SD).

Root Profile của KernelSU cho phép tùy chỉnh UID, GID và các nhóm cho quy trình gốc sau khi thực thi su. Ví dụ: Cấu hình gốc của ứng dụng gốc có thể đặt UID của nó thành 2000, có nghĩa là khi sử dụng su, các quyền thực tế của ứng dụng sẽ ở cấp shell ADB. Nhóm inet có thể bị xóa, ngăn lệnh su truy cập mạng.

Ghi chú

Hồ sơ ứng dụng chỉ kiểm soát các quyền của tiến trình gốc sau khi sử dụng su; nó không kiểm soát các quyền của ứng dụng. Nếu một ứng dụng đã yêu cầu quyền truy cập mạng, ứng dụng đó vẫn có thể truy cập mạng ngay cả khi không sử dụng su. Việc xóa nhóm inet khỏi su chỉ ngăn su truy cập mạng.

Root Profile được thực thi trong kernel và không dựa vào hành vi tự nguyện của các ứng dụng root, không giống như việc chuyển đổi người dùng hoặc nhóm thông qua su, việc cấp quyền su hoàn toàn phụ thuộc vào người dùng chứ không phải nhà phát triển.

Capabilities

Capabilities (khả năng) là một cơ chế phân tách đặc quyền trong Linux.

Với mục đích thực hiện kiểm tra quyền, việc triển khai UNIX truyền thống phân biệt hai loại quy trình: quy trình đặc quyền (có ID người dùng hiệu quả là 0, được gọi là siêu người dùng hoặc root) và quy trình không có đặc quyền (có UID hiệu dụng khác 0). Các quy trình đặc quyền bỏ qua tất cả các bước kiểm tra quyền của kernel, trong khi các quy trình không có đặc quyền phải chịu sự kiểm tra quyền đầy đủ dựa trên thông tin xác thực của quy trình (thường là: UID hiệu quả, GID hiệu quả và danh sách nhóm bổ sung).

Bắt đầu với Linux 2.2, Linux chia các đặc quyền truyền thống được liên kết với siêu người dùng thành các đơn vị riêng biệt, được gọi là các khả năng, có thể được bật và tắt một cách độc lập.

Mỗi Khả năng đại diện cho một hoặc nhiều đặc quyền. Ví dụ: CAP_DAC_READ_SEARCH thể hiện khả năng bỏ qua việc kiểm tra quyền để đọc tệp cũng như quyền đọc và thực thi thư mục. Nếu người dùng có UID hiệu dụng là 0 (người dùng root) thiếu khả năng CAP_DAC_READ_SEARCH hoặc cao hơn, điều này có nghĩa là ngay cả khi họ là root, họ không thể tùy ý đọc tệp.

Cấu hình gốc của KernelSU cho phép tùy chỉnh các Khả năng của tiến trình gốc sau khi thực thi su, nhờ đó đạt được việc cấp một phần "quyền root". Không giống như UID và GID đã nói ở trên, một số ứng dụng gốc nhất định yêu cầu UID là 0 sau khi sử dụng su. Trong những trường hợp như vậy, việc giới hạn Khả năng của người dùng root này bằng UID 0 có thể hạn chế các hoạt động được phép của họ.

Rất Khuyến Nghị

Capabilities của Linux tài liệu chính thức cung cấp giải thích chi tiết về các khả năng mà mỗi Capabilities thể hiện. Nếu bạn có ý định tùy chỉnh Capabilities, bạn nên đọc tài liệu này trước.

SELinux

SELinux là một cơ chế Kiểm Soát Truy Cập Bắt Buộc (Mandatory Access Control: MAC) mạnh mẽ. Nó hoạt động theo nguyên tắc từ chối mặc định: bất kỳ hành động nào không được cho phép rõ ràng đều bị từ chối.

SELinux có thể chạy ở hai chế độ chung:

  1. Chế độ cho phép (Permissive mode): Các sự kiện từ chối được ghi lại nhưng không được thực thi.
  2. Chế độ thực thi (Enforcing mode): Các sự kiện từ chối được ghi lại và thực thi.

Cảnh báo

Các hệ thống Android hiện đại phụ thuộc rất nhiều vào SELinux để đảm bảo an ninh hệ thống tổng thể. Chúng tôi khuyên bạn không nên sử dụng bất kỳ hệ thống tùy chỉnh nào chạy ở "chế độ cho phép" vì nó không mang lại lợi thế đáng kể nào so với hệ thống mở hoàn toàn.

Việc giải thích khái niệm đầy đủ về SELinux rất phức tạp và nằm ngoài phạm vi của tài liệu này. Trước tiên nên hiểu hoạt động của nó thông qua các tài nguyên sau:

  1. Wikipedia
  2. Red Hat: What Is SELinux?
  3. ArchLinux: SELinux

Root Profile của KernelSU cho phép tùy chỉnh ngữ cảnh SELinux của tiến trình gốc sau khi thực thi su. Các quy tắc kiểm soát truy cập cụ thể có thể được đặt cho bối cảnh này để cho phép kiểm soát chi tiết hơn các quyền .

Trong các trường hợp điển hình, khi một ứng dụng thực thi su, nó sẽ chuyển quy trình sang miền SELinux với quyền truy cập không hạn chế, chẳng hạn như u:r:su:s0. Thông qua Root Profile, miền này có thể được chuyển sang miền tùy chỉnh, chẳng hạn như u:r:app1:s0 và một loạt quy tắc có thể được xác định cho miền này:

sh
type app1
+enforce app1
+typeattribute app1 mlstrustedsubject
+allow app1 * * *
type app1
+enforce app1
+typeattribute app1 mlstrustedsubject
+allow app1 * * *

Lưu ý rằng quy tắc allow app1 * * * chỉ được sử dụng cho mục đích minh họa. Trong thực tế, quy tắc này không nên được sử dụng rộng rãi vì nó không khác nhiều so với chế độ cho phép.

Escalation

Nếu cấu hình của Root Profile không được đặt đúng cách, một tình huống escalation (leo thang) có thể xảy ra: các hạn chế do Root Profile áp đặt có thể vô tình bị lỗi.

Ví dụ: nếu bạn cấp quyền root cho người dùng shell ADB (đây là trường hợp phổ biến), sau đó bạn cấp quyền root cho một ứng dụng thông thường nhưng định cấu hình cấu hình gốc của nó bằng UID 2000 (là UID của người dùng shell ADB) , ứng dụng có thể có được quyền truy cập root đầy đủ bằng cách thực hiện lệnh su hai lần:

  1. Lần thực thi su đầu tiên phải tuân theo sự thực thi của App Profile và sẽ chuyển sang UID 2000 (adb shell) thay vì 0 (root).
  2. Lần thực thi su thứ hai, vì UID là 2000 và bạn đã cấp quyền truy cập root cho UID 2000 (adb shell) trong cấu hình, ứng dụng sẽ có toàn quyền root.

Ghi chú

Hành vi này hoàn toàn được mong đợi và không phải là lỗi. Vì vậy, chúng tôi khuyến nghị như sau:

Nếu bạn thực sự cần cấp quyền root cho ADB (ví dụ: với tư cách là nhà phát triển), bạn không nên thay đổi UID thành 2000 khi định cấu hình Root Profile. Sử dụng 1000 (hệ thống) sẽ là lựa chọn tốt hơn.

Non-Root Profile

Umount Modules

KernelSU cung cấp một cơ chế systemless để sửa đổi các phân vùng hệ thống, đạt được thông qua việc gắn overlayfs. Tuy nhiên, một số ứng dụng có thể nhạy cảm với hành vi đó. Do đó, chúng ta có thể dỡ bỏ các mô-đun được gắn trên các ứng dụng này bằng cách đặt tùy chọn "umount modules".

Ngoài ra, giao diện cài đặt của trình quản lý KernelSU cung cấp một công tắc cho "umount modules by default". Theo mặc định, công tắc này được bật, có nghĩa là KernelSU hoặc một số mô-đun sẽ hủy tải các mô-đun cho ứng dụng này trừ khi áp dụng cài đặt bổ sung. Nếu bạn không thích cài đặt này hoặc nếu nó ảnh hưởng đến một số ứng dụng nhất định, bạn có các tùy chọn sau:

  1. Giữ nút chuyển cho "umount modules by default" và tắt riêng tùy chọn "umount modules" trong App Profile đối với các ứng dụng yêu cầu tải mô-đun (hoạt động như "whitelist").
  2. Tắt khóa chuyển cho "umount modules by default" và bật riêng tùy chọn "umount modules" trong App Profile cho các ứng dụng yêu cầu dỡ bỏ mô-đun (hoạt động như "blacklist").

INFO

Trong các thiết bị sử dụng kernel phiên bản 5.10 trở lên, kernel thực hiện việc dỡ tải các mô-đun. Tuy nhiên, đối với các thiết bị chạy phiên bản kernel dưới 5.10, công tắc này chỉ đơn thuần là một tùy chọn cấu hình và bản thân KernelSU không thực hiện bất kỳ hành động nào. Một số mô-đun, chẳng hạn như Zygisksu, có thể sử dụng công tắc này để xác định xem có cần thiết phải dỡ bỏ mô-đun hay không.

`,46),l=[a];function p(h,e,i,r,g,u){return c(),o("div",null,l)}const E=n(t,[["render",p]]);export{d as __pageData,E as default}; diff --git a/assets/vi_VN_guide_app-profile.md.0b09a7b5.lean.js b/assets/vi_VN_guide_app-profile.md.0b09a7b5.lean.js new file mode 100644 index 000000000000..f4095de61373 --- /dev/null +++ b/assets/vi_VN_guide_app-profile.md.0b09a7b5.lean.js @@ -0,0 +1 @@ +import{_ as n,o as c,c as o,Q as s}from"./chunks/framework.ec8f7e8e.js";const d=JSON.parse('{"title":"App Profile","description":"","frontmatter":{},"headers":[],"relativePath":"vi_VN/guide/app-profile.md","filePath":"vi_VN/guide/app-profile.md"}'),t={name:"vi_VN/guide/app-profile.md"},a=s("",46),l=[a];function p(h,e,i,r,g,u){return c(),o("div",null,l)}const E=n(t,[["render",p]]);export{d as __pageData,E as default}; diff --git a/assets/vi_VN_guide_app-profile.md.37d09526.js b/assets/vi_VN_guide_app-profile.md.37d09526.js deleted file mode 100644 index 080c2bfcc8bf..000000000000 --- a/assets/vi_VN_guide_app-profile.md.37d09526.js +++ /dev/null @@ -1,5 +0,0 @@ -import{_ as n,o as c,c as o,O as t}from"./chunks/framework.43781440.js";const y=JSON.parse('{"title":"App Profile","description":"","frontmatter":{},"headers":[],"relativePath":"vi_VN/guide/app-profile.md","filePath":"vi_VN/guide/app-profile.md"}'),h={name:"vi_VN/guide/app-profile.md"},s=t(`

App Profile

App Profile là một cơ chế do KernelSU cung cấp để tùy chỉnh cấu hình của các ứng dụng khác nhau.

Đối với các ứng dụng được cấp quyền root (tức là có thể sử dụng su), App Profile cũng có thể được gọi là Root Profile. Nó cho phép tùy chỉnh các quy tắc uid, gid, groups, capabilitiesSELinux của lệnh su, do đó hạn chế các đặc quyền của người dùng root. Ví dụ: nó có thể chỉ cấp quyền mạng cho các ứng dụng tường lửa trong khi từ chối quyền truy cập tệp hoặc có thể cấp quyền shell thay vì quyền truy cập root đầy đủ cho các ứng dụng đóng băng: giữ nguồn điện theo nguyên tắc đặc quyền tối thiểu.

Đối với các ứng dụng thông thường không có quyền root, App Profile có thể kiểm soát hành vi của hệ thống kernel và mô-đun đối với các ứng dụng này. Ví dụ, nó có thể xác định liệu các sửa đổi do mô-đun tạo ra có nên được giải quyết hay không. Hệ thống kernel và mô-đun có thể đưa ra quyết định dựa trên cấu hình này, chẳng hạn như thực hiện các hoạt động tương tự như "hiding"

Root Profile

UID, GID, và Groups

Hệ thống Linux có hai khái niệm: người dùng (user) và nhóm (group). Mỗi người dùng có một user ID (UID) và một người dùng có thể thuộc nhiều nhóm, mỗi nhóm có group ID (GID) riêng. Những ID này được sử dụng để xác định người dùng trong hệ thống và xác định tài nguyên hệ thống nào họ có thể truy cập.

Người dùng có UID bằng 0 được gọi là người dùng root và các nhóm có GID bằng 0 được gọi là nhóm root. Nhóm người dùng root thường giữ các đặc quyền hệ thống cao nhất.

Trong trường hợp hệ thống Android, mỗi ứng dụng là một người dùng riêng biệt (không bao gồm các trường hợp UID dùng chung) với một UID duy nhất. Ví dụ: 0 đại diện cho người dùng root, 1000 đại diện cho system, 2000 đại diện cho ADB shell và các UID từ 10000 đến 19999 đại diện cho các ứng dụng thông thường.

INFO

Ở đây, UID được đề cập không giống với khái niệm nhiều người dùng hoặc hồ sơ công việc (Work profile) trong hệ thống Android. Hồ sơ công việc thực sự được triển khai bằng cách phân vùng phạm vi UID. Ví dụ: 10000-19999 đại diện cho người dùng chính, trong khi 110000-119999 đại diện cho hồ sơ công việc. Mỗi ứng dụng thông thường trong số đó đều có UID riêng.

Mỗi ứng dụng có thể có nhiều nhóm, với GID đại diện cho nhóm chính, thường khớp với UID. Các nhóm khác được gọi là nhóm bổ sung. Một số quyền nhất định được kiểm soát thông qua các nhóm, chẳng hạn như quyền truy cập mạng hoặc truy cập Bluetooth.

Ví dụ: nếu chúng ta thực thi lệnh id trong shell ADB, kết quả đầu ra có thể trông như thế này:

sh
oriole:/ $ id
-uid=2000(shell) gid=2000(shell) groups=2000(shell),1004(input),1007(log),1011(adb),1015(sdcard_rw),1028(sdcard_r),1078(ext_data_rw),1079(ext_obb_rw),3001(net_bt_admin),3002(net_bt),3003(inet),3006(net_bw_stats),3009(readproc),3011(uhid),3012(readtracefs) context=u:r:shell:s0

Ở đây, UID là 2000 và GID (ID nhóm chính) cũng là 2000. Ngoài ra, nó thuộc một số nhóm bổ sung, chẳng hạn như inet (biểu thị khả năng tạo ổ cắm AF_INETAF_INET6) và sdcard_rw (biểu thị quyền đọc/ghi đối với thẻ SD).

Root Profile của KernelSU cho phép tùy chỉnh UID, GID và các nhóm cho quy trình gốc sau khi thực thi su. Ví dụ: Cấu hình gốc của ứng dụng gốc có thể đặt UID của nó thành 2000, có nghĩa là khi sử dụng su, các quyền thực tế của ứng dụng sẽ ở cấp shell ADB. Nhóm inet có thể bị xóa, ngăn lệnh su truy cập mạng.

Ghi chú

Hồ sơ ứng dụng chỉ kiểm soát các quyền của tiến trình gốc sau khi sử dụng su; nó không kiểm soát các quyền của ứng dụng. Nếu một ứng dụng đã yêu cầu quyền truy cập mạng, ứng dụng đó vẫn có thể truy cập mạng ngay cả khi không sử dụng su. Việc xóa nhóm inet khỏi su chỉ ngăn su truy cập mạng.

Root Profile được thực thi trong kernel và không dựa vào hành vi tự nguyện của các ứng dụng root, không giống như việc chuyển đổi người dùng hoặc nhóm thông qua su, việc cấp quyền su hoàn toàn phụ thuộc vào người dùng chứ không phải nhà phát triển.

Capabilities

Capabilities (khả năng) là một cơ chế phân tách đặc quyền trong Linux.

Với mục đích thực hiện kiểm tra quyền, việc triển khai UNIX truyền thống phân biệt hai loại quy trình: quy trình đặc quyền (có ID người dùng hiệu quả là 0, được gọi là siêu người dùng hoặc root) và quy trình không có đặc quyền (có UID hiệu dụng khác 0). Các quy trình đặc quyền bỏ qua tất cả các bước kiểm tra quyền của kernel, trong khi các quy trình không có đặc quyền phải chịu sự kiểm tra quyền đầy đủ dựa trên thông tin xác thực của quy trình (thường là: UID hiệu quả, GID hiệu quả và danh sách nhóm bổ sung).

Bắt đầu với Linux 2.2, Linux chia các đặc quyền truyền thống được liên kết với siêu người dùng thành các đơn vị riêng biệt, được gọi là các khả năng, có thể được bật và tắt một cách độc lập.

Mỗi Khả năng đại diện cho một hoặc nhiều đặc quyền. Ví dụ: CAP_DAC_READ_SEARCH thể hiện khả năng bỏ qua việc kiểm tra quyền để đọc tệp cũng như quyền đọc và thực thi thư mục. Nếu người dùng có UID hiệu dụng là 0 (người dùng root) thiếu khả năng CAP_DAC_READ_SEARCH hoặc cao hơn, điều này có nghĩa là ngay cả khi họ là root, họ không thể tùy ý đọc tệp.

Cấu hình gốc của KernelSU cho phép tùy chỉnh các Khả năng của tiến trình gốc sau khi thực thi su, nhờ đó đạt được việc cấp một phần "quyền root". Không giống như UID và GID đã nói ở trên, một số ứng dụng gốc nhất định yêu cầu UID là 0 sau khi sử dụng su. Trong những trường hợp như vậy, việc giới hạn Khả năng của người dùng root này bằng UID 0 có thể hạn chế các hoạt động được phép của họ.

Rất Khuyến Nghị

Capabilities của Linux tài liệu chính thức cung cấp giải thích chi tiết về các khả năng mà mỗi Capabilities thể hiện. Nếu bạn có ý định tùy chỉnh Capabilities, bạn nên đọc tài liệu này trước.

SELinux

SELinux là một cơ chế Kiểm Soát Truy Cập Bắt Buộc (Mandatory Access Control: MAC) mạnh mẽ. Nó hoạt động theo nguyên tắc từ chối mặc định: bất kỳ hành động nào không được cho phép rõ ràng đều bị từ chối.

SELinux có thể chạy ở hai chế độ chung:

  1. Chế độ cho phép (Permissive mode): Các sự kiện từ chối được ghi lại nhưng không được thực thi.
  2. Chế độ thực thi (Enforcing mode): Các sự kiện từ chối được ghi lại và thực thi.

Cảnh báo

Các hệ thống Android hiện đại phụ thuộc rất nhiều vào SELinux để đảm bảo an ninh hệ thống tổng thể. Chúng tôi khuyên bạn không nên sử dụng bất kỳ hệ thống tùy chỉnh nào chạy ở "chế độ cho phép" vì nó không mang lại lợi thế đáng kể nào so với hệ thống mở hoàn toàn.

Việc giải thích khái niệm đầy đủ về SELinux rất phức tạp và nằm ngoài phạm vi của tài liệu này. Trước tiên nên hiểu hoạt động của nó thông qua các tài nguyên sau:

  1. Wikipedia
  2. Red Hat: What Is SELinux?
  3. ArchLinux: SELinux

Root Profile của KernelSU cho phép tùy chỉnh ngữ cảnh SELinux của tiến trình gốc sau khi thực thi su. Các quy tắc kiểm soát truy cập cụ thể có thể được đặt cho bối cảnh này để cho phép kiểm soát chi tiết hơn các quyền .

Trong các trường hợp điển hình, khi một ứng dụng thực thi su, nó sẽ chuyển quy trình sang miền SELinux với quyền truy cập không hạn chế, chẳng hạn như u:r:su:s0. Thông qua Root Profile, miền này có thể được chuyển sang miền tùy chỉnh, chẳng hạn như u:r:app1:s0 và một loạt quy tắc có thể được xác định cho miền này:

sh
type app1
-enforce app1
-typeattribute app1 mlstrustedsubject
-allow app1 * * *

Lưu ý rằng quy tắc allow app1 * * * chỉ được sử dụng cho mục đích minh họa. Trong thực tế, quy tắc này không nên được sử dụng rộng rãi vì nó không khác nhiều so với chế độ cho phép.

Escalation

Nếu cấu hình của Root Profile không được đặt đúng cách, một tình huống escalation (leo thang) có thể xảy ra: các hạn chế do Root Profile áp đặt có thể vô tình bị lỗi.

Ví dụ: nếu bạn cấp quyền root cho người dùng shell ADB (đây là trường hợp phổ biến), sau đó bạn cấp quyền root cho một ứng dụng thông thường nhưng định cấu hình cấu hình gốc của nó bằng UID 2000 (là UID của người dùng shell ADB) , ứng dụng có thể có được quyền truy cập root đầy đủ bằng cách thực hiện lệnh su hai lần:

  1. Lần thực thi su đầu tiên phải tuân theo sự thực thi của App Profile và sẽ chuyển sang UID 2000 (adb shell) thay vì 0 (root).
  2. Lần thực thi su thứ hai, vì UID là 2000 và bạn đã cấp quyền truy cập root cho UID 2000 (adb shell) trong cấu hình, ứng dụng sẽ có toàn quyền root.

Ghi chú

Hành vi này hoàn toàn được mong đợi và không phải là lỗi. Vì vậy, chúng tôi khuyến nghị như sau:

Nếu bạn thực sự cần cấp quyền root cho ADB (ví dụ: với tư cách là nhà phát triển), bạn không nên thay đổi UID thành 2000 khi định cấu hình Root Profile. Sử dụng 1000 (hệ thống) sẽ là lựa chọn tốt hơn.

Non-Root Profile

Umount Modules

KernelSU cung cấp một cơ chế systemless để sửa đổi các phân vùng hệ thống, đạt được thông qua việc gắn overlayfs. Tuy nhiên, một số ứng dụng có thể nhạy cảm với hành vi đó. Do đó, chúng ta có thể dỡ bỏ các mô-đun được gắn trên các ứng dụng này bằng cách đặt tùy chọn "umount modules".

Ngoài ra, giao diện cài đặt của trình quản lý KernelSU cung cấp một công tắc cho "umount modules by default". Theo mặc định, công tắc này được bật, có nghĩa là KernelSU hoặc một số mô-đun sẽ hủy tải các mô-đun cho ứng dụng này trừ khi áp dụng cài đặt bổ sung. Nếu bạn không thích cài đặt này hoặc nếu nó ảnh hưởng đến một số ứng dụng nhất định, bạn có các tùy chọn sau:

  1. Giữ nút chuyển cho "umount modules by default" và tắt riêng tùy chọn "umount modules" trong App Profile đối với các ứng dụng yêu cầu tải mô-đun (hoạt động như "whitelist").
  2. Tắt khóa chuyển cho "umount modules by default" và bật riêng tùy chọn "umount modules" trong App Profile cho các ứng dụng yêu cầu dỡ bỏ mô-đun (hoạt động như "blacklist").

INFO

Trong các thiết bị sử dụng kernel phiên bản 5.10 trở lên, kernel thực hiện việc dỡ tải các mô-đun. Tuy nhiên, đối với các thiết bị chạy phiên bản kernel dưới 5.10, công tắc này chỉ đơn thuần là một tùy chọn cấu hình và bản thân KernelSU không thực hiện bất kỳ hành động nào. Một số mô-đun, chẳng hạn như Zygisksu, có thể sử dụng công tắc này để xác định xem có cần thiết phải dỡ bỏ mô-đun hay không.

`,46),a=[s];function i(l,e,p,r,g,u){return c(),o("div",null,a)}const D=n(h,[["render",i]]);export{y as __pageData,D as default}; diff --git a/assets/vi_VN_guide_app-profile.md.37d09526.lean.js b/assets/vi_VN_guide_app-profile.md.37d09526.lean.js deleted file mode 100644 index 5edcd168ecfe..000000000000 --- a/assets/vi_VN_guide_app-profile.md.37d09526.lean.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as n,o as c,c as o,O as t}from"./chunks/framework.43781440.js";const y=JSON.parse('{"title":"App Profile","description":"","frontmatter":{},"headers":[],"relativePath":"vi_VN/guide/app-profile.md","filePath":"vi_VN/guide/app-profile.md"}'),h={name:"vi_VN/guide/app-profile.md"},s=t("",46),a=[s];function i(l,e,p,r,g,u){return c(),o("div",null,a)}const D=n(h,[["render",i]]);export{y as __pageData,D as default}; diff --git a/assets/vi_VN_guide_difference-with-magisk.md.efd07233.js b/assets/vi_VN_guide_difference-with-magisk.md.57b7c7d3.js similarity index 96% rename from assets/vi_VN_guide_difference-with-magisk.md.efd07233.js rename to assets/vi_VN_guide_difference-with-magisk.md.57b7c7d3.js index 7554a8589a7d..be0d83a979db 100644 --- a/assets/vi_VN_guide_difference-with-magisk.md.efd07233.js +++ b/assets/vi_VN_guide_difference-with-magisk.md.57b7c7d3.js @@ -1 +1 @@ -import{_ as n,o as i,c as e,O as t}from"./chunks/framework.43781440.js";const k=JSON.parse('{"title":"Sự khác biệt với Magisk","description":"","frontmatter":{},"headers":[],"relativePath":"vi_VN/guide/difference-with-magisk.md","filePath":"vi_VN/guide/difference-with-magisk.md"}'),c={name:"vi_VN/guide/difference-with-magisk.md"},h=t('

Sự khác biệt với Magisk

Mặc dù có nhiều điểm tương đồng giữa mô-đun KernelSU và mô-đun Magisk nhưng chắc chắn vẫn có một số khác biệt do cơ chế triển khai hoàn toàn khác nhau của chúng. Nếu muốn mô-đun của mình chạy trên cả Magisk và KernelSU, bạn phải hiểu những khác biệt này.

Điểm tương đồng

  • Định dạng file mô-đun: đều sử dụng định dạng zip để sắp xếp các mô-đun và định dạng của các mô-đun gần như giống nhau
  • Thư mục cài đặt mô-đun: cả hai đều nằm trong /data/adb/modules
  • systemless: cả hai đều hỗ trợ sửa đổi /system theo cách không có hệ thống thông qua các mô-đun
  • post-fs-data.sh: thời gian thực hiện và ngữ nghĩa hoàn toàn giống nhau
  • service.sh: thời gian thực hiện và ngữ nghĩa hoàn toàn giống nhau
  • system.prop: hoàn toàn giống nhau
  • sepolicy.rule: hoàn toàn giống nhau
  • BusyBox: các tập lệnh được chạy trong BusyBox với "standalone mode" được bật trong cả hai trường hợp

Điểm khác biệt

Trước khi hiểu sự khác biệt, bạn cần biết cách phân biệt mô-đun của bạn đang chạy trong KernelSU hay Magisk. Bạn có thể sử dụng biến môi trường KSU để phân biệt nó ở tất cả những nơi bạn có thể chạy tập lệnh mô-đun (customize.sh, post-fs-data.sh, service.sh). Trong KernelSU, biến môi trường này sẽ được đặt thành true.

Dưới đây là một số khác biệt:

  • Không thể cài đặt các mô-đun KernelSU ở chế độ Recovery.
  • Các mô-đun KernelSU không có hỗ trợ tích hợp cho Zygisk (nhưng bạn có thể sử dụng các mô-đun Zygisk thông qua ZygiskNext.
  • Phương pháp thay thế hoặc xóa file trong module KernelSU hoàn toàn khác với Magisk. KernelSU không hỗ trợ phương thức .replace. Thay vào đó, bạn cần tạo một file cùng tên với mknod filename c 0 0 để xóa file tương ứng.
  • Các thư mục của BusyBox khác nhau. BusyBox tích hợp trong KernelSU nằm ở /data/adb/ksu/bin/busybox, trong khi ở Magisk nó nằm ở /data/adb/magisk/busybox. Lưu ý rằng đây là hoạt động nội bộ của KernelSU và có thể thay đổi trong tương lai!
  • KernelSU không hỗ trợ file .replace; tuy nhiên, KernelSU hỗ trợ biến REMOVEREPLACE để xóa hoặc thay thế các tệp và thư mục.
  • KernelSU thêm giai đoạn boot-completed để chạy một số script khi khởi động xong.
  • KernelSU thêm giai đoạn post-mount để chạy một số tập lệnh sau khi gắn overlayfs
',8),a=[h];function o(g,s,l,r,d,u){return i(),e("div",null,a)}const b=n(c,[["render",o]]);export{k as __pageData,b as default}; +import{_ as n,o as i,c as e,Q as t}from"./chunks/framework.ec8f7e8e.js";const k=JSON.parse('{"title":"Sự khác biệt với Magisk","description":"","frontmatter":{},"headers":[],"relativePath":"vi_VN/guide/difference-with-magisk.md","filePath":"vi_VN/guide/difference-with-magisk.md"}'),c={name:"vi_VN/guide/difference-with-magisk.md"},h=t('

Sự khác biệt với Magisk

Mặc dù có nhiều điểm tương đồng giữa mô-đun KernelSU và mô-đun Magisk nhưng chắc chắn vẫn có một số khác biệt do cơ chế triển khai hoàn toàn khác nhau của chúng. Nếu muốn mô-đun của mình chạy trên cả Magisk và KernelSU, bạn phải hiểu những khác biệt này.

Điểm tương đồng

  • Định dạng file mô-đun: đều sử dụng định dạng zip để sắp xếp các mô-đun và định dạng của các mô-đun gần như giống nhau
  • Thư mục cài đặt mô-đun: cả hai đều nằm trong /data/adb/modules
  • systemless: cả hai đều hỗ trợ sửa đổi /system theo cách không có hệ thống thông qua các mô-đun
  • post-fs-data.sh: thời gian thực hiện và ngữ nghĩa hoàn toàn giống nhau
  • service.sh: thời gian thực hiện và ngữ nghĩa hoàn toàn giống nhau
  • system.prop: hoàn toàn giống nhau
  • sepolicy.rule: hoàn toàn giống nhau
  • BusyBox: các tập lệnh được chạy trong BusyBox với "standalone mode" được bật trong cả hai trường hợp

Điểm khác biệt

Trước khi hiểu sự khác biệt, bạn cần biết cách phân biệt mô-đun của bạn đang chạy trong KernelSU hay Magisk. Bạn có thể sử dụng biến môi trường KSU để phân biệt nó ở tất cả những nơi bạn có thể chạy tập lệnh mô-đun (customize.sh, post-fs-data.sh, service.sh). Trong KernelSU, biến môi trường này sẽ được đặt thành true.

Dưới đây là một số khác biệt:

  • Không thể cài đặt các mô-đun KernelSU ở chế độ Recovery.
  • Các mô-đun KernelSU không có hỗ trợ tích hợp cho Zygisk (nhưng bạn có thể sử dụng các mô-đun Zygisk thông qua ZygiskNext.
  • Phương pháp thay thế hoặc xóa file trong module KernelSU hoàn toàn khác với Magisk. KernelSU không hỗ trợ phương thức .replace. Thay vào đó, bạn cần tạo một file cùng tên với mknod filename c 0 0 để xóa file tương ứng.
  • Các thư mục của BusyBox khác nhau. BusyBox tích hợp trong KernelSU nằm ở /data/adb/ksu/bin/busybox, trong khi ở Magisk nó nằm ở /data/adb/magisk/busybox. Lưu ý rằng đây là hoạt động nội bộ của KernelSU và có thể thay đổi trong tương lai!
  • KernelSU không hỗ trợ file .replace; tuy nhiên, KernelSU hỗ trợ biến REMOVEREPLACE để xóa hoặc thay thế các tệp và thư mục.
  • KernelSU thêm giai đoạn boot-completed để chạy một số script khi khởi động xong.
  • KernelSU thêm giai đoạn post-mount để chạy một số tập lệnh sau khi gắn overlayfs
',8),a=[h];function o(g,s,l,r,d,u){return i(),e("div",null,a)}const b=n(c,[["render",o]]);export{k as __pageData,b as default}; diff --git a/assets/vi_VN_guide_difference-with-magisk.md.efd07233.lean.js b/assets/vi_VN_guide_difference-with-magisk.md.57b7c7d3.lean.js similarity index 72% rename from assets/vi_VN_guide_difference-with-magisk.md.efd07233.lean.js rename to assets/vi_VN_guide_difference-with-magisk.md.57b7c7d3.lean.js index 1996323d1168..90d836e19af3 100644 --- a/assets/vi_VN_guide_difference-with-magisk.md.efd07233.lean.js +++ b/assets/vi_VN_guide_difference-with-magisk.md.57b7c7d3.lean.js @@ -1 +1 @@ -import{_ as n,o as i,c as e,O as t}from"./chunks/framework.43781440.js";const k=JSON.parse('{"title":"Sự khác biệt với Magisk","description":"","frontmatter":{},"headers":[],"relativePath":"vi_VN/guide/difference-with-magisk.md","filePath":"vi_VN/guide/difference-with-magisk.md"}'),c={name:"vi_VN/guide/difference-with-magisk.md"},h=t("",8),a=[h];function o(g,s,l,r,d,u){return i(),e("div",null,a)}const b=n(c,[["render",o]]);export{k as __pageData,b as default}; +import{_ as n,o as i,c as e,Q as t}from"./chunks/framework.ec8f7e8e.js";const k=JSON.parse('{"title":"Sự khác biệt với Magisk","description":"","frontmatter":{},"headers":[],"relativePath":"vi_VN/guide/difference-with-magisk.md","filePath":"vi_VN/guide/difference-with-magisk.md"}'),c={name:"vi_VN/guide/difference-with-magisk.md"},h=t("",8),a=[h];function o(g,s,l,r,d,u){return i(),e("div",null,a)}const b=n(c,[["render",o]]);export{k as __pageData,b as default}; diff --git a/assets/vi_VN_guide_faq.md.e9230071.js b/assets/vi_VN_guide_faq.md.510e2971.js similarity index 98% rename from assets/vi_VN_guide_faq.md.e9230071.js rename to assets/vi_VN_guide_faq.md.510e2971.js index 9a13ceb60124..23efc2abb841 100644 --- a/assets/vi_VN_guide_faq.md.e9230071.js +++ b/assets/vi_VN_guide_faq.md.510e2971.js @@ -1 +1 @@ -import{_ as n,o as e,c as h,O as t}from"./chunks/framework.43781440.js";const b=JSON.parse('{"title":"FAQ","description":"","frontmatter":{},"headers":[],"relativePath":"vi_VN/guide/faq.md","filePath":"vi_VN/guide/faq.md"}'),a={name:"vi_VN/guide/faq.md"},o=t('

FAQ

KernelSU có hỗ trợ thiết bị của tôi không?

Đầu tiên, thiết bị của bạn sẽ có thể mở khóa bootloader. Nếu không thể thì nó không được hỗ trợ.

Sau đó, cài đặt Ứng dụng KernelSU manager vào thiết bị của bạn và mở nó, nếu nó hiển thị Unsupported thì thiết bị của bạn chưa được hỗ trợ ngay, nhưng bạn có thể tạo nguồn kernel và tích hợp KernelSU để nó hoạt động hoặc sử dụng unofficially-support-devices.

KernelSU có cần mở khóa Bootloader không?

Chắc chắn có.

KernelSU có hỗ trợ các mô-đun không?

Có, nhưng đây là phiên bản đầu tiên nên có thể bị lỗi. Đợi nó ổn định nhé 😃

KernelSU có hỗ trợ Xposed không?

Có, DreamlandTaiChi hiện đã hoạt động. Đối với LSPosed, bạn có thể làm cho nó hoạt động bằng ZygiskNext

KernelSU có hỗ trợ Zygisk không?

KernelSU không có hỗ trợ Zygisk tích hợp sẵn nhưng thay vào đó, bạn có thể sử dụng ZygiskNext.

KernelSU có tương thích với Magisk không?

Hệ thống mô-đun của KernelSU xung đột với magic mount của Magisk, nếu có bất kỳ mô-đun nào được kích hoạt trong KernelSU thì toàn bộ Magisk sẽ không hoạt động.

Nhưng nếu bạn chỉ sử dụng su của KernelSU thì nó sẽ hoạt động tốt với Magisk: KernelSU sửa đổi kernel và Magisk sửa đổi ramdisk, chúng có thể hoạt động cùng nhau.

KernelSU sẽ thay thế Magisk?

Chúng tôi không nghĩ như vậy và đó không phải là mục tiêu của chúng tôi. Magisk đủ tốt cho giải pháp root userspace và nó sẽ tồn tại lâu dài. Mục tiêu của KernelSU là cung cấp giao diện kernel cho người dùng chứ không thay thế Magisk.

KernelSU có thể hỗ trợ các thiết bị không phải GKI không?

Điều đó là có thể. Nhưng bạn nên tải nguồn kernel về và tích hợp KernelSU vào source tree rồi tự biên dịch kernel.

KernelSU có thể hỗ trợ các thiết bị dưới Android 12 không?

Chính kernel của thiết bị ảnh hưởng đến khả năng tương thích của KernelSU và nó không liên quan gì đến phiên bản Android. Hạn chế duy nhất là các thiết bị chạy Android 12 phải là kernel 5.10+(thiết bị GKI). Vì thế:

  1. Các thiết bị chạy Android 12 phải được hỗ trợ.
  2. Các thiết bị có kernel cũ (Một số thiết bị Android 12 cũng là kernel cũ) tương thích (Bạn nên tự build kernel)

KernelSU có thể hỗ trợ kernel cũ không?

Có thể, KernelSU hiện đã được backport sang kernel 4.14, đối với kernel cũ hơn, bạn cần backport nó một cách cẩn thận và PR rất đáng hoan nghênh!

Làm cách nào để tích hợp KernelSU cho kernel cũ?

Vui lòng tham khảo hướng dẫn

Tại sao phiên bản Android của tôi là 13 và kernel hiển thị "android12-5.10"?

Phiên bản Kernel không liên quan gì đến phiên bản Android, nếu bạn cần flash kernel thì dùng luôn phiên bản kernel, phiên bản Android không quá quan trọng.

Đã có mount namespace --mount-master/global trên KernelSU chưa?

Hiện tại thì không (có thể có trong tương lai), nhưng có nhiều cách để chuyển sang global mount namespace một cách thủ công, chẳng hạn như:

  1. nsenter -t 1 -m sh để lấy shell trong global mount namespace.
  2. Thêm nsenter --mount=/proc/1/ns/mnt vào lệnh bạn muốn thực thi, sau đó lệnh được thực thi trong global mount namespace. KernelSU cũng sử dụng cách này

Tôi là GKI1.0, tôi có thể sử dụng cái này không?

GKI1 khác hoàn toàn với GKI2, bạn phải tự biên dịch kernel.

',33),r=[o];function c(i,l,g,k,s,d){return e(),h("div",null,r)}const p=n(a,[["render",c]]);export{b as __pageData,p as default}; +import{_ as n,o as e,c as h,Q as t}from"./chunks/framework.ec8f7e8e.js";const b=JSON.parse('{"title":"FAQ","description":"","frontmatter":{},"headers":[],"relativePath":"vi_VN/guide/faq.md","filePath":"vi_VN/guide/faq.md"}'),a={name:"vi_VN/guide/faq.md"},o=t('

FAQ

KernelSU có hỗ trợ thiết bị của tôi không?

Đầu tiên, thiết bị của bạn sẽ có thể mở khóa bootloader. Nếu không thể thì nó không được hỗ trợ.

Sau đó, cài đặt Ứng dụng KernelSU manager vào thiết bị của bạn và mở nó, nếu nó hiển thị Unsupported thì thiết bị của bạn chưa được hỗ trợ ngay, nhưng bạn có thể tạo nguồn kernel và tích hợp KernelSU để nó hoạt động hoặc sử dụng unofficially-support-devices.

KernelSU có cần mở khóa Bootloader không?

Chắc chắn có.

KernelSU có hỗ trợ các mô-đun không?

Có, nhưng đây là phiên bản đầu tiên nên có thể bị lỗi. Đợi nó ổn định nhé 😃

KernelSU có hỗ trợ Xposed không?

Có, DreamlandTaiChi hiện đã hoạt động. Đối với LSPosed, bạn có thể làm cho nó hoạt động bằng ZygiskNext

KernelSU có hỗ trợ Zygisk không?

KernelSU không có hỗ trợ Zygisk tích hợp sẵn nhưng thay vào đó, bạn có thể sử dụng ZygiskNext.

KernelSU có tương thích với Magisk không?

Hệ thống mô-đun của KernelSU xung đột với magic mount của Magisk, nếu có bất kỳ mô-đun nào được kích hoạt trong KernelSU thì toàn bộ Magisk sẽ không hoạt động.

Nhưng nếu bạn chỉ sử dụng su của KernelSU thì nó sẽ hoạt động tốt với Magisk: KernelSU sửa đổi kernel và Magisk sửa đổi ramdisk, chúng có thể hoạt động cùng nhau.

KernelSU sẽ thay thế Magisk?

Chúng tôi không nghĩ như vậy và đó không phải là mục tiêu của chúng tôi. Magisk đủ tốt cho giải pháp root userspace và nó sẽ tồn tại lâu dài. Mục tiêu của KernelSU là cung cấp giao diện kernel cho người dùng chứ không thay thế Magisk.

KernelSU có thể hỗ trợ các thiết bị không phải GKI không?

Điều đó là có thể. Nhưng bạn nên tải nguồn kernel về và tích hợp KernelSU vào source tree rồi tự biên dịch kernel.

KernelSU có thể hỗ trợ các thiết bị dưới Android 12 không?

Chính kernel của thiết bị ảnh hưởng đến khả năng tương thích của KernelSU và nó không liên quan gì đến phiên bản Android. Hạn chế duy nhất là các thiết bị chạy Android 12 phải là kernel 5.10+(thiết bị GKI). Vì thế:

  1. Các thiết bị chạy Android 12 phải được hỗ trợ.
  2. Các thiết bị có kernel cũ (Một số thiết bị Android 12 cũng là kernel cũ) tương thích (Bạn nên tự build kernel)

KernelSU có thể hỗ trợ kernel cũ không?

Có thể, KernelSU hiện đã được backport sang kernel 4.14, đối với kernel cũ hơn, bạn cần backport nó một cách cẩn thận và PR rất đáng hoan nghênh!

Làm cách nào để tích hợp KernelSU cho kernel cũ?

Vui lòng tham khảo hướng dẫn

Tại sao phiên bản Android của tôi là 13 và kernel hiển thị "android12-5.10"?

Phiên bản Kernel không liên quan gì đến phiên bản Android, nếu bạn cần flash kernel thì dùng luôn phiên bản kernel, phiên bản Android không quá quan trọng.

Đã có mount namespace --mount-master/global trên KernelSU chưa?

Hiện tại thì không (có thể có trong tương lai), nhưng có nhiều cách để chuyển sang global mount namespace một cách thủ công, chẳng hạn như:

  1. nsenter -t 1 -m sh để lấy shell trong global mount namespace.
  2. Thêm nsenter --mount=/proc/1/ns/mnt vào lệnh bạn muốn thực thi, sau đó lệnh được thực thi trong global mount namespace. KernelSU cũng sử dụng cách này

Tôi là GKI1.0, tôi có thể sử dụng cái này không?

GKI1 khác hoàn toàn với GKI2, bạn phải tự biên dịch kernel.

',33),r=[o];function c(i,l,g,k,s,d){return e(),h("div",null,r)}const p=n(a,[["render",c]]);export{b as __pageData,p as default}; diff --git a/assets/vi_VN_guide_faq.md.e9230071.lean.js b/assets/vi_VN_guide_faq.md.510e2971.lean.js similarity index 67% rename from assets/vi_VN_guide_faq.md.e9230071.lean.js rename to assets/vi_VN_guide_faq.md.510e2971.lean.js index c0e46f56dd28..904b7179deed 100644 --- a/assets/vi_VN_guide_faq.md.e9230071.lean.js +++ b/assets/vi_VN_guide_faq.md.510e2971.lean.js @@ -1 +1 @@ -import{_ as n,o as e,c as h,O as t}from"./chunks/framework.43781440.js";const b=JSON.parse('{"title":"FAQ","description":"","frontmatter":{},"headers":[],"relativePath":"vi_VN/guide/faq.md","filePath":"vi_VN/guide/faq.md"}'),a={name:"vi_VN/guide/faq.md"},o=t("",33),r=[o];function c(i,l,g,k,s,d){return e(),h("div",null,r)}const p=n(a,[["render",c]]);export{b as __pageData,p as default}; +import{_ as n,o as e,c as h,Q as t}from"./chunks/framework.ec8f7e8e.js";const b=JSON.parse('{"title":"FAQ","description":"","frontmatter":{},"headers":[],"relativePath":"vi_VN/guide/faq.md","filePath":"vi_VN/guide/faq.md"}'),a={name:"vi_VN/guide/faq.md"},o=t("",33),r=[o];function c(i,l,g,k,s,d){return e(),h("div",null,r)}const p=n(a,[["render",c]]);export{b as __pageData,p as default}; diff --git a/assets/vi_VN_guide_hidden-features.md.fca4632f.js b/assets/vi_VN_guide_hidden-features.md.f00f568c.js similarity index 86% rename from assets/vi_VN_guide_hidden-features.md.fca4632f.js rename to assets/vi_VN_guide_hidden-features.md.f00f568c.js index f8bbe9553dc4..9fafb2dad6be 100644 --- a/assets/vi_VN_guide_hidden-features.md.fca4632f.js +++ b/assets/vi_VN_guide_hidden-features.md.f00f568c.js @@ -1 +1 @@ -import{_ as e,o as t,c as a,O as n}from"./chunks/framework.43781440.js";const m=JSON.parse('{"title":"Tính Năng Ẩn","description":"","frontmatter":{},"headers":[],"relativePath":"vi_VN/guide/hidden-features.md","filePath":"vi_VN/guide/hidden-features.md"}'),c={name:"vi_VN/guide/hidden-features.md"},s=n('

Tính Năng Ẩn

.ksurc

Theo mặc định, /system/bin/sh tải /system/etc/mkshrc.

Bạn có thể tạo su tải tệp rc tùy chỉnh bằng cách tạo tệp /data/adb/ksu/.ksurc.

',4),r=[s];function o(d,i,h,_,u,l){return t(),a("div",null,r)}const f=e(c,[["render",o]]);export{m as __pageData,f as default}; +import{_ as e,o as t,c as a,Q as n}from"./chunks/framework.ec8f7e8e.js";const m=JSON.parse('{"title":"Tính Năng Ẩn","description":"","frontmatter":{},"headers":[],"relativePath":"vi_VN/guide/hidden-features.md","filePath":"vi_VN/guide/hidden-features.md"}'),c={name:"vi_VN/guide/hidden-features.md"},s=n('

Tính Năng Ẩn

.ksurc

Theo mặc định, /system/bin/sh tải /system/etc/mkshrc.

Bạn có thể tạo su tải tệp rc tùy chỉnh bằng cách tạo tệp /data/adb/ksu/.ksurc.

',4),r=[s];function o(d,i,h,_,u,l){return t(),a("div",null,r)}const f=e(c,[["render",o]]);export{m as __pageData,f as default}; diff --git a/assets/vi_VN_guide_hidden-features.md.fca4632f.lean.js b/assets/vi_VN_guide_hidden-features.md.f00f568c.lean.js similarity index 70% rename from assets/vi_VN_guide_hidden-features.md.fca4632f.lean.js rename to assets/vi_VN_guide_hidden-features.md.f00f568c.lean.js index 8f4bad94b115..2dad296edb8b 100644 --- a/assets/vi_VN_guide_hidden-features.md.fca4632f.lean.js +++ b/assets/vi_VN_guide_hidden-features.md.f00f568c.lean.js @@ -1 +1 @@ -import{_ as e,o as t,c as a,O as n}from"./chunks/framework.43781440.js";const m=JSON.parse('{"title":"Tính Năng Ẩn","description":"","frontmatter":{},"headers":[],"relativePath":"vi_VN/guide/hidden-features.md","filePath":"vi_VN/guide/hidden-features.md"}'),c={name:"vi_VN/guide/hidden-features.md"},s=n("",4),r=[s];function o(d,i,h,_,u,l){return t(),a("div",null,r)}const f=e(c,[["render",o]]);export{m as __pageData,f as default}; +import{_ as e,o as t,c as a,Q as n}from"./chunks/framework.ec8f7e8e.js";const m=JSON.parse('{"title":"Tính Năng Ẩn","description":"","frontmatter":{},"headers":[],"relativePath":"vi_VN/guide/hidden-features.md","filePath":"vi_VN/guide/hidden-features.md"}'),c={name:"vi_VN/guide/hidden-features.md"},s=n("",4),r=[s];function o(d,i,h,_,u,l){return t(),a("div",null,r)}const f=e(c,[["render",o]]);export{m as __pageData,f as default}; diff --git a/assets/vi_VN_guide_how-to-build.md.f5e5323c.js b/assets/vi_VN_guide_how-to-build.md.f5e5323c.js deleted file mode 100644 index d3432eb2b767..000000000000 --- a/assets/vi_VN_guide_how-to-build.md.f5e5323c.js +++ /dev/null @@ -1,4 +0,0 @@ -import{_ as s,o as n,c as a,O as l}from"./chunks/framework.43781440.js";const y=JSON.parse('{"title":"Làm thế nào để xây dựng KernelSU?","description":"","frontmatter":{},"headers":[],"relativePath":"vi_VN/guide/how-to-build.md","filePath":"vi_VN/guide/how-to-build.md"}'),e={name:"vi_VN/guide/how-to-build.md"},o=l(`

Làm thế nào để xây dựng KernelSU?

Trước tiên, bạn nên đọc tài liệu chính thức của Android để xây dựng kernel:

  1. Building Kernels
  2. GKI Release Builds

WARNING

Trang này dành cho thiết bị GKI, nếu bạn sử dụng kernel cũ, vui lòng tham khảo cách tích hợp KernelSU cho kernel cũ

Build Kernel

Đồng bộ hóa mã nguồn kernel

sh
repo init -u https://android.googlesource.com/kernel/manifest
-mv <kernel_manifest.xml> .repo/manifests
-repo init -m manifest.xml
-repo sync

<kernel_manifest.xml> là một tệp kê khai có thể xác định duy nhất một bản dựng, bạn có thể sử dụng tệp kê khai đó để thực hiện một bản dựng có thể dự đoán lại. Bạn nên tải xuống tệp kê khai từ Google GKI release builds

Build

Trước tiên, vui lòng kiểm tra tài liệu chính thức.

Ví dụ: chúng ta cần xây dựng kernel image aarch64:

sh
LTO=thin BUILD_CONFIG=common/build.config.gki.aarch64 build/build.sh

Đừng quên thêm cờ LTO=thin, nếu không quá trình xây dựng có thể thất bại nếu bộ nhớ máy tính của bạn nhỏ hơn 24Gb.

Bắt đầu từ Android 13, kernel được xây dựng bởi bazel:

sh
tools/bazel build --config=fast //common:kernel_aarch64_dist

Build Kernel với KernelSU

Nếu bạn có thể build kernel thành công thì việc xây dựng KernelSU thật dễ dàng, Chọn bất kỳ một lần chạy trong thư mục gốc nguồn Kernel:

  • Thẻ mới nhất (ổn định)
sh
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -
  • nhánh chính (dev)
sh
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -s main
  • Chọn thẻ (chẳng hạn như v0.5.2)
sh
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -s v0.5.2

Và sau đó build lại kernel và bạn sẽ có được image kernel với KernelSU!

`,24),t=[o];function p(r,c,i,h,d,u){return n(),a("div",null,t)}const g=s(e,[["render",p]]);export{y as __pageData,g as default}; diff --git a/assets/vi_VN_guide_how-to-build.md.f5e5323c.lean.js b/assets/vi_VN_guide_how-to-build.md.f5e5323c.lean.js deleted file mode 100644 index 79372a4fcb6a..000000000000 --- a/assets/vi_VN_guide_how-to-build.md.f5e5323c.lean.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as s,o as n,c as a,O as l}from"./chunks/framework.43781440.js";const y=JSON.parse('{"title":"Làm thế nào để xây dựng KernelSU?","description":"","frontmatter":{},"headers":[],"relativePath":"vi_VN/guide/how-to-build.md","filePath":"vi_VN/guide/how-to-build.md"}'),e={name:"vi_VN/guide/how-to-build.md"},o=l("",24),t=[o];function p(r,c,i,h,d,u){return n(),a("div",null,t)}const g=s(e,[["render",p]]);export{y as __pageData,g as default}; diff --git a/assets/vi_VN_guide_how-to-build.md.f65da7b9.js b/assets/vi_VN_guide_how-to-build.md.f65da7b9.js new file mode 100644 index 000000000000..c62345b26170 --- /dev/null +++ b/assets/vi_VN_guide_how-to-build.md.f65da7b9.js @@ -0,0 +1,7 @@ +import{_ as s,o as n,c as a,Q as l}from"./chunks/framework.ec8f7e8e.js";const E=JSON.parse('{"title":"Làm thế nào để xây dựng KernelSU?","description":"","frontmatter":{},"headers":[],"relativePath":"vi_VN/guide/how-to-build.md","filePath":"vi_VN/guide/how-to-build.md"}'),e={name:"vi_VN/guide/how-to-build.md"},o=l(`

Làm thế nào để xây dựng KernelSU?

Trước tiên, bạn nên đọc tài liệu chính thức của Android để xây dựng kernel:

  1. Building Kernels
  2. GKI Release Builds

WARNING

Trang này dành cho thiết bị GKI, nếu bạn sử dụng kernel cũ, vui lòng tham khảo cách tích hợp KernelSU cho kernel cũ

Build Kernel

Đồng bộ hóa mã nguồn kernel

sh
repo init -u https://android.googlesource.com/kernel/manifest
+mv <kernel_manifest.xml> .repo/manifests
+repo init -m manifest.xml
+repo sync
repo init -u https://android.googlesource.com/kernel/manifest
+mv <kernel_manifest.xml> .repo/manifests
+repo init -m manifest.xml
+repo sync

<kernel_manifest.xml> là một tệp kê khai có thể xác định duy nhất một bản dựng, bạn có thể sử dụng tệp kê khai đó để thực hiện một bản dựng có thể dự đoán lại. Bạn nên tải xuống tệp kê khai từ Google GKI release builds

Build

Trước tiên, vui lòng kiểm tra tài liệu chính thức.

Ví dụ: chúng ta cần xây dựng kernel image aarch64:

sh
LTO=thin BUILD_CONFIG=common/build.config.gki.aarch64 build/build.sh
LTO=thin BUILD_CONFIG=common/build.config.gki.aarch64 build/build.sh

Đừng quên thêm cờ LTO=thin, nếu không quá trình xây dựng có thể thất bại nếu bộ nhớ máy tính của bạn nhỏ hơn 24Gb.

Bắt đầu từ Android 13, kernel được xây dựng bởi bazel:

sh
tools/bazel build --config=fast //common:kernel_aarch64_dist
tools/bazel build --config=fast //common:kernel_aarch64_dist

Build Kernel với KernelSU

Nếu bạn có thể build kernel thành công thì việc xây dựng KernelSU thật dễ dàng, Chọn bất kỳ một lần chạy trong thư mục gốc nguồn Kernel:

  • Thẻ mới nhất (ổn định)
sh
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -
  • nhánh chính (dev)
sh
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -s main
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -s main
  • Chọn thẻ (chẳng hạn như v0.5.2)
sh
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -s v0.5.2
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -s v0.5.2

Và sau đó build lại kernel và bạn sẽ có được image kernel với KernelSU!

`,24),p=[o];function t(r,c,i,h,d,y){return n(),a("div",null,p)}const g=s(e,[["render",t]]);export{E as __pageData,g as default}; diff --git a/assets/vi_VN_guide_how-to-build.md.f65da7b9.lean.js b/assets/vi_VN_guide_how-to-build.md.f65da7b9.lean.js new file mode 100644 index 000000000000..26a285c0a85d --- /dev/null +++ b/assets/vi_VN_guide_how-to-build.md.f65da7b9.lean.js @@ -0,0 +1 @@ +import{_ as s,o as n,c as a,Q as l}from"./chunks/framework.ec8f7e8e.js";const E=JSON.parse('{"title":"Làm thế nào để xây dựng KernelSU?","description":"","frontmatter":{},"headers":[],"relativePath":"vi_VN/guide/how-to-build.md","filePath":"vi_VN/guide/how-to-build.md"}'),e={name:"vi_VN/guide/how-to-build.md"},o=l("",24),p=[o];function t(r,c,i,h,d,y){return n(),a("div",null,p)}const g=s(e,[["render",t]]);export{E as __pageData,g as default}; diff --git a/assets/vi_VN_guide_how-to-integrate-for-non-gki.md.9b3f1403.js b/assets/vi_VN_guide_how-to-integrate-for-non-gki.md.9b3f1403.js deleted file mode 100644 index ca9bfc596210..000000000000 --- a/assets/vi_VN_guide_how-to-integrate-for-non-gki.md.9b3f1403.js +++ /dev/null @@ -1,90 +0,0 @@ -import{_ as s,o as n,c as a,O as l}from"./chunks/framework.43781440.js";const A=JSON.parse('{"title":"Làm thế nào để tích hợp KernelSU vào thiết bị không sử dụng GKI ?","description":"","frontmatter":{},"headers":[],"relativePath":"vi_VN/guide/how-to-integrate-for-non-gki.md","filePath":"vi_VN/guide/how-to-integrate-for-non-gki.md"}'),e={name:"vi_VN/guide/how-to-integrate-for-non-gki.md"},p=l(`

Làm thế nào để tích hợp KernelSU vào thiết bị không sử dụng GKI ?

KernelSU có thể được tích hợp vào kernel không phải GKI và hiện tại nó đã được backport xuống 4.14, thậm chí cũng có thể chạy trên kernel thấp hơn 4.14.

Do các kernel không phải GKI bị phân mảnh nên chúng tôi không có cách build thống nhất, vì vậy chúng tôi không thể cung cấp các boot image không phải GKI. Nhưng bạn có thể tự build kernel với KernelSU được tích hợp vào.

Đầu tiên, bạn phải build được kernel từ nguồn có khả năng boot được , nếu kernel không có mã nguồn mở thì rất khó để chạy KernelSU cho thiết bị của bạn.

Nếu bạn có thể build kernel khởi động được, có hai cách để tích hợp KernelSU vào mã nguồn kernel:

  1. Tự động với kprobe
  2. Thủ công

Tích hợp vào kprobe

KernelSU sử dụng kprobe để thực hiện hook kernel, nếu kprobe chạy tốt trong kernel của bạn thì nên sử dụng cách này.

Đầu tiên, thêm KernelSU vào mã nguồn kernel của bạn:

  • Thẻ mới nhất (ổn định)
sh
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -
  • Nhánh chính (dev)
sh
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -s main
  • Chọn thẻ (chẳng hạn như v0.5.2)
sh
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -s v0.5.2

Sau đó, bạn nên kiểm tra xem kprobe có được bật trong config của bạn hay không, nếu không, vui lòng thêm các cấu hình sau vào:

CONFIG_KPROBES=y
-CONFIG_HAVE_KPROBES=y
-CONFIG_KPROBE_EVENTS=y

Rồi build lại kernel của bạn, KernelSU sẽ hoạt động ok.

Trong trường hợp kprobe chưa được bật, bạn có thể thêm CONFIG_MODULES=y vào kernel config. (Nếu vẫn không có hiệu lực thì hãy sử dụng make menuconfig rồi tìm các thành phần khác mà kprobe phụ thuộc).

Nhưng nếu bạn gặp bootloop khi tích hợp KernelSU thì có khả năng kprobe bị hỏng trong kernel, bạn nên fix lỗi kprobe trong mã nguồn hoặc dùng cách 2.

Chỉnh sửa mã nguồn kernel thủ công

Nếu kprobe không thể hoạt động trong kernel của bạn (có thể là lỗi do upstream hoặc kernel dưới bản 4.8), thì bạn có thể thử cách này:

Đầu tiên, thêm KernelSU vào mã nguồn kernel của bạn:

sh
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -

Sau đó, thêm lệnh gọi KernelSU vào mã nguồn kernel, đây là một patch bạn có thể tham khảo:

diff
diff --git a/fs/exec.c b/fs/exec.c
-index ac59664eaecf..bdd585e1d2cc 100644
---- a/fs/exec.c
-+++ b/fs/exec.c
-@@ -1890,11 +1890,14 @@ static int __do_execve_file(int fd, struct filename *filename,
- 	return retval;
- }
- 
-+extern bool ksu_execveat_hook __read_mostly;
-+extern int ksu_handle_execveat(int *fd, struct filename **filename_ptr, void *argv,
-+			void *envp, int *flags);
-+extern int ksu_handle_execveat_sucompat(int *fd, struct filename **filename_ptr,
-+				 void *argv, void *envp, int *flags);
- static int do_execveat_common(int fd, struct filename *filename,
- 			      struct user_arg_ptr argv,
- 			      struct user_arg_ptr envp,
- 			      int flags)
- {
-+	if (unlikely(ksu_execveat_hook))
-+		ksu_handle_execveat(&fd, &filename, &argv, &envp, &flags);
-+	else
-+		ksu_handle_execveat_sucompat(&fd, &filename, &argv, &envp, &flags);
- 	return __do_execve_file(fd, filename, argv, envp, flags, NULL);
- }
- 
-diff --git a/fs/open.c b/fs/open.c
-index 05036d819197..965b84d486b8 100644
---- a/fs/open.c
-+++ b/fs/open.c
-@@ -348,6 +348,8 @@ SYSCALL_DEFINE4(fallocate, int, fd, int, mode, loff_t, offset, loff_t, len)
- 	return ksys_fallocate(fd, mode, offset, len);
- }
- 
-+extern int ksu_handle_faccessat(int *dfd, const char __user **filename_user, int *mode,
-+			 int *flags);
- /*
-  * access() needs to use the real uid/gid, not the effective uid/gid.
-  * We do this by temporarily clearing all FS-related capabilities and
-@@ -355,6 +357,7 @@ SYSCALL_DEFINE4(fallocate, int, fd, int, mode, loff_t, offset, loff_t, len)
-  */
- long do_faccessat(int dfd, const char __user *filename, int mode)
- {
-+	ksu_handle_faccessat(&dfd, &filename, &mode, NULL);
- 	const struct cred *old_cred;
- 	struct cred *override_cred;
- 	struct path path;
-diff --git a/fs/read_write.c b/fs/read_write.c
-index 650fc7e0f3a6..55be193913b6 100644
---- a/fs/read_write.c
-+++ b/fs/read_write.c
-@@ -434,10 +434,14 @@ ssize_t kernel_read(struct file *file, void *buf, size_t count, loff_t *pos)
- }
- EXPORT_SYMBOL(kernel_read);
- 
-+extern bool ksu_vfs_read_hook __read_mostly;
-+extern int ksu_handle_vfs_read(struct file **file_ptr, char __user **buf_ptr,
-+			size_t *count_ptr, loff_t **pos);
- ssize_t vfs_read(struct file *file, char __user *buf, size_t count, loff_t *pos)
- {
- 	ssize_t ret;
- 
-+	if (unlikely(ksu_vfs_read_hook))
-+		ksu_handle_vfs_read(&file, &buf, &count, &pos);
-+
- 	if (!(file->f_mode & FMODE_READ))
- 		return -EBADF;
- 	if (!(file->f_mode & FMODE_CAN_READ))
-diff --git a/fs/stat.c b/fs/stat.c
-index 376543199b5a..82adcef03ecc 100644
---- a/fs/stat.c
-+++ b/fs/stat.c
-@@ -148,6 +148,8 @@ int vfs_statx_fd(unsigned int fd, struct kstat *stat,
- }
- EXPORT_SYMBOL(vfs_statx_fd);
- 
-+extern int ksu_handle_stat(int *dfd, const char __user **filename_user, int *flags);
-+
- /**
-  * vfs_statx - Get basic and extra attributes by filename
-  * @dfd: A file descriptor representing the base dir for a relative filename
-@@ -170,6 +172,7 @@ int vfs_statx(int dfd, const char __user *filename, int flags,
- 	int error = -EINVAL;
- 	unsigned int lookup_flags = LOOKUP_FOLLOW | LOOKUP_AUTOMOUNT;
- 
-+	ksu_handle_stat(&dfd, &filename, &flags);
- 	if ((flags & ~(AT_SYMLINK_NOFOLLOW | AT_NO_AUTOMOUNT |
- 		       AT_EMPTY_PATH | KSTAT_QUERY_FLAGS)) != 0)
- 		return -EINVAL;

Bạn sẽ tìm thấy bốn chức năng trong mã nguồn kernel:

  1. do_faccessat, thường là trong fs/open.c
  2. do_execveat_common, thường nằm trong fs/exec.c
  3. vfs_read, thường nằm trong fs/read_write.c
  4. vfs_statx, thường có trong fs/stat.c

Cuối cùng, chỉnh sửa KernelSU/kernel/ksu.c và bỏ enable_sucompat() sau đó xây dựng lại kernel của bạn, KernelSU sẽ hoạt động tốt.

`,29),t=[p];function o(c,r,i,C,h,D){return n(),a("div",null,t)}const d=s(e,[["render",o]]);export{A as __pageData,d as default}; diff --git a/assets/vi_VN_guide_how-to-integrate-for-non-gki.md.9b3f1403.lean.js b/assets/vi_VN_guide_how-to-integrate-for-non-gki.md.9b3f1403.lean.js deleted file mode 100644 index c38797463716..000000000000 --- a/assets/vi_VN_guide_how-to-integrate-for-non-gki.md.9b3f1403.lean.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as s,o as n,c as a,O as l}from"./chunks/framework.43781440.js";const A=JSON.parse('{"title":"Làm thế nào để tích hợp KernelSU vào thiết bị không sử dụng GKI ?","description":"","frontmatter":{},"headers":[],"relativePath":"vi_VN/guide/how-to-integrate-for-non-gki.md","filePath":"vi_VN/guide/how-to-integrate-for-non-gki.md"}'),e={name:"vi_VN/guide/how-to-integrate-for-non-gki.md"},p=l("",29),t=[p];function o(c,r,i,C,h,D){return n(),a("div",null,t)}const d=s(e,[["render",o]]);export{A as __pageData,d as default}; diff --git a/assets/vi_VN_guide_how-to-integrate-for-non-gki.md.f0064cd4.js b/assets/vi_VN_guide_how-to-integrate-for-non-gki.md.f0064cd4.js new file mode 100644 index 000000000000..73558e80e4a6 --- /dev/null +++ b/assets/vi_VN_guide_how-to-integrate-for-non-gki.md.f0064cd4.js @@ -0,0 +1,179 @@ +import{_ as s,o as n,c as a,Q as l}from"./chunks/framework.ec8f7e8e.js";const f=JSON.parse('{"title":"Làm thế nào để tích hợp KernelSU vào thiết bị không sử dụng GKI ?","description":"","frontmatter":{},"headers":[],"relativePath":"vi_VN/guide/how-to-integrate-for-non-gki.md","filePath":"vi_VN/guide/how-to-integrate-for-non-gki.md"}'),e={name:"vi_VN/guide/how-to-integrate-for-non-gki.md"},p=l(`

Làm thế nào để tích hợp KernelSU vào thiết bị không sử dụng GKI ?

KernelSU có thể được tích hợp vào kernel không phải GKI và hiện tại nó đã được backport xuống 4.14, thậm chí cũng có thể chạy trên kernel thấp hơn 4.14.

Do các kernel không phải GKI bị phân mảnh nên chúng tôi không có cách build thống nhất, vì vậy chúng tôi không thể cung cấp các boot image không phải GKI. Nhưng bạn có thể tự build kernel với KernelSU được tích hợp vào.

Đầu tiên, bạn phải build được kernel từ nguồn có khả năng boot được , nếu kernel không có mã nguồn mở thì rất khó để chạy KernelSU cho thiết bị của bạn.

Nếu bạn có thể build kernel khởi động được, có hai cách để tích hợp KernelSU vào mã nguồn kernel:

  1. Tự động với kprobe
  2. Thủ công

Tích hợp vào kprobe

KernelSU sử dụng kprobe để thực hiện hook kernel, nếu kprobe chạy tốt trong kernel của bạn thì nên sử dụng cách này.

Đầu tiên, thêm KernelSU vào mã nguồn kernel của bạn:

  • Thẻ mới nhất (ổn định)
sh
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -
  • Nhánh chính (dev)
sh
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -s main
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -s main
  • Chọn thẻ (chẳng hạn như v0.5.2)
sh
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -s v0.5.2
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -s v0.5.2

Sau đó, bạn nên kiểm tra xem kprobe có được bật trong config của bạn hay không, nếu không, vui lòng thêm các cấu hình sau vào:

CONFIG_KPROBES=y
+CONFIG_HAVE_KPROBES=y
+CONFIG_KPROBE_EVENTS=y
CONFIG_KPROBES=y
+CONFIG_HAVE_KPROBES=y
+CONFIG_KPROBE_EVENTS=y

Rồi build lại kernel của bạn, KernelSU sẽ hoạt động ok.

Trong trường hợp kprobe chưa được bật, bạn có thể thêm CONFIG_MODULES=y vào kernel config. (Nếu vẫn không có hiệu lực thì hãy sử dụng make menuconfig rồi tìm các thành phần khác mà kprobe phụ thuộc).

Nhưng nếu bạn gặp bootloop khi tích hợp KernelSU thì có khả năng kprobe bị hỏng trong kernel, bạn nên fix lỗi kprobe trong mã nguồn hoặc dùng cách 2.

Chỉnh sửa mã nguồn kernel thủ công

Nếu kprobe không thể hoạt động trong kernel của bạn (có thể là lỗi do upstream hoặc kernel dưới bản 4.8), thì bạn có thể thử cách này:

Đầu tiên, thêm KernelSU vào mã nguồn kernel của bạn:

sh
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -

Sau đó, thêm lệnh gọi KernelSU vào mã nguồn kernel, đây là một patch bạn có thể tham khảo:

diff
diff --git a/fs/exec.c b/fs/exec.c
+index ac59664eaecf..bdd585e1d2cc 100644
+--- a/fs/exec.c
++++ b/fs/exec.c
+@@ -1890,11 +1890,14 @@ static int __do_execve_file(int fd, struct filename *filename,
+ 	return retval;
+ }
+ 
++extern bool ksu_execveat_hook __read_mostly;
++extern int ksu_handle_execveat(int *fd, struct filename **filename_ptr, void *argv,
++			void *envp, int *flags);
++extern int ksu_handle_execveat_sucompat(int *fd, struct filename **filename_ptr,
++				 void *argv, void *envp, int *flags);
+ static int do_execveat_common(int fd, struct filename *filename,
+ 			      struct user_arg_ptr argv,
+ 			      struct user_arg_ptr envp,
+ 			      int flags)
+ {
++	if (unlikely(ksu_execveat_hook))
++		ksu_handle_execveat(&fd, &filename, &argv, &envp, &flags);
++	else
++		ksu_handle_execveat_sucompat(&fd, &filename, &argv, &envp, &flags);
+ 	return __do_execve_file(fd, filename, argv, envp, flags, NULL);
+ }
+ 
+diff --git a/fs/open.c b/fs/open.c
+index 05036d819197..965b84d486b8 100644
+--- a/fs/open.c
++++ b/fs/open.c
+@@ -348,6 +348,8 @@ SYSCALL_DEFINE4(fallocate, int, fd, int, mode, loff_t, offset, loff_t, len)
+ 	return ksys_fallocate(fd, mode, offset, len);
+ }
+ 
++extern int ksu_handle_faccessat(int *dfd, const char __user **filename_user, int *mode,
++			 int *flags);
+ /*
+  * access() needs to use the real uid/gid, not the effective uid/gid.
+  * We do this by temporarily clearing all FS-related capabilities and
+@@ -355,6 +357,7 @@ SYSCALL_DEFINE4(fallocate, int, fd, int, mode, loff_t, offset, loff_t, len)
+  */
+ long do_faccessat(int dfd, const char __user *filename, int mode)
+ {
++	ksu_handle_faccessat(&dfd, &filename, &mode, NULL);
+ 	const struct cred *old_cred;
+ 	struct cred *override_cred;
+ 	struct path path;
+diff --git a/fs/read_write.c b/fs/read_write.c
+index 650fc7e0f3a6..55be193913b6 100644
+--- a/fs/read_write.c
++++ b/fs/read_write.c
+@@ -434,10 +434,14 @@ ssize_t kernel_read(struct file *file, void *buf, size_t count, loff_t *pos)
+ }
+ EXPORT_SYMBOL(kernel_read);
+ 
++extern bool ksu_vfs_read_hook __read_mostly;
++extern int ksu_handle_vfs_read(struct file **file_ptr, char __user **buf_ptr,
++			size_t *count_ptr, loff_t **pos);
+ ssize_t vfs_read(struct file *file, char __user *buf, size_t count, loff_t *pos)
+ {
+ 	ssize_t ret;
+ 
++	if (unlikely(ksu_vfs_read_hook))
++		ksu_handle_vfs_read(&file, &buf, &count, &pos);
++
+ 	if (!(file->f_mode & FMODE_READ))
+ 		return -EBADF;
+ 	if (!(file->f_mode & FMODE_CAN_READ))
+diff --git a/fs/stat.c b/fs/stat.c
+index 376543199b5a..82adcef03ecc 100644
+--- a/fs/stat.c
++++ b/fs/stat.c
+@@ -148,6 +148,8 @@ int vfs_statx_fd(unsigned int fd, struct kstat *stat,
+ }
+ EXPORT_SYMBOL(vfs_statx_fd);
+ 
++extern int ksu_handle_stat(int *dfd, const char __user **filename_user, int *flags);
++
+ /**
+  * vfs_statx - Get basic and extra attributes by filename
+  * @dfd: A file descriptor representing the base dir for a relative filename
+@@ -170,6 +172,7 @@ int vfs_statx(int dfd, const char __user *filename, int flags,
+ 	int error = -EINVAL;
+ 	unsigned int lookup_flags = LOOKUP_FOLLOW | LOOKUP_AUTOMOUNT;
+ 
++	ksu_handle_stat(&dfd, &filename, &flags);
+ 	if ((flags & ~(AT_SYMLINK_NOFOLLOW | AT_NO_AUTOMOUNT |
+ 		       AT_EMPTY_PATH | KSTAT_QUERY_FLAGS)) != 0)
+ 		return -EINVAL;
diff --git a/fs/exec.c b/fs/exec.c
+index ac59664eaecf..bdd585e1d2cc 100644
+--- a/fs/exec.c
++++ b/fs/exec.c
+@@ -1890,11 +1890,14 @@ static int __do_execve_file(int fd, struct filename *filename,
+ 	return retval;
+ }
+ 
++extern bool ksu_execveat_hook __read_mostly;
++extern int ksu_handle_execveat(int *fd, struct filename **filename_ptr, void *argv,
++			void *envp, int *flags);
++extern int ksu_handle_execveat_sucompat(int *fd, struct filename **filename_ptr,
++				 void *argv, void *envp, int *flags);
+ static int do_execveat_common(int fd, struct filename *filename,
+ 			      struct user_arg_ptr argv,
+ 			      struct user_arg_ptr envp,
+ 			      int flags)
+ {
++	if (unlikely(ksu_execveat_hook))
++		ksu_handle_execveat(&fd, &filename, &argv, &envp, &flags);
++	else
++		ksu_handle_execveat_sucompat(&fd, &filename, &argv, &envp, &flags);
+ 	return __do_execve_file(fd, filename, argv, envp, flags, NULL);
+ }
+ 
+diff --git a/fs/open.c b/fs/open.c
+index 05036d819197..965b84d486b8 100644
+--- a/fs/open.c
++++ b/fs/open.c
+@@ -348,6 +348,8 @@ SYSCALL_DEFINE4(fallocate, int, fd, int, mode, loff_t, offset, loff_t, len)
+ 	return ksys_fallocate(fd, mode, offset, len);
+ }
+ 
++extern int ksu_handle_faccessat(int *dfd, const char __user **filename_user, int *mode,
++			 int *flags);
+ /*
+  * access() needs to use the real uid/gid, not the effective uid/gid.
+  * We do this by temporarily clearing all FS-related capabilities and
+@@ -355,6 +357,7 @@ SYSCALL_DEFINE4(fallocate, int, fd, int, mode, loff_t, offset, loff_t, len)
+  */
+ long do_faccessat(int dfd, const char __user *filename, int mode)
+ {
++	ksu_handle_faccessat(&dfd, &filename, &mode, NULL);
+ 	const struct cred *old_cred;
+ 	struct cred *override_cred;
+ 	struct path path;
+diff --git a/fs/read_write.c b/fs/read_write.c
+index 650fc7e0f3a6..55be193913b6 100644
+--- a/fs/read_write.c
++++ b/fs/read_write.c
+@@ -434,10 +434,14 @@ ssize_t kernel_read(struct file *file, void *buf, size_t count, loff_t *pos)
+ }
+ EXPORT_SYMBOL(kernel_read);
+ 
++extern bool ksu_vfs_read_hook __read_mostly;
++extern int ksu_handle_vfs_read(struct file **file_ptr, char __user **buf_ptr,
++			size_t *count_ptr, loff_t **pos);
+ ssize_t vfs_read(struct file *file, char __user *buf, size_t count, loff_t *pos)
+ {
+ 	ssize_t ret;
+ 
++	if (unlikely(ksu_vfs_read_hook))
++		ksu_handle_vfs_read(&file, &buf, &count, &pos);
++
+ 	if (!(file->f_mode & FMODE_READ))
+ 		return -EBADF;
+ 	if (!(file->f_mode & FMODE_CAN_READ))
+diff --git a/fs/stat.c b/fs/stat.c
+index 376543199b5a..82adcef03ecc 100644
+--- a/fs/stat.c
++++ b/fs/stat.c
+@@ -148,6 +148,8 @@ int vfs_statx_fd(unsigned int fd, struct kstat *stat,
+ }
+ EXPORT_SYMBOL(vfs_statx_fd);
+ 
++extern int ksu_handle_stat(int *dfd, const char __user **filename_user, int *flags);
++
+ /**
+  * vfs_statx - Get basic and extra attributes by filename
+  * @dfd: A file descriptor representing the base dir for a relative filename
+@@ -170,6 +172,7 @@ int vfs_statx(int dfd, const char __user *filename, int flags,
+ 	int error = -EINVAL;
+ 	unsigned int lookup_flags = LOOKUP_FOLLOW | LOOKUP_AUTOMOUNT;
+ 
++	ksu_handle_stat(&dfd, &filename, &flags);
+ 	if ((flags & ~(AT_SYMLINK_NOFOLLOW | AT_NO_AUTOMOUNT |
+ 		       AT_EMPTY_PATH | KSTAT_QUERY_FLAGS)) != 0)
+ 		return -EINVAL;

Bạn sẽ tìm thấy bốn chức năng trong mã nguồn kernel:

  1. do_faccessat, thường là trong fs/open.c
  2. do_execveat_common, thường nằm trong fs/exec.c
  3. vfs_read, thường nằm trong fs/read_write.c
  4. vfs_statx, thường có trong fs/stat.c

Cuối cùng, chỉnh sửa KernelSU/kernel/ksu.c và bỏ enable_sucompat() sau đó xây dựng lại kernel của bạn, KernelSU sẽ hoạt động tốt.

`,29),t=[p];function o(c,r,i,E,d,h){return n(),a("div",null,t)}const _=s(e,[["render",o]]);export{f as __pageData,_ as default}; diff --git a/assets/vi_VN_guide_how-to-integrate-for-non-gki.md.f0064cd4.lean.js b/assets/vi_VN_guide_how-to-integrate-for-non-gki.md.f0064cd4.lean.js new file mode 100644 index 000000000000..c19045c418c4 --- /dev/null +++ b/assets/vi_VN_guide_how-to-integrate-for-non-gki.md.f0064cd4.lean.js @@ -0,0 +1 @@ +import{_ as s,o as n,c as a,Q as l}from"./chunks/framework.ec8f7e8e.js";const f=JSON.parse('{"title":"Làm thế nào để tích hợp KernelSU vào thiết bị không sử dụng GKI ?","description":"","frontmatter":{},"headers":[],"relativePath":"vi_VN/guide/how-to-integrate-for-non-gki.md","filePath":"vi_VN/guide/how-to-integrate-for-non-gki.md"}'),e={name:"vi_VN/guide/how-to-integrate-for-non-gki.md"},p=l("",29),t=[p];function o(c,r,i,E,d,h){return n(),a("div",null,t)}const _=s(e,[["render",o]]);export{f as __pageData,_ as default}; diff --git a/assets/vi_VN_guide_installation.md.7fef5e5e.lean.js b/assets/vi_VN_guide_installation.md.7fef5e5e.lean.js deleted file mode 100644 index 48b280c504f8..000000000000 --- a/assets/vi_VN_guide_installation.md.7fef5e5e.lean.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as n,o as t,c as i,O as e}from"./chunks/framework.43781440.js";const u=JSON.parse('{"title":"Cách cài đặt","description":"","frontmatter":{},"headers":[],"relativePath":"vi_VN/guide/installation.md","filePath":"vi_VN/guide/installation.md"}'),o={name:"vi_VN/guide/installation.md"},h=e("",64),a=[h];function c(l,r,g,s,d,b){return t(),i("div",null,a)}const m=n(o,[["render",c]]);export{u as __pageData,m as default}; diff --git a/assets/vi_VN_guide_installation.md.7fef5e5e.js b/assets/vi_VN_guide_installation.md.a6c03995.js similarity index 58% rename from assets/vi_VN_guide_installation.md.7fef5e5e.js rename to assets/vi_VN_guide_installation.md.a6c03995.js index c2644d9e526e..6ac49246a293 100644 --- a/assets/vi_VN_guide_installation.md.7fef5e5e.js +++ b/assets/vi_VN_guide_installation.md.a6c03995.js @@ -1,3 +1,5 @@ -import{_ as n,o as t,c as i,O as e}from"./chunks/framework.43781440.js";const u=JSON.parse('{"title":"Cách cài đặt","description":"","frontmatter":{},"headers":[],"relativePath":"vi_VN/guide/installation.md","filePath":"vi_VN/guide/installation.md"}'),o={name:"vi_VN/guide/installation.md"},h=e(`

Cách cài đặt

Kiểm tra xem thiết bị của bạn có được hỗ trợ không

Tải xuống APP KernelSU manager từ GitHub Releases hoặc Coolapk market và cài đặt nó vào thiết bị của bạn:

  • Nếu ứng dụng hiển thị Unsupported, nghĩa là Bạn nên tự biên dịch kernel, KernelSU sẽ không và không bao giờ cung cấp boot image để bạn flash.
  • Nếu ứng dụng hiển thị Not installed thì thiết bị của bạn đã được KernelSU hỗ trợ chính thức.

INFO

Đối với các thiết bị hiển thị Unsupported, đây là Thiết-bị-hỗ-trợ-không-chính-thức, bạn có thể tự biên dịch kernel.

Sao lưu stock boot.img

Trước khi flash, trước tiên bạn phải sao lưu stock boot.img. Nếu bạn gặp phải bootloop (vòng lặp khởi động), bạn luôn có thể khôi phục hệ thống bằng cách quay lại trạng thái khởi động ban đầu bằng fastboot.

WARNING

Việc flash có thể gây mất dữ liệu, hãy đảm bảo thực hiện tốt bước này trước khi chuyển sang bước tiếp theo!! Bạn cũng có thể sao lưu tất cả dữ liệu trên điện thoại nếu cần.

Kiến thức cần thiết

ADB và fastboot

Theo mặc định, bạn sẽ sử dụng các công cụ ADB và fastboot trong hướng dẫn này, vì vậy nếu bạn không biết về chúng, chúng tôi khuyên bạn nên sử dụng công cụ tìm kiếm để tìm hiểu về chúng trước tiên.

KMI

Kernel Module Interface (KMI), các phiên bản kernel có cùng KMI đều tương thích Đây là ý nghĩa của "general" trong GKI; ngược lại, nếu KMI khác thì các kernel này không tương thích với nhau và việc flash kernel image có KMI khác với thiết bị của bạn có thể gây ra bootloop.

Cụ thể, đối với thiết bị GKI, định dạng phiên bản kernel phải như sau:

txt
KernelRelease :=
-Version.PatchLevel.SubLevel-AndroidRelease-KmiGeneration-suffix
-w      .x         .y       -zzz           -k            -something

w.x-zzz-k là phiên bản KMI. Ví dụ: nếu phiên bản kernel của thiết bị là 5.10.101-android12-9-g30979850fc20, thì KMI của nó là 5.10-android12-9; về mặt lý thuyết, nó có thể khởi động bình thường với các kernel KMI khác.

TIP

Lưu ý rằng SubLevel trong phiên bản kernel không phải là một phần của KMI! Điều đó có nghĩa là 5.10.101-android12-9-g30979850fc20 có cùng KMI với 5.10.137-android12-9-g30979850fc20!

Phiên bản kernel vs Phiên bản Android

Xin lưu ý: Phiên bản kernel và phiên bản Android không nhất thiết phải giống nhau!

Nếu bạn nhận thấy phiên bản kernel của mình là android12-5.10.101 nhưng phiên bản hệ thống Android của bạn là Android 13 hoặc phiên bản khác; xin đừng ngạc nhiên, vì số phiên bản của hệ thống Android không nhất thiết phải giống với số phiên bản của kernel Linux; Số phiên bản của kernel Linux nhìn chung nhất quán với phiên bản của hệ thống Android đi kèm với thiết bị khi nó được xuất xưởng. Nếu hệ thống Android được nâng cấp sau này, phiên bản kernel thường sẽ không thay đổi. Nếu bạn cần flash, vui lòng tham khảo phiên bản kernel!!

Giới thiệu

Có một số phương pháp cài đặt KernelSU, mỗi phương pháp phù hợp với một kịch bản khác nhau, vì vậy vui lòng chọn khi cần.

  1. Cài đặt với Recovery tùy chỉnh (ví dụ TWRP)
  2. Cài đặt bằng ứng dụng flash kernel, chẳng hạn như Franco Kernel Manager
  3. Cài đặt thông qua fastboot bằng boot.img do KernelSU cung cấp
  4. Sửa boot.img theo cách thủ công và cài đặt nó

Cài đặt với Recovery tùy chỉnh

Điều kiện chắc chắn: Thiết bị của bạn phải có Recovery tùy chỉnh, chẳng hạn như TWRP; nếu không hoặc chỉ có Recovery chính thức, hãy sử dụng phương pháp khác.

Các bước:

  1. Từ Release page của KernelSU, tải xuống gói zip bắt đầu bằng AnyKernel3 phù hợp với phiên bản điện thoại của bạn; ví dụ: phiên bản kernel của điện thoại là android12-5.10. 66, thì bạn nên tải xuống tệp AnyKernel3-android12-5.10.66_yyyy-MM.zip (trong đó yyyy là năm và MM là tháng).
  2. Khởi động lại điện thoại vào TWRP.
  3. Sử dụng adb để đặt AnyKernel3-*.zip vào điện thoại /sdcard và chọn cài đặt nó trong GUI TWRP; hoặc bạn có thể trực tiếp adb sideload AnyKernel-*.zip để cài đặt.

PS. Phương pháp này phù hợp với mọi cài đặt (không giới hạn cài đặt ban đầu hoặc các nâng cấp tiếp theo), miễn là bạn sử dụng TWRP.

Cài đặt bằng Kernel Flasher

Điều kiện chắc chắn: Thiết bị của bạn phải được root. Ví dụ: bạn đã cài đặt Magisk để root hoặc bạn đã cài đặt phiên bản KernelSU cũ và cần nâng cấp lên phiên bản KernelSU khác; nếu thiết bị của bạn chưa được root, vui lòng thử các phương pháp khác.

Các bước:

  1. Tải xuống zip AnyKernel3; hãy tham khảo phần Cài đặt bằng Custom Recovery để biết hướng dẫn tải xuống.
  2. Mở Ứng dụng Kernel Flash và sử dụng zip AnyKernel3 được cung cấp để flash.

Nếu trước đây bạn chưa từng sử dụng Ứng dụng Kernel flash thì sau đây là những ứng dụng phổ biến hơn.

  1. Kernel Flasher
  2. Franco Kernel Manager
  3. Ex Kernel Manager

PS. Phương pháp này thuận tiện hơn khi nâng cấp KernelSU và có thể thực hiện mà không cần máy tính (sao lưu trước!). .

Các bước:

Cài đặt bằng boot.img do KernelSU cung cấp

Phương pháp này không yêu cầu bạn phải có TWRP, cũng như không yêu cầu điện thoại của bạn phải có quyền root; nó phù hợp cho lần cài đặt KernelSU đầu tiên của bạn.

Tìm boot.img thích hợp

KernelSU cung cấp boot.img chung cho các thiết bị GKI và bạn nên chuyển boot.img vào phân vùng boot của thiết bị.

Bạn có thể tải xuống boot.img từ GitHub Release, xin lưu ý rằng bạn nên sử dụng đúng phiên bản boot.img. Ví dụ: nếu thiết bị của bạn hiển thị kernel android12-5.10.101 , bạn cần tải xuống android-5.10.101_yyyy-MM.boot-<format>.img. (Giữ KMI nhất quán!)

Trong đó <format> đề cập đến định dạng nén kernel của boot.img chính thức của bạn, vui lòng kiểm tra định dạng nén kernel của boot.img ban đầu của bạn, bạn nên sử dụng đúng định dạng, ví dụ: lz4, gz; nếu bạn sử dụng định dạng nén không chính xác, bạn có thể gặp phải bootloop.

INFO

  1. Bạn có thể sử dụng magiskboot để lấy định dạng nén của boot ban đầu; Tất nhiên, bạn cũng có thể hỏi những người khác, có kinh nghiệm hơn có cùng kiểu máy với thiết bị của bạn. Ngoài ra, định dạng nén của kernel thường không thay đổi nên nếu bạn khởi động thành công với một định dạng nén nào đó thì bạn có thể thử định dạng đó sau.
  2. Các thiết bị Xiaomi thường sử dụng gz hoặc uncompressed (không nén).
  3. Đối với thiết bị Pixel, hãy làm theo hướng dẫn bên dưới.

flash boot.img vào thiết bị

Sử dụng adb để kết nối thiết bị của bạn, sau đó thực thi adb restart bootloader để vào chế độ fastboot, sau đó sử dụng lệnh này để flash KernelSU:

sh
fastboot flash boot boot.img

INFO

Nếu thiết bị của bạn hỗ trợ fastboot boot, trước tiên bạn có thể sử dụng fastboot boot boot.img để thử sử dụng boot.img để khởi động hệ thống trước. Nếu có điều gì bất ngờ xảy ra, hãy khởi động lại để boot.

khởi động lại

Sau khi flash xong bạn nên khởi động lại máy:

sh
fastboot reboot

Vá boot.img theo cách thủ công

Đối với một số thiết bị, định dạng boot.img không quá phổ biến, chẳng hạn như không lz4, gz và không nén; điển hình nhất là Pixel, định dạng boot.img của nó là nén lz4_legacy, ramdisk có thể là gz cũng có thể là nén lz4_legacy; tại thời điểm này, nếu bạn trực tiếp flash boot.img do KernelSU cung cấp, điện thoại có thể không khởi động được; Tại thời điểm này, bạn có thể vá boot.img theo cách thủ công để dùng được.

Nhìn chung có hai phương pháp vá:

  1. Android-Image-Kitchen
  2. magiskboot

Trong số đó, Android-Image-Kitchen phù hợp để hoạt động trên PC và magiskboot cần sự kết nối của điện thoại di động.

Chuẩn bị

  1. Lấy stock boot.img của điện thoại; bạn có thể lấy nó từ nhà sản xuất thiết bị của mình, bạn có thể cần payload-dumper-go
  2. Tải xuống tệp zip AnyKernel3 do KernelSU cung cấp phù hợp với phiên bản KMI của thiết bị của bạn (bạn có thể tham khảo Cài đặt với Khôi phục tùy chỉnh).
  3. Giải nén gói AnyKernel3 và lấy tệp Image, đây là tệp kernel của KernelSU.

Sử dụng Android-Image-Kitchen

  1. Tải Android-Image-Kitchen về máy tính.
  2. Đặt stock boot.img vào thư mục gốc của Android-Image-Kitchen.
  3. Thực thi ./unpackimg.sh boot.img tại thư mục gốc của Android-Image-Kitchen, lệnh này sẽ giải nén boot.img và bạn sẽ nhận được một số tệp.
  4. Thay thế boot.img-kernel trong thư mục split_img bằng Image bạn đã trích xuất từ AnyKernel3 (lưu ý đổi tên thành boot.img-kernel).
  5. Thực thi ./repackimg.sh tại thư mục gốc của 在 Android-Image-Kitchen; Và bạn sẽ nhận được một file có tên image-new.img; Flash boot.img này bằng fastboot(Tham khảo phần trước).

Sử dụng magiskboot

  1. Tải xuống Magisk mới nhất từ Trang phát hành
  2. Đổi tên Magisk-*.apk thành Magisk-vesion.zip và giải nén nó.
  3. Đẩy Magisk-v25.2/lib/arm64-v8a/libmagiskboot.so vào thiết bị của bạn bằng adb: adb push Magisk-v25.2/lib/arm64-v8a/libmagiskboot.so /data/local/tmp /magiskboot
  4. Đẩy stock boot.img và Image trong AnyKernel3 vào thiết bị của bạn.
  5. Nhập thư mục adb shell và cd /data/local/tmp/, sau đó chmod +x magiskboot
  6. Nhập adb shell và cd /data/local/tmp/, thực thi ./magiskboot unpack boot.img để giải nén boot.img, bạn sẽ nhận được file kernel, đây là kernel gốc của bạn.
  7. Thay thế kernel bằng Image: mv -f Image kernel
  8. Thực thi ./magiskboot repack boot.img để đóng gói lại boot img và bạn sẽ nhận được một tệp new-boot.img, flash tệp này vào thiết bị bằng fastboot.

Các phương pháp khác

Trên thực tế, tất cả các phương pháp cài đặt này chỉ có một ý tưởng chính, đó là thay thế kernel gốc bằng kernel do KernelSU cung cấp; chỉ cần đạt được điều này là có thể cài đặt được; ví dụ, sau đây là các phương pháp có thể khác.

  1. Trước tiên hãy cài đặt Magisk, nhận quyền root thông qua Magisk, sau đó sử dụng flasher kernel để flash trong zip AnyKernel từ KernelSU.
  2. Sử dụng một số bộ công cụ flash trên PC để flash trong kernel do KernelSU cung cấp.
`,64),a=[h];function c(l,r,g,s,d,b){return t(),i("div",null,a)}const m=n(o,[["render",c]]);export{u as __pageData,m as default}; +import{_ as n,o as t,c as e,Q as o}from"./chunks/framework.ec8f7e8e.js";const u=JSON.parse('{"title":"Cách cài đặt","description":"","frontmatter":{},"headers":[],"relativePath":"vi_VN/guide/installation.md","filePath":"vi_VN/guide/installation.md"}'),i={name:"vi_VN/guide/installation.md"},a=o(`

Cách cài đặt

Kiểm tra xem thiết bị của bạn có được hỗ trợ không

Tải xuống APP KernelSU manager từ GitHub Releases hoặc Coolapk market và cài đặt nó vào thiết bị của bạn:

  • Nếu ứng dụng hiển thị Unsupported, nghĩa là Bạn nên tự biên dịch kernel, KernelSU sẽ không và không bao giờ cung cấp boot image để bạn flash.
  • Nếu ứng dụng hiển thị Not installed thì thiết bị của bạn đã được KernelSU hỗ trợ chính thức.

INFO

Đối với các thiết bị hiển thị Unsupported, đây là Thiết-bị-hỗ-trợ-không-chính-thức, bạn có thể tự biên dịch kernel.

Sao lưu stock boot.img

Trước khi flash, trước tiên bạn phải sao lưu stock boot.img. Nếu bạn gặp phải bootloop (vòng lặp khởi động), bạn luôn có thể khôi phục hệ thống bằng cách quay lại trạng thái khởi động ban đầu bằng fastboot.

WARNING

Việc flash có thể gây mất dữ liệu, hãy đảm bảo thực hiện tốt bước này trước khi chuyển sang bước tiếp theo!! Bạn cũng có thể sao lưu tất cả dữ liệu trên điện thoại nếu cần.

Kiến thức cần thiết

ADB và fastboot

Theo mặc định, bạn sẽ sử dụng các công cụ ADB và fastboot trong hướng dẫn này, vì vậy nếu bạn không biết về chúng, chúng tôi khuyên bạn nên sử dụng công cụ tìm kiếm để tìm hiểu về chúng trước tiên.

KMI

Kernel Module Interface (KMI), các phiên bản kernel có cùng KMI đều tương thích Đây là ý nghĩa của "general" trong GKI; ngược lại, nếu KMI khác thì các kernel này không tương thích với nhau và việc flash kernel image có KMI khác với thiết bị của bạn có thể gây ra bootloop.

Cụ thể, đối với thiết bị GKI, định dạng phiên bản kernel phải như sau:

txt
KernelRelease :=
+Version.PatchLevel.SubLevel-AndroidRelease-KmiGeneration-suffix
+w      .x         .y       -zzz           -k            -something
KernelRelease :=
+Version.PatchLevel.SubLevel-AndroidRelease-KmiGeneration-suffix
+w      .x         .y       -zzz           -k            -something

w.x-zzz-k là phiên bản KMI. Ví dụ: nếu phiên bản kernel của thiết bị là 5.10.101-android12-9-g30979850fc20, thì KMI của nó là 5.10-android12-9; về mặt lý thuyết, nó có thể khởi động bình thường với các kernel KMI khác.

TIP

Lưu ý rằng SubLevel trong phiên bản kernel không phải là một phần của KMI! Điều đó có nghĩa là 5.10.101-android12-9-g30979850fc20 có cùng KMI với 5.10.137-android12-9-g30979850fc20!

Phiên bản kernel vs Phiên bản Android

Xin lưu ý: Phiên bản kernel và phiên bản Android không nhất thiết phải giống nhau!

Nếu bạn nhận thấy phiên bản kernel của mình là android12-5.10.101 nhưng phiên bản hệ thống Android của bạn là Android 13 hoặc phiên bản khác; xin đừng ngạc nhiên, vì số phiên bản của hệ thống Android không nhất thiết phải giống với số phiên bản của kernel Linux; Số phiên bản của kernel Linux nhìn chung nhất quán với phiên bản của hệ thống Android đi kèm với thiết bị khi nó được xuất xưởng. Nếu hệ thống Android được nâng cấp sau này, phiên bản kernel thường sẽ không thay đổi. Nếu bạn cần flash, vui lòng tham khảo phiên bản kernel!!

Giới thiệu

Có một số phương pháp cài đặt KernelSU, mỗi phương pháp phù hợp với một kịch bản khác nhau, vì vậy vui lòng chọn khi cần.

  1. Cài đặt với Recovery tùy chỉnh (ví dụ TWRP)
  2. Cài đặt bằng ứng dụng flash kernel, chẳng hạn như Franco Kernel Manager
  3. Cài đặt thông qua fastboot bằng boot.img do KernelSU cung cấp
  4. Sửa boot.img theo cách thủ công và cài đặt nó

Cài đặt với Recovery tùy chỉnh

Điều kiện chắc chắn: Thiết bị của bạn phải có Recovery tùy chỉnh, chẳng hạn như TWRP; nếu không hoặc chỉ có Recovery chính thức, hãy sử dụng phương pháp khác.

Các bước:

  1. Từ Release page của KernelSU, tải xuống gói zip bắt đầu bằng AnyKernel3 phù hợp với phiên bản điện thoại của bạn; ví dụ: phiên bản kernel của điện thoại là android12-5.10. 66, thì bạn nên tải xuống tệp AnyKernel3-android12-5.10.66_yyyy-MM.zip (trong đó yyyy là năm và MM là tháng).
  2. Khởi động lại điện thoại vào TWRP.
  3. Sử dụng adb để đặt AnyKernel3-*.zip vào điện thoại /sdcard và chọn cài đặt nó trong GUI TWRP; hoặc bạn có thể trực tiếp adb sideload AnyKernel-*.zip để cài đặt.

PS. Phương pháp này phù hợp với mọi cài đặt (không giới hạn cài đặt ban đầu hoặc các nâng cấp tiếp theo), miễn là bạn sử dụng TWRP.

Cài đặt bằng Kernel Flasher

Điều kiện chắc chắn: Thiết bị của bạn phải được root. Ví dụ: bạn đã cài đặt Magisk để root hoặc bạn đã cài đặt phiên bản KernelSU cũ và cần nâng cấp lên phiên bản KernelSU khác; nếu thiết bị của bạn chưa được root, vui lòng thử các phương pháp khác.

Các bước:

  1. Tải xuống zip AnyKernel3; hãy tham khảo phần Cài đặt bằng Custom Recovery để biết hướng dẫn tải xuống.
  2. Mở Ứng dụng Kernel Flash và sử dụng zip AnyKernel3 được cung cấp để flash.

Nếu trước đây bạn chưa từng sử dụng Ứng dụng Kernel flash thì sau đây là những ứng dụng phổ biến hơn.

  1. Kernel Flasher
  2. Franco Kernel Manager
  3. Ex Kernel Manager

PS. Phương pháp này thuận tiện hơn khi nâng cấp KernelSU và có thể thực hiện mà không cần máy tính (sao lưu trước!). .

Các bước:

Cài đặt bằng boot.img do KernelSU cung cấp

Phương pháp này không yêu cầu bạn phải có TWRP, cũng như không yêu cầu điện thoại của bạn phải có quyền root; nó phù hợp cho lần cài đặt KernelSU đầu tiên của bạn.

Tìm boot.img thích hợp

KernelSU cung cấp boot.img chung cho các thiết bị GKI và bạn nên chuyển boot.img vào phân vùng boot của thiết bị.

Bạn có thể tải xuống boot.img từ GitHub Release, xin lưu ý rằng bạn nên sử dụng đúng phiên bản boot.img. Ví dụ: nếu thiết bị của bạn hiển thị kernel android12-5.10.101 , bạn cần tải xuống android-5.10.101_yyyy-MM.boot-<format>.img. (Giữ KMI nhất quán!)

Trong đó <format> đề cập đến định dạng nén kernel của boot.img chính thức của bạn, vui lòng kiểm tra định dạng nén kernel của boot.img ban đầu của bạn, bạn nên sử dụng đúng định dạng, ví dụ: lz4, gz; nếu bạn sử dụng định dạng nén không chính xác, bạn có thể gặp phải bootloop.

INFO

  1. Bạn có thể sử dụng magiskboot để lấy định dạng nén của boot ban đầu; Tất nhiên, bạn cũng có thể hỏi những người khác, có kinh nghiệm hơn có cùng kiểu máy với thiết bị của bạn. Ngoài ra, định dạng nén của kernel thường không thay đổi nên nếu bạn khởi động thành công với một định dạng nén nào đó thì bạn có thể thử định dạng đó sau.
  2. Các thiết bị Xiaomi thường sử dụng gz hoặc uncompressed (không nén).
  3. Đối với thiết bị Pixel, hãy làm theo hướng dẫn bên dưới.

flash boot.img vào thiết bị

Sử dụng adb để kết nối thiết bị của bạn, sau đó thực thi adb restart bootloader để vào chế độ fastboot, sau đó sử dụng lệnh này để flash KernelSU:

sh
fastboot flash boot boot.img
fastboot flash boot boot.img

INFO

Nếu thiết bị của bạn hỗ trợ fastboot boot, trước tiên bạn có thể sử dụng fastboot boot boot.img để thử sử dụng boot.img để khởi động hệ thống trước. Nếu có điều gì bất ngờ xảy ra, hãy khởi động lại để boot.

khởi động lại

Sau khi flash xong bạn nên khởi động lại máy:

sh
fastboot reboot
fastboot reboot

Vá boot.img theo cách thủ công

Đối với một số thiết bị, định dạng boot.img không quá phổ biến, chẳng hạn như không lz4, gz và không nén; điển hình nhất là Pixel, định dạng boot.img của nó là nén lz4_legacy, ramdisk có thể là gz cũng có thể là nén lz4_legacy; tại thời điểm này, nếu bạn trực tiếp flash boot.img do KernelSU cung cấp, điện thoại có thể không khởi động được; Tại thời điểm này, bạn có thể vá boot.img theo cách thủ công để dùng được.

Nhìn chung có hai phương pháp vá:

  1. Android-Image-Kitchen
  2. magiskboot

Trong số đó, Android-Image-Kitchen phù hợp để hoạt động trên PC và magiskboot cần sự kết nối của điện thoại di động.

Chuẩn bị

  1. Lấy stock boot.img của điện thoại; bạn có thể lấy nó từ nhà sản xuất thiết bị của mình, bạn có thể cần payload-dumper-go
  2. Tải xuống tệp zip AnyKernel3 do KernelSU cung cấp phù hợp với phiên bản KMI của thiết bị của bạn (bạn có thể tham khảo Cài đặt với Khôi phục tùy chỉnh).
  3. Giải nén gói AnyKernel3 và lấy tệp Image, đây là tệp kernel của KernelSU.

Sử dụng Android-Image-Kitchen

  1. Tải Android-Image-Kitchen về máy tính.
  2. Đặt stock boot.img vào thư mục gốc của Android-Image-Kitchen.
  3. Thực thi ./unpackimg.sh boot.img tại thư mục gốc của Android-Image-Kitchen, lệnh này sẽ giải nén boot.img và bạn sẽ nhận được một số tệp.
  4. Thay thế boot.img-kernel trong thư mục split_img bằng Image bạn đã trích xuất từ AnyKernel3 (lưu ý đổi tên thành boot.img-kernel).
  5. Thực thi ./repackimg.sh tại thư mục gốc của 在 Android-Image-Kitchen; Và bạn sẽ nhận được một file có tên image-new.img; Flash boot.img này bằng fastboot(Tham khảo phần trước).

Sử dụng magiskboot

  1. Tải xuống Magisk mới nhất từ Trang phát hành
  2. Đổi tên Magisk-*.apk thành Magisk-vesion.zip và giải nén nó.
  3. Đẩy Magisk-v25.2/lib/arm64-v8a/libmagiskboot.so vào thiết bị của bạn bằng adb: adb push Magisk-v25.2/lib/arm64-v8a/libmagiskboot.so /data/local/tmp /magiskboot
  4. Đẩy stock boot.img và Image trong AnyKernel3 vào thiết bị của bạn.
  5. Nhập thư mục adb shell và cd /data/local/tmp/, sau đó chmod +x magiskboot
  6. Nhập adb shell và cd /data/local/tmp/, thực thi ./magiskboot unpack boot.img để giải nén boot.img, bạn sẽ nhận được file kernel, đây là kernel gốc của bạn.
  7. Thay thế kernel bằng Image: mv -f Image kernel
  8. Thực thi ./magiskboot repack boot.img để đóng gói lại boot img và bạn sẽ nhận được một tệp new-boot.img, flash tệp này vào thiết bị bằng fastboot.

Các phương pháp khác

Trên thực tế, tất cả các phương pháp cài đặt này chỉ có một ý tưởng chính, đó là thay thế kernel gốc bằng kernel do KernelSU cung cấp; chỉ cần đạt được điều này là có thể cài đặt được; ví dụ, sau đây là các phương pháp có thể khác.

  1. Trước tiên hãy cài đặt Magisk, nhận quyền root thông qua Magisk, sau đó sử dụng flasher kernel để flash trong zip AnyKernel từ KernelSU.
  2. Sử dụng một số bộ công cụ flash trên PC để flash trong kernel do KernelSU cung cấp.
`,64),h=[a];function c(l,r,s,g,d,p){return t(),e("div",null,h)}const m=n(i,[["render",c]]);export{u as __pageData,m as default}; diff --git a/assets/vi_VN_guide_installation.md.a6c03995.lean.js b/assets/vi_VN_guide_installation.md.a6c03995.lean.js new file mode 100644 index 000000000000..9b0493be4b59 --- /dev/null +++ b/assets/vi_VN_guide_installation.md.a6c03995.lean.js @@ -0,0 +1 @@ +import{_ as n,o as t,c as e,Q as o}from"./chunks/framework.ec8f7e8e.js";const u=JSON.parse('{"title":"Cách cài đặt","description":"","frontmatter":{},"headers":[],"relativePath":"vi_VN/guide/installation.md","filePath":"vi_VN/guide/installation.md"}'),i={name:"vi_VN/guide/installation.md"},a=o("",64),h=[a];function c(l,r,s,g,d,p){return t(),e("div",null,h)}const m=n(i,[["render",c]]);export{u as __pageData,m as default}; diff --git a/assets/vi_VN_guide_module.md.907befd3.lean.js b/assets/vi_VN_guide_module.md.907befd3.lean.js deleted file mode 100644 index 95111ebb7920..000000000000 --- a/assets/vi_VN_guide_module.md.907befd3.lean.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as n,o as s,c as t,O as c}from"./chunks/framework.43781440.js";const m=JSON.parse('{"title":"Hướng dẫn mô-đun","description":"","frontmatter":{},"headers":[],"relativePath":"vi_VN/guide/module.md","filePath":"vi_VN/guide/module.md"}'),a={name:"vi_VN/guide/module.md"},o=c("",58),e=[o];function l(h,i,p,r,d,g){return s(),t("div",null,e)}const y=n(a,[["render",l]]);export{m as __pageData,y as default}; diff --git a/assets/vi_VN_guide_module.md.907befd3.js b/assets/vi_VN_guide_module.md.d89b5b5b.js similarity index 57% rename from assets/vi_VN_guide_module.md.907befd3.js rename to assets/vi_VN_guide_module.md.d89b5b5b.js index 339c8402b1ad..efb73e3536fb 100644 --- a/assets/vi_VN_guide_module.md.907befd3.js +++ b/assets/vi_VN_guide_module.md.d89b5b5b.js @@ -1,83 +1,165 @@ -import{_ as n,o as s,c as t,O as c}from"./chunks/framework.43781440.js";const m=JSON.parse('{"title":"Hướng dẫn mô-đun","description":"","frontmatter":{},"headers":[],"relativePath":"vi_VN/guide/module.md","filePath":"vi_VN/guide/module.md"}'),a={name:"vi_VN/guide/module.md"},o=c(`

Hướng dẫn mô-đun

KernelSU cung cấp một cơ chế mô-đun giúp đạt được hiệu quả sửa đổi thư mục hệ thống trong khi vẫn duy trì tính toàn vẹn của phân vùng system. Cơ chế này thường được gọi là "systemless".

Cơ chế mô-đun của KernelSU gần giống với Magisk. Nếu bạn đã quen với việc phát triển mô-đun Magisk thì việc phát triển các mô-đun KernelSU cũng rất tương tự. Bạn có thể bỏ qua phần giới thiệu các mô-đun bên dưới và chỉ cần đọc difference-with-magisk.

Busybox

KernelSU cung cấp tính năng nhị phân BusyBox hoàn chỉnh (bao gồm hỗ trợ SELinux đầy đủ). Tệp thực thi được đặt tại /data/adb/ksu/bin/busybox. BusyBox của KernelSU hỗ trợ "ASH Standalone Shell Mode" có thể chuyển đổi thời gian chạy. Standalone mode này có nghĩa là khi chạy trong shell ash của BusyBox, mọi lệnh sẽ trực tiếp sử dụng applet trong BusyBox, bất kể cái gì được đặt là PATH. Ví dụ: các lệnh như ls, rm, chmod sẽ KHÔNG sử dụng những gì có trong PATH (trong trường hợp Android theo mặc định, nó sẽ là /system/bin/ls, /system/bin/rm/system/bin/chmod tương ứng), nhưng thay vào đó sẽ gọi trực tiếp các ứng dụng BusyBox nội bộ. Điều này đảm bảo rằng các tập lệnh luôn chạy trong môi trường có thể dự đoán được và luôn có bộ lệnh đầy đủ cho dù nó đang chạy trên phiên bản Android nào. Để buộc lệnh not sử dụng BusyBox, bạn phải gọi tệp thực thi có đường dẫn đầy đủ.

Mỗi tập lệnh shell đơn lẻ chạy trong ngữ cảnh của KernelSU sẽ được thực thi trong shell ash của BusyBox với standalone mode được bật. Đối với những gì liên quan đến nhà phát triển bên thứ 3, điều này bao gồm tất cả các tập lệnh khởi động và tập lệnh cài đặt mô-đun.

Đối với những người muốn sử dụng tính năng "Standalone mode" này bên ngoài KernelSU, có 2 cách để kích hoạt tính năng này:

  1. Đặt biến môi trường ASH_STANDALONE thành 1
    Ví dụ: ASH_STANDALONE=1 /data/adb/ksu/bin/busybox sh <script>
  2. Chuyển đổi bằng các tùy chọn dòng lệnh:
    /data/adb/ksu/bin/busybox sh -o độc lập <script>

Để đảm bảo tất cả shell sh tiếp theo được thực thi cũng chạy ở standalone mode, tùy chọn 1 là phương thức ưu tiên (và đây là những gì KernelSU và KernelSU manager sử dụng nội bộ) vì các biến môi trường được kế thừa xuống các tiến trình con.

sự khác biệt với Magisk

BusyBox của KernelSU hiện đang sử dụng tệp nhị phân được biên dịch trực tiếp từ dự án Magisk. Cảm ơn Magisk! Vì vậy, bạn không phải lo lắng về vấn đề tương thích giữa các tập lệnh BusyBox trong Magisk và KernelSU vì chúng hoàn toàn giống nhau!

Mô-đun hạt nhânSU

Mô-đun KernelSU là một thư mục được đặt trong /data/adb/modules với cấu trúc bên dưới:

txt
/data/adb/modules
-├── .
-├── .
-|
-├── $MODID                  <--- Thư mục được đặt tên bằng ID của mô-đun
-│   │
-│   │      *** Nhận Dạng Mô-đun ***
-│   │
-│   ├── module.prop         <--- Tệp này lưu trữ metadata của mô-đun
-│   │
-│   │      *** Nội Dung Chính ***
-│   │
-│   ├── system              <--- Thư mục này sẽ được gắn kết nếu skip_mount không tồn tại
-│   │   ├── ...
-│   │   ├── ...
-│   │   └── ...
-│   │
-│   │      *** Cờ Trạng Thái ***
-│   │
-│   ├── skip_mount          <--- Nếu tồn tại, KernelSU sẽ KHÔNG gắn kết thư mục hệ thống của bạn
-│   ├── disable             <--- Nếu tồn tại, mô-đun sẽ bị vô hiệu hóa
-│   ├── remove              <--- Nếu tồn tại, mô-đun sẽ bị xóa trong lần khởi động lại tiếp theo
-│   │
-│   │      *** Tệp Tùy Chọn ***
-│   │
-│   ├── post-fs-data.sh     <--- Tập lệnh này sẽ được thực thi trong post-fs-data
-│   ├── post-mount.sh       <--- Tập lệnh này sẽ được thực thi trong post-mount
-│   ├── service.sh          <--- Tập lệnh này sẽ được thực thi trong dịch vụ late_start
-│   ├── boot-completed.sh   <--- Tập lệnh này sẽ được thực thi khi khởi động xong
-|   ├── uninstall.sh        <--- Tập lệnh này sẽ được thực thi khi KernelSU xóa mô-đun của bạn
-│   ├── system.prop         <--- Các thuộc tính trong tệp này sẽ được tải dưới dạng thuộc tính hệ thống bằng resetprop
-│   ├── sepolicy.rule       <--- Quy tắc riêng biệt tùy chỉnh bổ sung
-│   │
-│   │      *** Được Tạo Tự Động, KHÔNG TẠO HOẶC SỬA ĐỔI THỦ CÔNG ***
-│   │
-│   ├── vendor              <--- Một liên kết tượng trưng đến $MODID/system/vendor
-│   ├── product             <--- Một liên kết tượng trưng đến $MODID/system/product
-│   ├── system_ext          <--- Một liên kết tượng trưng đến $MODID/system/system_ext
-│   │
-│   │      *** Mọi tập tin / thư mục bổ sung đều được phép ***
-│   │
-│   ├── ...
-│   └── ...
-|
-├── another_module
-│   ├── .
-│   └── .
-├── .
-├── .

sự khác biệt với Magisk

KernelSU không có hỗ trợ tích hợp cho Zygisk nên không có nội dung liên quan đến Zygisk trong mô-đun. Tuy nhiên, bạn có thể sử dụng ZygiskNext để hỗ trợ các mô-đun Zygisk. Trong trường hợp này, nội dung của mô-đun Zygisk giống hệt với nội dung được Magisk hỗ trợ.

module.prop

module.prop là tệp cấu hình cho mô-đun. Trong KernelSU, nếu một mô-đun không chứa tệp này, nó sẽ không được nhận dạng là mô-đun. Định dạng của tập tin này như sau:

txt
id=<string>
-name=<string>
-version=<string>
-versionCode=<int>
-author=<string>
-description=<string>
  • id phải khớp với biểu thức chính quy này: ^[a-zA-Z][a-zA-Z0-9._-]+$
    ví dụ: ✓ a_module, ✓ a.module, ✓ module-101, ✗ a module, ✗ 1_module, ✗ -a-module
    Đây là mã định danh duy nhất của mô-đun của bạn. Bạn không nên thay đổi nó sau khi được xuất bản.
  • versionCode phải là số nguyên. Điều này được sử dụng để so sánh các phiên bản
  • Các chuỗi khác không được đề cập ở trên có thể là chuỗi một dòng bất kỳ.
  • Đảm bảo sử dụng kiểu ngắt dòng UNIX (LF) chứ không phải Windows (CR+LF) hoặc Macintosh (CR).

Tập lệnh Shell

Vui lòng đọc phần Boot Scripts để hiểu sự khác biệt giữa post-fs-data.shservice.sh. Đối với hầu hết các nhà phát triển mô-đun, service.sh sẽ đủ tốt nếu bạn chỉ cần chạy tập lệnh khởi động, nếu bạn cần chạy tập lệnh sau khi khởi động xong, vui lòng sử dụng boot-completed.sh. Nếu bạn muốn làm gì đó sau khi gắn các lớp phủ, vui lòng sử dụng post-mount.sh.

Trong tất cả các tập lệnh của mô-đun của bạn, vui lòng sử dụng MODDIR=\${0%/*} để lấy đường dẫn thư mục cơ sở của mô-đun của bạn; KHÔNG mã hóa cứng đường dẫn mô-đun của bạn trong tập lệnh.

sự khác biệt với Magisk

Bạn có thể sử dụng biến môi trường KSU để xác định xem tập lệnh đang chạy trong KernelSU hay Magisk. Nếu chạy trong KernelSU, giá trị này sẽ được đặt thành true.

thư mục system

Nội dung của thư mục này sẽ được phủ lên trên phân vùng /system của hệ thống bằng cách sử dụng overlayfs sau khi hệ thống được khởi động. Điều này có nghĩa rằng:

  1. Các file có cùng tên với các file trong thư mục tương ứng trong hệ thống sẽ bị ghi đè bởi các file trong thư mục này.
  2. Các thư mục có cùng tên với thư mục tương ứng trong hệ thống sẽ được gộp với các thư mục trong thư mục này.

Nếu bạn muốn xóa một tập tin hoặc thư mục trong thư mục hệ thống gốc, bạn cần tạo một tập tin có cùng tên với tập tin/thư mục trong thư mục mô-đun bằng cách sử dụng mknod filename c 0 0. Bằng cách này, hệ thống lớp phủ sẽ tự động "whiteout" (Xóa trắng) tệp này như thể nó đã bị xóa (phân vùng /system không thực sự bị thay đổi).

Bạn cũng có thể khai báo một biến có tên REMOVE chứa danh sách các thư mục trong customize.sh để thực hiện các thao tác xóa và KernelSU sẽ tự động thực thi mknod <TARGET> c 0 0 trong các thư mục tương ứng của mô-đun. Ví dụ:

sh
REMOVE="
-/system/app/YouTube
-/system/app/Bloatware
-"

Danh sách trên sẽ thực thi mknod $MODPATH/system/app/YouTuBe c 0 0mknod $MODPATH/system/app/Bloatware c 0 0; và /system/app/YouTube/system/app/Bloatware sẽ bị xóa sau khi mô-đun này có hiệu lực.

Nếu bạn muốn thay thế một thư mục trong hệ thống, bạn cần tạo một thư mục có cùng đường dẫn trong thư mục mô-đun của mình, sau đó đặt thuộc tính setfattr -ntrust.overlay.opaque -v y <TARGET> cho thư mục này. Bằng cách này, hệ thống Overlayfs sẽ tự động thay thế thư mục tương ứng trong hệ thống (mà không thay đổi phân vùng /system).

Bạn có thể khai báo một biến có tên REPLACE trong tệp customize.sh của mình, bao gồm danh sách các thư mục sẽ được thay thế và KernelSU sẽ tự động thực hiện các thao tác tương ứng trong thư mục mô-đun của bạn. Ví dụ:

REPLACE=" /system/app/YouTube /system/app/Bloatware "

Danh sách này sẽ tự động tạo các thư mục $MODPATH/system/app/YouTube$MODPATH/system/app/Bloatware, sau đó thực thi setfattr -ntrusted.overlay.opaque -v y $MODPATH/system/app/ YouTubesetfattr -n Trust.overlay.opaque -v y $MODPATH/system/app/Bloatware. Sau khi mô-đun có hiệu lực, /system/app/YouTube/system/app/Bloatware sẽ được thay thế bằng các thư mục trống.

sự khác biệt với Magisk

Cơ chế không hệ thống của KernelSU được triển khai thông qua các overlayfs của kernel, trong khi Magisk hiện sử dụng magic mount (bind mount). Hai phương pháp triển khai có những khác biệt đáng kể, nhưng mục tiêu cuối cùng đều giống nhau: sửa đổi các tệp /system mà không sửa đổi vật lý phân vùng /system.

Nếu bạn quan tâm đến overlayfs, bạn nên đọc documentation on overlayfs của Kernel Linux.

system.prop

Tệp này có cùng định dạng với build.prop. Mỗi dòng bao gồm [key]=[value].

sepolicy.rule

Nếu mô-đun của bạn yêu cầu một số bản vá lỗi chính sách bổ sung, vui lòng thêm các quy tắc đó vào tệp này. Mỗi dòng trong tệp này sẽ được coi là một tuyên bố chính sách.

Trình cài đặt mô-đun

Trình cài đặt mô-đun KernelSU là mô-đun KernelSU được đóng gói trong tệp zip có thể được flash trong APP KernelSU manager. Trình cài đặt mô-đun KernelSU đơn giản chỉ là mô-đun KernelSU được đóng gói dưới dạng tệp zip.

txt
module.zip
-
-├── customize.sh                       <--- (Tùy chọn, biết thêm chi tiết sau)
-│                                           Tập lệnh này sẽ có nguồn gốc từ update-binary
-├── ...
-├── ...  /* Các tập tin còn lại của mô-đun */
-

WARNING

Mô-đun KernelSU KHÔNG được hỗ trợ để cài đặt trong khôi phục tùy chỉnh!!

Tùy chỉnh

Nếu bạn cần tùy chỉnh quá trình cài đặt mô-đun, bạn có thể tùy ý tạo một tập lệnh trong trình cài đặt có tên customize.sh. Tập lệnh này sẽ được sourced (không được thực thi!) bởi tập lệnh cài đặt mô-đun sau khi tất cả các tệp được trích xuất và các quyền mặc định cũng như văn bản thứ hai được áp dụng. Điều này rất hữu ích nếu mô-đun của bạn yêu cầu thiết lập bổ sung dựa trên ABI của thiết bị hoặc bạn cần đặt các quyền/văn bản thứ hai đặc biệt cho một số tệp mô-đun của mình.

Nếu bạn muốn kiểm soát và tùy chỉnh hoàn toàn quá trình cài đặt, hãy khai báo SKIPUNZIP=1 trong customize.sh để bỏ qua tất cả các bước cài đặt mặc định. Bằng cách đó, customize.sh của bạn sẽ chịu trách nhiệm cài đặt mọi thứ.

Tập lệnh customize.sh chạy trong shell ash BusyBox của KernelSU với "Chế độ độc lập" được bật. Có sẵn các biến và hàm sau:

Biến

  • KSU (bool): biến để đánh dấu script đang chạy trong môi trường KernelSU, và giá trị của biến này sẽ luôn đúng. Bạn có thể sử dụng nó để phân biệt giữa KernelSU và Magisk.
  • KSU_VER (chuỗi): chuỗi phiên bản của KernelSU được cài đặt hiện tại (ví dụ: v0.4.0)
  • KSU_VER_CODE (int): mã phiên bản của KernelSU được cài đặt hiện tại trong không gian người dùng (ví dụ: 10672)
  • KSU_KERNEL_VER_CODE (int): mã phiên bản của KernelSU được cài đặt hiện tại trong không gian kernel (ví dụ: 10672)
  • BOOTMODE (bool): luôn là true trong KernelSU
  • MODPATH (đường dẫn): đường dẫn nơi các tập tin mô-đun của bạn sẽ được cài đặt
  • TMPDIR (đường dẫn): nơi bạn có thể lưu trữ tạm thời các tập tin
  • ZIPFILE (đường dẫn): zip cài đặt mô-đun của bạn
  • ARCH (chuỗi): kiến trúc CPU của thiết bị. Giá trị là arm, arm64, x86 hoặc x64
  • IS64BIT (bool): true nếu $ARCHarm64 hoặc x64
  • API (int): cấp độ API (phiên bản Android) của thiết bị (ví dụ: 23 cho Android 6.0)

WARNING

Trong KernelSU, MAGISK_VER_CODE luôn là 25200 và MAGISK_VER luôn là v25.2. Vui lòng không sử dụng hai biến này để xác định xem nó có chạy trên KernelSU hay không.

Hàm

txt
ui_print <msg>
-    in <msg> ra console
-    Tránh sử dụng 'echo' vì nó sẽ không hiển thị trong console của recovery tùy chỉnh
-
-abort <msg>
-    in thông báo lỗi <msg> ra bàn điều khiển và chấm dứt cài đặt
-    Tránh sử dụng 'exit' vì nó sẽ bỏ qua các bước dọn dẹp chấm dứt
-
-set_perm <target> <owner> <group> <permission> [context]
-    nếu [context] không được đặt, mặc định là "u:object_r:system_file:s0"
-    chức năng này là một shorthand cho các lệnh sau:
-       chown owner.group target
-       chmod permission target
-       chcon context target
-
-set_perm_recursive <directory> <owner> <group> <dirpermission> <filepermission> [context]
-    nếu [context] không được đặt, mặc định là "u:object_r:system_file:s0"
-    đối với tất cả các tệp trong <directory>, nó sẽ gọi:
-       bối cảnh cấp phép tệp của nhóm chủ sở hữu tệp set_perm
-    đối với tất cả các thư mục trong <directory> (bao gồm cả chính nó), nó sẽ gọi:
-       set_perm bối cảnh phân quyền của nhóm chủ sở hữu thư mục

Tập lệnh khởi động

Trong KernelSU, tập lệnh được chia thành hai loại dựa trên chế độ chạy của chúng: chế độ post-fs-data và chế độ dịch vụ late_start:

  • chế độ post-fs-data
    • Giai đoạn này là BLOCKING. Quá trình khởi động bị tạm dừng trước khi thực thi xong hoặc đã trôi qua 10 giây.
    • Các tập lệnh chạy trước khi bất kỳ mô-đun nào được gắn kết. Điều này cho phép nhà phát triển mô-đun tự động điều chỉnh các mô-đun của họ trước khi nó được gắn kết.
    • Giai đoạn này xảy ra trước khi Zygote được khởi động, điều này gần như có ý nghĩa đối với mọi thứ trong Android
    • CẢNH BÁO: sử dụng setprop sẽ làm quá trình khởi động bị nghẽn! Thay vào đó, vui lòng sử dụng resetprop -n <prop_name> <prop_value>.
    • Chỉ chạy tập lệnh ở chế độ này nếu cần thiết.
  • chế độ dịch vụ late_start
    • Giai đoạn này là NON-BLOCKING. Tập lệnh của bạn chạy song song với phần còn lại của quá trình khởi động.
    • Đây là giai đoạn được khuyến nghị để chạy hầu hết các tập lệnh.

Trong KernelSU, tập lệnh khởi động được chia thành hai loại dựa trên vị trí lưu trữ của chúng: tập lệnh chung và tập lệnh mô-đun:

  • Kịch Bản Chung
    • Được đặt trong /data/adb/post-fs-data.d, /data/adb/service.d, /data/adb/post-mount.d hoặc /data/adb/boot- đã hoàn thành.d
    • Chỉ được thực thi nếu tập lệnh được đặt là có thể thực thi được (chmod +x script.sh)
    • Các tập lệnh trong post-fs-data.d chạy ở chế độ post-fs-data và các tập lệnh trong service.d chạy ở chế độ dịch vụ late_start.
    • Các mô-đun KHÔNG thêm các tập lệnh chung trong quá trình cài đặt
  • Tập Lệnh Mô-đun
    • Được đặt trong thư mục riêng của mô-đun
    • Chỉ thực hiện nếu mô-đun được kích hoạt
    • post-fs-data.sh chạy ở chế độ post-fs-data, service.sh chạy ở chế độ dịch vụ late_start, boot-completed.sh chạy khi khởi động xong, post-mount.sh chạy trên overlayfs được gắn kết.

Tất cả các tập lệnh khởi động sẽ chạy trong shell ash BusyBox của KernelSU với "Standalone Mode" được bật.

`,58),e=[o];function l(h,i,p,r,d,g){return s(),t("div",null,e)}const y=n(a,[["render",l]]);export{m as __pageData,y as default}; +import{_ as n,o as s,c as e,Q as a}from"./chunks/framework.ec8f7e8e.js";const y=JSON.parse('{"title":"Hướng dẫn mô-đun","description":"","frontmatter":{},"headers":[],"relativePath":"vi_VN/guide/module.md","filePath":"vi_VN/guide/module.md"}'),t={name:"vi_VN/guide/module.md"},c=a(`

Hướng dẫn mô-đun

KernelSU cung cấp một cơ chế mô-đun giúp đạt được hiệu quả sửa đổi thư mục hệ thống trong khi vẫn duy trì tính toàn vẹn của phân vùng system. Cơ chế này thường được gọi là "systemless".

Cơ chế mô-đun của KernelSU gần giống với Magisk. Nếu bạn đã quen với việc phát triển mô-đun Magisk thì việc phát triển các mô-đun KernelSU cũng rất tương tự. Bạn có thể bỏ qua phần giới thiệu các mô-đun bên dưới và chỉ cần đọc difference-with-magisk.

Busybox

KernelSU cung cấp tính năng nhị phân BusyBox hoàn chỉnh (bao gồm hỗ trợ SELinux đầy đủ). Tệp thực thi được đặt tại /data/adb/ksu/bin/busybox. BusyBox của KernelSU hỗ trợ "ASH Standalone Shell Mode" có thể chuyển đổi thời gian chạy. Standalone mode này có nghĩa là khi chạy trong shell ash của BusyBox, mọi lệnh sẽ trực tiếp sử dụng applet trong BusyBox, bất kể cái gì được đặt là PATH. Ví dụ: các lệnh như ls, rm, chmod sẽ KHÔNG sử dụng những gì có trong PATH (trong trường hợp Android theo mặc định, nó sẽ là /system/bin/ls, /system/bin/rm/system/bin/chmod tương ứng), nhưng thay vào đó sẽ gọi trực tiếp các ứng dụng BusyBox nội bộ. Điều này đảm bảo rằng các tập lệnh luôn chạy trong môi trường có thể dự đoán được và luôn có bộ lệnh đầy đủ cho dù nó đang chạy trên phiên bản Android nào. Để buộc lệnh not sử dụng BusyBox, bạn phải gọi tệp thực thi có đường dẫn đầy đủ.

Mỗi tập lệnh shell đơn lẻ chạy trong ngữ cảnh của KernelSU sẽ được thực thi trong shell ash của BusyBox với standalone mode được bật. Đối với những gì liên quan đến nhà phát triển bên thứ 3, điều này bao gồm tất cả các tập lệnh khởi động và tập lệnh cài đặt mô-đun.

Đối với những người muốn sử dụng tính năng "Standalone mode" này bên ngoài KernelSU, có 2 cách để kích hoạt tính năng này:

  1. Đặt biến môi trường ASH_STANDALONE thành 1
    Ví dụ: ASH_STANDALONE=1 /data/adb/ksu/bin/busybox sh <script>
  2. Chuyển đổi bằng các tùy chọn dòng lệnh:
    /data/adb/ksu/bin/busybox sh -o độc lập <script>

Để đảm bảo tất cả shell sh tiếp theo được thực thi cũng chạy ở standalone mode, tùy chọn 1 là phương thức ưu tiên (và đây là những gì KernelSU và KernelSU manager sử dụng nội bộ) vì các biến môi trường được kế thừa xuống các tiến trình con.

sự khác biệt với Magisk

BusyBox của KernelSU hiện đang sử dụng tệp nhị phân được biên dịch trực tiếp từ dự án Magisk. Cảm ơn Magisk! Vì vậy, bạn không phải lo lắng về vấn đề tương thích giữa các tập lệnh BusyBox trong Magisk và KernelSU vì chúng hoàn toàn giống nhau!

Mô-đun hạt nhânSU

Mô-đun KernelSU là một thư mục được đặt trong /data/adb/modules với cấu trúc bên dưới:

txt
/data/adb/modules
+├── .
+├── .
+|
+├── $MODID                  <--- Thư mục được đặt tên bằng ID của mô-đun
+│   │
+│   │      *** Nhận Dạng Mô-đun ***
+│   │
+│   ├── module.prop         <--- Tệp này lưu trữ metadata của mô-đun
+│   │
+│   │      *** Nội Dung Chính ***
+│   │
+│   ├── system              <--- Thư mục này sẽ được gắn kết nếu skip_mount không tồn tại
+│   │   ├── ...
+│   │   ├── ...
+│   │   └── ...
+│   │
+│   │      *** Cờ Trạng Thái ***
+│   │
+│   ├── skip_mount          <--- Nếu tồn tại, KernelSU sẽ KHÔNG gắn kết thư mục hệ thống của bạn
+│   ├── disable             <--- Nếu tồn tại, mô-đun sẽ bị vô hiệu hóa
+│   ├── remove              <--- Nếu tồn tại, mô-đun sẽ bị xóa trong lần khởi động lại tiếp theo
+│   │
+│   │      *** Tệp Tùy Chọn ***
+│   │
+│   ├── post-fs-data.sh     <--- Tập lệnh này sẽ được thực thi trong post-fs-data
+│   ├── post-mount.sh       <--- Tập lệnh này sẽ được thực thi trong post-mount
+│   ├── service.sh          <--- Tập lệnh này sẽ được thực thi trong dịch vụ late_start
+│   ├── boot-completed.sh   <--- Tập lệnh này sẽ được thực thi khi khởi động xong
+|   ├── uninstall.sh        <--- Tập lệnh này sẽ được thực thi khi KernelSU xóa mô-đun của bạn
+│   ├── system.prop         <--- Các thuộc tính trong tệp này sẽ được tải dưới dạng thuộc tính hệ thống bằng resetprop
+│   ├── sepolicy.rule       <--- Quy tắc riêng biệt tùy chỉnh bổ sung
+│   │
+│   │      *** Được Tạo Tự Động, KHÔNG TẠO HOẶC SỬA ĐỔI THỦ CÔNG ***
+│   │
+│   ├── vendor              <--- Một liên kết tượng trưng đến $MODID/system/vendor
+│   ├── product             <--- Một liên kết tượng trưng đến $MODID/system/product
+│   ├── system_ext          <--- Một liên kết tượng trưng đến $MODID/system/system_ext
+│   │
+│   │      *** Mọi tập tin / thư mục bổ sung đều được phép ***
+│   │
+│   ├── ...
+│   └── ...
+|
+├── another_module
+│   ├── .
+│   └── .
+├── .
+├── .
/data/adb/modules
+├── .
+├── .
+|
+├── $MODID                  <--- Thư mục được đặt tên bằng ID của mô-đun
+│   │
+│   │      *** Nhận Dạng Mô-đun ***
+│   │
+│   ├── module.prop         <--- Tệp này lưu trữ metadata của mô-đun
+│   │
+│   │      *** Nội Dung Chính ***
+│   │
+│   ├── system              <--- Thư mục này sẽ được gắn kết nếu skip_mount không tồn tại
+│   │   ├── ...
+│   │   ├── ...
+│   │   └── ...
+│   │
+│   │      *** Cờ Trạng Thái ***
+│   │
+│   ├── skip_mount          <--- Nếu tồn tại, KernelSU sẽ KHÔNG gắn kết thư mục hệ thống của bạn
+│   ├── disable             <--- Nếu tồn tại, mô-đun sẽ bị vô hiệu hóa
+│   ├── remove              <--- Nếu tồn tại, mô-đun sẽ bị xóa trong lần khởi động lại tiếp theo
+│   │
+│   │      *** Tệp Tùy Chọn ***
+│   │
+│   ├── post-fs-data.sh     <--- Tập lệnh này sẽ được thực thi trong post-fs-data
+│   ├── post-mount.sh       <--- Tập lệnh này sẽ được thực thi trong post-mount
+│   ├── service.sh          <--- Tập lệnh này sẽ được thực thi trong dịch vụ late_start
+│   ├── boot-completed.sh   <--- Tập lệnh này sẽ được thực thi khi khởi động xong
+|   ├── uninstall.sh        <--- Tập lệnh này sẽ được thực thi khi KernelSU xóa mô-đun của bạn
+│   ├── system.prop         <--- Các thuộc tính trong tệp này sẽ được tải dưới dạng thuộc tính hệ thống bằng resetprop
+│   ├── sepolicy.rule       <--- Quy tắc riêng biệt tùy chỉnh bổ sung
+│   │
+│   │      *** Được Tạo Tự Động, KHÔNG TẠO HOẶC SỬA ĐỔI THỦ CÔNG ***
+│   │
+│   ├── vendor              <--- Một liên kết tượng trưng đến $MODID/system/vendor
+│   ├── product             <--- Một liên kết tượng trưng đến $MODID/system/product
+│   ├── system_ext          <--- Một liên kết tượng trưng đến $MODID/system/system_ext
+│   │
+│   │      *** Mọi tập tin / thư mục bổ sung đều được phép ***
+│   │
+│   ├── ...
+│   └── ...
+|
+├── another_module
+│   ├── .
+│   └── .
+├── .
+├── .

sự khác biệt với Magisk

KernelSU không có hỗ trợ tích hợp cho Zygisk nên không có nội dung liên quan đến Zygisk trong mô-đun. Tuy nhiên, bạn có thể sử dụng ZygiskNext để hỗ trợ các mô-đun Zygisk. Trong trường hợp này, nội dung của mô-đun Zygisk giống hệt với nội dung được Magisk hỗ trợ.

module.prop

module.prop là tệp cấu hình cho mô-đun. Trong KernelSU, nếu một mô-đun không chứa tệp này, nó sẽ không được nhận dạng là mô-đun. Định dạng của tập tin này như sau:

txt
id=<string>
+name=<string>
+version=<string>
+versionCode=<int>
+author=<string>
+description=<string>
id=<string>
+name=<string>
+version=<string>
+versionCode=<int>
+author=<string>
+description=<string>
  • id phải khớp với biểu thức chính quy này: ^[a-zA-Z][a-zA-Z0-9._-]+$
    ví dụ: ✓ a_module, ✓ a.module, ✓ module-101, ✗ a module, ✗ 1_module, ✗ -a-module
    Đây là mã định danh duy nhất của mô-đun của bạn. Bạn không nên thay đổi nó sau khi được xuất bản.
  • versionCode phải là số nguyên. Điều này được sử dụng để so sánh các phiên bản
  • Các chuỗi khác không được đề cập ở trên có thể là chuỗi một dòng bất kỳ.
  • Đảm bảo sử dụng kiểu ngắt dòng UNIX (LF) chứ không phải Windows (CR+LF) hoặc Macintosh (CR).

Tập lệnh Shell

Vui lòng đọc phần Boot Scripts để hiểu sự khác biệt giữa post-fs-data.shservice.sh. Đối với hầu hết các nhà phát triển mô-đun, service.sh sẽ đủ tốt nếu bạn chỉ cần chạy tập lệnh khởi động, nếu bạn cần chạy tập lệnh sau khi khởi động xong, vui lòng sử dụng boot-completed.sh. Nếu bạn muốn làm gì đó sau khi gắn các lớp phủ, vui lòng sử dụng post-mount.sh.

Trong tất cả các tập lệnh của mô-đun của bạn, vui lòng sử dụng MODDIR=\${0%/*} để lấy đường dẫn thư mục cơ sở của mô-đun của bạn; KHÔNG mã hóa cứng đường dẫn mô-đun của bạn trong tập lệnh.

sự khác biệt với Magisk

Bạn có thể sử dụng biến môi trường KSU để xác định xem tập lệnh đang chạy trong KernelSU hay Magisk. Nếu chạy trong KernelSU, giá trị này sẽ được đặt thành true.

thư mục system

Nội dung của thư mục này sẽ được phủ lên trên phân vùng /system của hệ thống bằng cách sử dụng overlayfs sau khi hệ thống được khởi động. Điều này có nghĩa rằng:

  1. Các file có cùng tên với các file trong thư mục tương ứng trong hệ thống sẽ bị ghi đè bởi các file trong thư mục này.
  2. Các thư mục có cùng tên với thư mục tương ứng trong hệ thống sẽ được gộp với các thư mục trong thư mục này.

Nếu bạn muốn xóa một tập tin hoặc thư mục trong thư mục hệ thống gốc, bạn cần tạo một tập tin có cùng tên với tập tin/thư mục trong thư mục mô-đun bằng cách sử dụng mknod filename c 0 0. Bằng cách này, hệ thống lớp phủ sẽ tự động "whiteout" (Xóa trắng) tệp này như thể nó đã bị xóa (phân vùng /system không thực sự bị thay đổi).

Bạn cũng có thể khai báo một biến có tên REMOVE chứa danh sách các thư mục trong customize.sh để thực hiện các thao tác xóa và KernelSU sẽ tự động thực thi mknod <TARGET> c 0 0 trong các thư mục tương ứng của mô-đun. Ví dụ:

sh
REMOVE="
+/system/app/YouTube
+/system/app/Bloatware
+"
REMOVE="
+/system/app/YouTube
+/system/app/Bloatware
+"

Danh sách trên sẽ thực thi mknod $MODPATH/system/app/YouTuBe c 0 0mknod $MODPATH/system/app/Bloatware c 0 0; và /system/app/YouTube/system/app/Bloatware sẽ bị xóa sau khi mô-đun này có hiệu lực.

Nếu bạn muốn thay thế một thư mục trong hệ thống, bạn cần tạo một thư mục có cùng đường dẫn trong thư mục mô-đun của mình, sau đó đặt thuộc tính setfattr -ntrust.overlay.opaque -v y <TARGET> cho thư mục này. Bằng cách này, hệ thống Overlayfs sẽ tự động thay thế thư mục tương ứng trong hệ thống (mà không thay đổi phân vùng /system).

Bạn có thể khai báo một biến có tên REPLACE trong tệp customize.sh của mình, bao gồm danh sách các thư mục sẽ được thay thế và KernelSU sẽ tự động thực hiện các thao tác tương ứng trong thư mục mô-đun của bạn. Ví dụ:

REPLACE=" /system/app/YouTube /system/app/Bloatware "

Danh sách này sẽ tự động tạo các thư mục $MODPATH/system/app/YouTube$MODPATH/system/app/Bloatware, sau đó thực thi setfattr -ntrusted.overlay.opaque -v y $MODPATH/system/app/ YouTubesetfattr -n Trust.overlay.opaque -v y $MODPATH/system/app/Bloatware. Sau khi mô-đun có hiệu lực, /system/app/YouTube/system/app/Bloatware sẽ được thay thế bằng các thư mục trống.

sự khác biệt với Magisk

Cơ chế không hệ thống của KernelSU được triển khai thông qua các overlayfs của kernel, trong khi Magisk hiện sử dụng magic mount (bind mount). Hai phương pháp triển khai có những khác biệt đáng kể, nhưng mục tiêu cuối cùng đều giống nhau: sửa đổi các tệp /system mà không sửa đổi vật lý phân vùng /system.

Nếu bạn quan tâm đến overlayfs, bạn nên đọc documentation on overlayfs của Kernel Linux.

system.prop

Tệp này có cùng định dạng với build.prop. Mỗi dòng bao gồm [key]=[value].

sepolicy.rule

Nếu mô-đun của bạn yêu cầu một số bản vá lỗi chính sách bổ sung, vui lòng thêm các quy tắc đó vào tệp này. Mỗi dòng trong tệp này sẽ được coi là một tuyên bố chính sách.

Trình cài đặt mô-đun

Trình cài đặt mô-đun KernelSU là mô-đun KernelSU được đóng gói trong tệp zip có thể được flash trong APP KernelSU manager. Trình cài đặt mô-đun KernelSU đơn giản chỉ là mô-đun KernelSU được đóng gói dưới dạng tệp zip.

txt
module.zip
+
+├── customize.sh                       <--- (Tùy chọn, biết thêm chi tiết sau)
+│                                           Tập lệnh này sẽ có nguồn gốc từ update-binary
+├── ...
+├── ...  /* Các tập tin còn lại của mô-đun */
+
module.zip
+
+├── customize.sh                       <--- (Tùy chọn, biết thêm chi tiết sau)
+│                                           Tập lệnh này sẽ có nguồn gốc từ update-binary
+├── ...
+├── ...  /* Các tập tin còn lại của mô-đun */
+

WARNING

Mô-đun KernelSU KHÔNG được hỗ trợ để cài đặt trong khôi phục tùy chỉnh!!

Tùy chỉnh

Nếu bạn cần tùy chỉnh quá trình cài đặt mô-đun, bạn có thể tùy ý tạo một tập lệnh trong trình cài đặt có tên customize.sh. Tập lệnh này sẽ được sourced (không được thực thi!) bởi tập lệnh cài đặt mô-đun sau khi tất cả các tệp được trích xuất và các quyền mặc định cũng như văn bản thứ hai được áp dụng. Điều này rất hữu ích nếu mô-đun của bạn yêu cầu thiết lập bổ sung dựa trên ABI của thiết bị hoặc bạn cần đặt các quyền/văn bản thứ hai đặc biệt cho một số tệp mô-đun của mình.

Nếu bạn muốn kiểm soát và tùy chỉnh hoàn toàn quá trình cài đặt, hãy khai báo SKIPUNZIP=1 trong customize.sh để bỏ qua tất cả các bước cài đặt mặc định. Bằng cách đó, customize.sh của bạn sẽ chịu trách nhiệm cài đặt mọi thứ.

Tập lệnh customize.sh chạy trong shell ash BusyBox của KernelSU với "Chế độ độc lập" được bật. Có sẵn các biến và hàm sau:

Biến

  • KSU (bool): biến để đánh dấu script đang chạy trong môi trường KernelSU, và giá trị của biến này sẽ luôn đúng. Bạn có thể sử dụng nó để phân biệt giữa KernelSU và Magisk.
  • KSU_VER (chuỗi): chuỗi phiên bản của KernelSU được cài đặt hiện tại (ví dụ: v0.4.0)
  • KSU_VER_CODE (int): mã phiên bản của KernelSU được cài đặt hiện tại trong không gian người dùng (ví dụ: 10672)
  • KSU_KERNEL_VER_CODE (int): mã phiên bản của KernelSU được cài đặt hiện tại trong không gian kernel (ví dụ: 10672)
  • BOOTMODE (bool): luôn là true trong KernelSU
  • MODPATH (đường dẫn): đường dẫn nơi các tập tin mô-đun của bạn sẽ được cài đặt
  • TMPDIR (đường dẫn): nơi bạn có thể lưu trữ tạm thời các tập tin
  • ZIPFILE (đường dẫn): zip cài đặt mô-đun của bạn
  • ARCH (chuỗi): kiến trúc CPU của thiết bị. Giá trị là arm, arm64, x86 hoặc x64
  • IS64BIT (bool): true nếu $ARCHarm64 hoặc x64
  • API (int): cấp độ API (phiên bản Android) của thiết bị (ví dụ: 23 cho Android 6.0)

WARNING

Trong KernelSU, MAGISK_VER_CODE luôn là 25200 và MAGISK_VER luôn là v25.2. Vui lòng không sử dụng hai biến này để xác định xem nó có chạy trên KernelSU hay không.

Hàm

txt
ui_print <msg>
+    in <msg> ra console
+    Tránh sử dụng 'echo' vì nó sẽ không hiển thị trong console của recovery tùy chỉnh
+
+abort <msg>
+    in thông báo lỗi <msg> ra bàn điều khiển và chấm dứt cài đặt
+    Tránh sử dụng 'exit' vì nó sẽ bỏ qua các bước dọn dẹp chấm dứt
+
+set_perm <target> <owner> <group> <permission> [context]
+    nếu [context] không được đặt, mặc định là "u:object_r:system_file:s0"
+    chức năng này là một shorthand cho các lệnh sau:
+       chown owner.group target
+       chmod permission target
+       chcon context target
+
+set_perm_recursive <directory> <owner> <group> <dirpermission> <filepermission> [context]
+    nếu [context] không được đặt, mặc định là "u:object_r:system_file:s0"
+    đối với tất cả các tệp trong <directory>, nó sẽ gọi:
+       bối cảnh cấp phép tệp của nhóm chủ sở hữu tệp set_perm
+    đối với tất cả các thư mục trong <directory> (bao gồm cả chính nó), nó sẽ gọi:
+       set_perm bối cảnh phân quyền của nhóm chủ sở hữu thư mục
ui_print <msg>
+    in <msg> ra console
+    Tránh sử dụng 'echo' vì nó sẽ không hiển thị trong console của recovery tùy chỉnh
+
+abort <msg>
+    in thông báo lỗi <msg> ra bàn điều khiển và chấm dứt cài đặt
+    Tránh sử dụng 'exit' vì nó sẽ bỏ qua các bước dọn dẹp chấm dứt
+
+set_perm <target> <owner> <group> <permission> [context]
+    nếu [context] không được đặt, mặc định là "u:object_r:system_file:s0"
+    chức năng này là một shorthand cho các lệnh sau:
+       chown owner.group target
+       chmod permission target
+       chcon context target
+
+set_perm_recursive <directory> <owner> <group> <dirpermission> <filepermission> [context]
+    nếu [context] không được đặt, mặc định là "u:object_r:system_file:s0"
+    đối với tất cả các tệp trong <directory>, nó sẽ gọi:
+       bối cảnh cấp phép tệp của nhóm chủ sở hữu tệp set_perm
+    đối với tất cả các thư mục trong <directory> (bao gồm cả chính nó), nó sẽ gọi:
+       set_perm bối cảnh phân quyền của nhóm chủ sở hữu thư mục

Tập lệnh khởi động

Trong KernelSU, tập lệnh được chia thành hai loại dựa trên chế độ chạy của chúng: chế độ post-fs-data và chế độ dịch vụ late_start:

  • chế độ post-fs-data
    • Giai đoạn này là BLOCKING. Quá trình khởi động bị tạm dừng trước khi thực thi xong hoặc đã trôi qua 10 giây.
    • Các tập lệnh chạy trước khi bất kỳ mô-đun nào được gắn kết. Điều này cho phép nhà phát triển mô-đun tự động điều chỉnh các mô-đun của họ trước khi nó được gắn kết.
    • Giai đoạn này xảy ra trước khi Zygote được khởi động, điều này gần như có ý nghĩa đối với mọi thứ trong Android
    • CẢNH BÁO: sử dụng setprop sẽ làm quá trình khởi động bị nghẽn! Thay vào đó, vui lòng sử dụng resetprop -n <prop_name> <prop_value>.
    • Chỉ chạy tập lệnh ở chế độ này nếu cần thiết.
  • chế độ dịch vụ late_start
    • Giai đoạn này là NON-BLOCKING. Tập lệnh của bạn chạy song song với phần còn lại của quá trình khởi động.
    • Đây là giai đoạn được khuyến nghị để chạy hầu hết các tập lệnh.

Trong KernelSU, tập lệnh khởi động được chia thành hai loại dựa trên vị trí lưu trữ của chúng: tập lệnh chung và tập lệnh mô-đun:

  • Kịch Bản Chung
    • Được đặt trong /data/adb/post-fs-data.d, /data/adb/service.d, /data/adb/post-mount.d hoặc /data/adb/boot- đã hoàn thành.d
    • Chỉ được thực thi nếu tập lệnh được đặt là có thể thực thi được (chmod +x script.sh)
    • Các tập lệnh trong post-fs-data.d chạy ở chế độ post-fs-data và các tập lệnh trong service.d chạy ở chế độ dịch vụ late_start.
    • Các mô-đun KHÔNG thêm các tập lệnh chung trong quá trình cài đặt
  • Tập Lệnh Mô-đun
    • Được đặt trong thư mục riêng của mô-đun
    • Chỉ thực hiện nếu mô-đun được kích hoạt
    • post-fs-data.sh chạy ở chế độ post-fs-data, service.sh chạy ở chế độ dịch vụ late_start, boot-completed.sh chạy khi khởi động xong, post-mount.sh chạy trên overlayfs được gắn kết.

Tất cả các tập lệnh khởi động sẽ chạy trong shell ash BusyBox của KernelSU với "Standalone Mode" được bật.

`,58),l=[c];function o(p,i,h,r,g,d){return s(),e("div",null,l)}const m=n(t,[["render",o]]);export{y as __pageData,m as default}; diff --git a/assets/vi_VN_guide_module.md.d89b5b5b.lean.js b/assets/vi_VN_guide_module.md.d89b5b5b.lean.js new file mode 100644 index 000000000000..a39512237a09 --- /dev/null +++ b/assets/vi_VN_guide_module.md.d89b5b5b.lean.js @@ -0,0 +1 @@ +import{_ as n,o as s,c as e,Q as a}from"./chunks/framework.ec8f7e8e.js";const y=JSON.parse('{"title":"Hướng dẫn mô-đun","description":"","frontmatter":{},"headers":[],"relativePath":"vi_VN/guide/module.md","filePath":"vi_VN/guide/module.md"}'),t={name:"vi_VN/guide/module.md"},c=a("",58),l=[c];function o(p,i,h,r,g,d){return s(),e("div",null,l)}const m=n(t,[["render",o]]);export{y as __pageData,m as default}; diff --git a/assets/vi_VN_guide_rescue-from-bootloop.md.d796809c.js b/assets/vi_VN_guide_rescue-from-bootloop.md.dc2854ff.js similarity index 98% rename from assets/vi_VN_guide_rescue-from-bootloop.md.d796809c.js rename to assets/vi_VN_guide_rescue-from-bootloop.md.dc2854ff.js index f8b2357d79a9..b56a2d290a2c 100644 --- a/assets/vi_VN_guide_rescue-from-bootloop.md.d796809c.js +++ b/assets/vi_VN_guide_rescue-from-bootloop.md.dc2854ff.js @@ -1 +1 @@ -import{_ as n,o as h,c as t,O as c}from"./chunks/framework.43781440.js";const m=JSON.parse('{"title":"Cứu khỏi bootloop (Vòng lặp khởi động)","description":"","frontmatter":{},"headers":[],"relativePath":"vi_VN/guide/rescue-from-bootloop.md","filePath":"vi_VN/guide/rescue-from-bootloop.md"}'),i={name:"vi_VN/guide/rescue-from-bootloop.md"},o=c('

Cứu khỏi bootloop (Vòng lặp khởi động)

Khi flash một thiết bị, chúng ta có thể gặp phải tình trạng máy "bị brick". Về lý thuyết, nếu bạn chỉ sử dụng fastboot để flash phân vùng boot hoặc cài đặt các mô-đun không phù hợp khiến máy không khởi động được thì điều này có thể được khắc phục bằng các thao tác thích hợp. Tài liệu này nhằm mục đích cung cấp một số phương pháp khẩn cấp để giúp bạn khôi phục từ thiết bị "bị brick".

Brick bởi flash vào phân vùng boot

Trong KernelSU, các tình huống sau có thể gây ra lỗi khởi động khi flash phân vùng khởi động:

  1. Bạn flash image boot sai định dạng. Ví dụ: nếu định dạng khởi động điện thoại của bạn là gz, nhưng bạn flash image định dạng lz4 thì điện thoại sẽ không thể khởi động.
  2. Điện thoại của bạn cần tắt xác minh AVB để khởi động bình thường (thường yêu cầu xóa tất cả dữ liệu trên điện thoại).
  3. Kernel của bạn có một số lỗi hoặc không phù hợp để flash điện thoại của bạn.

Bất kể tình huống thế nào, bạn có thể khôi phục bằng cách flash boot image gốc. Do đó, khi bắt đầu hướng dẫn cài đặt, chúng tôi thực sự khuyên bạn nên sao lưu boot image gốc trước khi flash. Nếu chưa sao lưu, bạn có thể lấy boot image gốc từ người dùng khác có cùng thiết bị với bạn hoặc từ chương trình cơ sở chính thức (official firmware).

Brick bởi mô-đun

Việc cài đặt mô-đun có thể là nguyên nhân phổ biến hơn khiến thiết bị của bạn bị brick, nhưng chúng tôi phải nghiêm túc cảnh báo bạn: Không cài đặt mô-đun từ các nguồn không xác định! Vì các mô-đun có đặc quyền root nên chúng có thể gây ra thiệt hại không thể khắc phục cho thiết bị của bạn!

Mô-đun bình thường

Nếu bạn đã flash một mô-đun đã được chứng minh là an toàn nhưng khiến thiết bị của bạn không khởi động được thì tình huống này có thể dễ dàng phục hồi trong KernelSU mà không phải lo lắng gì. KernelSU có các cơ chế tích hợp sẵn để giải cứu thiết bị của bạn, bao gồm:

  1. Cập nhật AB
  2. Cứu bằng cách nhấn Giảm âm lượng

Cập nhật AB

Các bản cập nhật mô-đun của KernelSU lấy cảm hứng từ cơ chế cập nhật AB của hệ thống Android được sử dụng trong các bản cập nhật OTA. Nếu bạn cài đặt một mô-đun mới hoặc cập nhật mô-đun hiện có, nó sẽ không trực tiếp sửa đổi tệp mô-đun hiện đang sử dụng. Thay vào đó, tất cả các mô-đun sẽ được tích hợp vào một hình ảnh cập nhật khác. Sau khi hệ thống được khởi động lại, nó sẽ cố gắng bắt đầu sử dụng hình ảnh cập nhật này. Nếu hệ thống Android khởi động thành công, các mô-đun sẽ được cập nhật thực sự.

Vì vậy, phương pháp đơn giản và được sử dụng phổ biến nhất để cứu thiết bị của bạn là buộc khởi động lại. Nếu bạn không thể khởi động hệ thống của mình sau khi flash một mô-đun, bạn có thể nhấn và giữ nút nguồn trong hơn 10 giây và hệ thống sẽ tự động khởi động lại; sau khi khởi động lại, nó sẽ quay trở lại trạng thái trước khi cập nhật mô-đun và các mô-đun được cập nhật trước đó sẽ tự động bị tắt.

Cứu bằng cách nhấn Giảm âm lượng

Nếu bản cập nhật AB vẫn không giải quyết được vấn đề, bạn có thể thử sử dụng Chế độ an toàn. Ở Chế độ an toàn, tất cả các mô-đun đều bị tắt.

Có hai cách để vào Chế độ an toàn:

  1. Chế Độ An Toàn tích hợp (built-in Safe Mode) của một số hệ thống; một số hệ thống có Chế độ an toàn tích hợp có thể được truy cập bằng cách nhấn và giữ nút giảm âm lượng, trong khi những hệ thống khác (chẳng hạn như MIUI) có thể bật Chế Độ An Toàn trong Recovery. Khi vào Chế Độ An Toàn của hệ thống, KernelSU cũng sẽ vào Chế Độ An Toàn và tự động tắt các mô-đun.
  2. Chế Độ An Toàn tích hợp (built-in Safe Mode) của KernelSU; phương pháp thao tác là nhấn phím giảm âm lượng liên tục hơn ba lần sau màn hình khởi động đầu tiên. Lưu ý là nhấn-thả, nhấn-thả, nhấn-thả chứ không phải nhấn giữ.

Sau khi vào chế độ an toàn, tất cả các mô-đun trên trang mô-đun của KernelSU Manager đều bị tắt nhưng bạn có thể thực hiện thao tác "gỡ cài đặt" để gỡ cài đặt bất kỳ mô-đun nào có thể gây ra sự cố.

Chế độ an toàn tích hợp được triển khai trong kernel, do đó không có khả năng thiếu các sự kiện chính do bị chặn. Tuy nhiên, đối với các hạt nhân không phải GKI, có thể cần phải tích hợp mã thủ công và bạn có thể tham khảo tài liệu chính thức để được hướng dẫn.

Mô-đun độc hại

Nếu các phương pháp trên không thể cứu được thiết bị của bạn thì rất có thể mô-đun bạn cài đặt có hoạt động độc hại hoặc đã làm hỏng thiết bị của bạn thông qua các phương tiện khác. Trong trường hợp này, chỉ có hai gợi ý:

  1. Xóa sạch dữ liệu và flash hệ thống chính thức.
  2. Tham khảo dịch vụ hậu mãi.
',23),a=[o];function g(l,u,r,b,e,p){return h(),t("div",null,a)}const k=n(i,[["render",g]]);export{m as __pageData,k as default}; +import{_ as n,o as h,c as t,Q as c}from"./chunks/framework.ec8f7e8e.js";const m=JSON.parse('{"title":"Cứu khỏi bootloop (Vòng lặp khởi động)","description":"","frontmatter":{},"headers":[],"relativePath":"vi_VN/guide/rescue-from-bootloop.md","filePath":"vi_VN/guide/rescue-from-bootloop.md"}'),i={name:"vi_VN/guide/rescue-from-bootloop.md"},o=c('

Cứu khỏi bootloop (Vòng lặp khởi động)

Khi flash một thiết bị, chúng ta có thể gặp phải tình trạng máy "bị brick". Về lý thuyết, nếu bạn chỉ sử dụng fastboot để flash phân vùng boot hoặc cài đặt các mô-đun không phù hợp khiến máy không khởi động được thì điều này có thể được khắc phục bằng các thao tác thích hợp. Tài liệu này nhằm mục đích cung cấp một số phương pháp khẩn cấp để giúp bạn khôi phục từ thiết bị "bị brick".

Brick bởi flash vào phân vùng boot

Trong KernelSU, các tình huống sau có thể gây ra lỗi khởi động khi flash phân vùng khởi động:

  1. Bạn flash image boot sai định dạng. Ví dụ: nếu định dạng khởi động điện thoại của bạn là gz, nhưng bạn flash image định dạng lz4 thì điện thoại sẽ không thể khởi động.
  2. Điện thoại của bạn cần tắt xác minh AVB để khởi động bình thường (thường yêu cầu xóa tất cả dữ liệu trên điện thoại).
  3. Kernel của bạn có một số lỗi hoặc không phù hợp để flash điện thoại của bạn.

Bất kể tình huống thế nào, bạn có thể khôi phục bằng cách flash boot image gốc. Do đó, khi bắt đầu hướng dẫn cài đặt, chúng tôi thực sự khuyên bạn nên sao lưu boot image gốc trước khi flash. Nếu chưa sao lưu, bạn có thể lấy boot image gốc từ người dùng khác có cùng thiết bị với bạn hoặc từ chương trình cơ sở chính thức (official firmware).

Brick bởi mô-đun

Việc cài đặt mô-đun có thể là nguyên nhân phổ biến hơn khiến thiết bị của bạn bị brick, nhưng chúng tôi phải nghiêm túc cảnh báo bạn: Không cài đặt mô-đun từ các nguồn không xác định! Vì các mô-đun có đặc quyền root nên chúng có thể gây ra thiệt hại không thể khắc phục cho thiết bị của bạn!

Mô-đun bình thường

Nếu bạn đã flash một mô-đun đã được chứng minh là an toàn nhưng khiến thiết bị của bạn không khởi động được thì tình huống này có thể dễ dàng phục hồi trong KernelSU mà không phải lo lắng gì. KernelSU có các cơ chế tích hợp sẵn để giải cứu thiết bị của bạn, bao gồm:

  1. Cập nhật AB
  2. Cứu bằng cách nhấn Giảm âm lượng

Cập nhật AB

Các bản cập nhật mô-đun của KernelSU lấy cảm hứng từ cơ chế cập nhật AB của hệ thống Android được sử dụng trong các bản cập nhật OTA. Nếu bạn cài đặt một mô-đun mới hoặc cập nhật mô-đun hiện có, nó sẽ không trực tiếp sửa đổi tệp mô-đun hiện đang sử dụng. Thay vào đó, tất cả các mô-đun sẽ được tích hợp vào một hình ảnh cập nhật khác. Sau khi hệ thống được khởi động lại, nó sẽ cố gắng bắt đầu sử dụng hình ảnh cập nhật này. Nếu hệ thống Android khởi động thành công, các mô-đun sẽ được cập nhật thực sự.

Vì vậy, phương pháp đơn giản và được sử dụng phổ biến nhất để cứu thiết bị của bạn là buộc khởi động lại. Nếu bạn không thể khởi động hệ thống của mình sau khi flash một mô-đun, bạn có thể nhấn và giữ nút nguồn trong hơn 10 giây và hệ thống sẽ tự động khởi động lại; sau khi khởi động lại, nó sẽ quay trở lại trạng thái trước khi cập nhật mô-đun và các mô-đun được cập nhật trước đó sẽ tự động bị tắt.

Cứu bằng cách nhấn Giảm âm lượng

Nếu bản cập nhật AB vẫn không giải quyết được vấn đề, bạn có thể thử sử dụng Chế độ an toàn. Ở Chế độ an toàn, tất cả các mô-đun đều bị tắt.

Có hai cách để vào Chế độ an toàn:

  1. Chế Độ An Toàn tích hợp (built-in Safe Mode) của một số hệ thống; một số hệ thống có Chế độ an toàn tích hợp có thể được truy cập bằng cách nhấn và giữ nút giảm âm lượng, trong khi những hệ thống khác (chẳng hạn như MIUI) có thể bật Chế Độ An Toàn trong Recovery. Khi vào Chế Độ An Toàn của hệ thống, KernelSU cũng sẽ vào Chế Độ An Toàn và tự động tắt các mô-đun.
  2. Chế Độ An Toàn tích hợp (built-in Safe Mode) của KernelSU; phương pháp thao tác là nhấn phím giảm âm lượng liên tục hơn ba lần sau màn hình khởi động đầu tiên. Lưu ý là nhấn-thả, nhấn-thả, nhấn-thả chứ không phải nhấn giữ.

Sau khi vào chế độ an toàn, tất cả các mô-đun trên trang mô-đun của KernelSU Manager đều bị tắt nhưng bạn có thể thực hiện thao tác "gỡ cài đặt" để gỡ cài đặt bất kỳ mô-đun nào có thể gây ra sự cố.

Chế độ an toàn tích hợp được triển khai trong kernel, do đó không có khả năng thiếu các sự kiện chính do bị chặn. Tuy nhiên, đối với các hạt nhân không phải GKI, có thể cần phải tích hợp mã thủ công và bạn có thể tham khảo tài liệu chính thức để được hướng dẫn.

Mô-đun độc hại

Nếu các phương pháp trên không thể cứu được thiết bị của bạn thì rất có thể mô-đun bạn cài đặt có hoạt động độc hại hoặc đã làm hỏng thiết bị của bạn thông qua các phương tiện khác. Trong trường hợp này, chỉ có hai gợi ý:

  1. Xóa sạch dữ liệu và flash hệ thống chính thức.
  2. Tham khảo dịch vụ hậu mãi.
',23),a=[o];function g(l,u,r,b,e,p){return h(),t("div",null,a)}const k=n(i,[["render",g]]);export{m as __pageData,k as default}; diff --git a/assets/vi_VN_guide_rescue-from-bootloop.md.d796809c.lean.js b/assets/vi_VN_guide_rescue-from-bootloop.md.dc2854ff.lean.js similarity index 73% rename from assets/vi_VN_guide_rescue-from-bootloop.md.d796809c.lean.js rename to assets/vi_VN_guide_rescue-from-bootloop.md.dc2854ff.lean.js index 922f0f9448c5..47da8c0c69c1 100644 --- a/assets/vi_VN_guide_rescue-from-bootloop.md.d796809c.lean.js +++ b/assets/vi_VN_guide_rescue-from-bootloop.md.dc2854ff.lean.js @@ -1 +1 @@ -import{_ as n,o as h,c as t,O as c}from"./chunks/framework.43781440.js";const m=JSON.parse('{"title":"Cứu khỏi bootloop (Vòng lặp khởi động)","description":"","frontmatter":{},"headers":[],"relativePath":"vi_VN/guide/rescue-from-bootloop.md","filePath":"vi_VN/guide/rescue-from-bootloop.md"}'),i={name:"vi_VN/guide/rescue-from-bootloop.md"},o=c("",23),a=[o];function g(l,u,r,b,e,p){return h(),t("div",null,a)}const k=n(i,[["render",g]]);export{m as __pageData,k as default}; +import{_ as n,o as h,c as t,Q as c}from"./chunks/framework.ec8f7e8e.js";const m=JSON.parse('{"title":"Cứu khỏi bootloop (Vòng lặp khởi động)","description":"","frontmatter":{},"headers":[],"relativePath":"vi_VN/guide/rescue-from-bootloop.md","filePath":"vi_VN/guide/rescue-from-bootloop.md"}'),i={name:"vi_VN/guide/rescue-from-bootloop.md"},o=c("",23),a=[o];function g(l,u,r,b,e,p){return h(),t("div",null,a)}const k=n(i,[["render",g]]);export{m as __pageData,k as default}; diff --git a/assets/vi_VN_guide_unofficially-support-devices.md.b3af952a.js b/assets/vi_VN_guide_unofficially-support-devices.md.2078077c.js similarity index 82% rename from assets/vi_VN_guide_unofficially-support-devices.md.b3af952a.js rename to assets/vi_VN_guide_unofficially-support-devices.md.2078077c.js index d07783002166..ffd808fdda63 100644 --- a/assets/vi_VN_guide_unofficially-support-devices.md.b3af952a.js +++ b/assets/vi_VN_guide_unofficially-support-devices.md.2078077c.js @@ -1 +1 @@ -import{d as h}from"./chunks/repos.9a031e32.js";import{o as n,c as r,z as t,F as a,L as l,b as c,O as s,t as i}from"./chunks/framework.43781440.js";const o=s('

Thiết bị hỗ trợ không chính thức

WARNING

Đây là trang liệt kê kernel cho các thiết bị không dùng GKI được hỗ trợ bởi các lập trình viên khác.

WARNING

Trang này chỉ để cho bạn tìm thấy source cho thiết bị của bạn, nó HOÀN TOÀN KHÔNG được review bởi lập trình viên của KernelSU. Vậy nên hãy chấp nhận rủi ro khi sử dụng chúng.

',3),_=t("thead",null,[t("tr",null,[t("th",null,"Người bảo trì"),t("th",null,"Kho lưu trữ"),t("th",null,"Thiết bị hỗ trợ")])],-1),d=["href"],u=["href"],v=JSON.parse('{"title":"Thiết bị hỗ trợ không chính thức","description":"","frontmatter":{},"headers":[],"relativePath":"vi_VN/guide/unofficially-support-devices.md","filePath":"vi_VN/guide/unofficially-support-devices.md"}'),b={name:"vi_VN/guide/unofficially-support-devices.md"},N=Object.assign(b,{setup(g){return(k,m)=>(n(),r("div",null,[o,t("table",null,[_,t("tbody",null,[(n(!0),r(a,null,l(c(h),e=>(n(),r("tr",{key:e.devices},[t("td",null,[t("a",{href:e.maintainer_link,target:"_blank",rel:"noreferrer"},i(e.maintainer),9,d)]),t("td",null,[t("a",{href:e.kernel_link,target:"_blank",rel:"noreferrer"},i(e.kernel_name),9,u)]),t("td",null,i(e.devices),1)]))),128))])])]))}});export{v as __pageData,N as default}; +import{d as h}from"./chunks/repos.9a031e32.js";import{o as n,c as r,k as t,F as a,D as l,l as c,Q as s,t as i}from"./chunks/framework.ec8f7e8e.js";const o=s('

Thiết bị hỗ trợ không chính thức

WARNING

Đây là trang liệt kê kernel cho các thiết bị không dùng GKI được hỗ trợ bởi các lập trình viên khác.

WARNING

Trang này chỉ để cho bạn tìm thấy source cho thiết bị của bạn, nó HOÀN TOÀN KHÔNG được review bởi lập trình viên của KernelSU. Vậy nên hãy chấp nhận rủi ro khi sử dụng chúng.

',3),_=t("thead",null,[t("tr",null,[t("th",null,"Người bảo trì"),t("th",null,"Kho lưu trữ"),t("th",null,"Thiết bị hỗ trợ")])],-1),d=["href"],u=["href"],v=JSON.parse('{"title":"Thiết bị hỗ trợ không chính thức","description":"","frontmatter":{},"headers":[],"relativePath":"vi_VN/guide/unofficially-support-devices.md","filePath":"vi_VN/guide/unofficially-support-devices.md"}'),g={name:"vi_VN/guide/unofficially-support-devices.md"},N=Object.assign(g,{setup(k){return(b,m)=>(n(),r("div",null,[o,t("table",null,[_,t("tbody",null,[(n(!0),r(a,null,l(c(h),e=>(n(),r("tr",{key:e.devices},[t("td",null,[t("a",{href:e.maintainer_link,target:"_blank",rel:"noreferrer"},i(e.maintainer),9,d)]),t("td",null,[t("a",{href:e.kernel_link,target:"_blank",rel:"noreferrer"},i(e.kernel_name),9,u)]),t("td",null,i(e.devices),1)]))),128))])])]))}});export{v as __pageData,N as default}; diff --git a/assets/vi_VN_guide_unofficially-support-devices.md.b3af952a.lean.js b/assets/vi_VN_guide_unofficially-support-devices.md.2078077c.lean.js similarity index 69% rename from assets/vi_VN_guide_unofficially-support-devices.md.b3af952a.lean.js rename to assets/vi_VN_guide_unofficially-support-devices.md.2078077c.lean.js index 2cb83c1f2617..47765529397b 100644 --- a/assets/vi_VN_guide_unofficially-support-devices.md.b3af952a.lean.js +++ b/assets/vi_VN_guide_unofficially-support-devices.md.2078077c.lean.js @@ -1 +1 @@ -import{d as h}from"./chunks/repos.9a031e32.js";import{o as n,c as r,z as t,F as a,L as l,b as c,O as s,t as i}from"./chunks/framework.43781440.js";const o=s("",3),_=t("thead",null,[t("tr",null,[t("th",null,"Người bảo trì"),t("th",null,"Kho lưu trữ"),t("th",null,"Thiết bị hỗ trợ")])],-1),d=["href"],u=["href"],v=JSON.parse('{"title":"Thiết bị hỗ trợ không chính thức","description":"","frontmatter":{},"headers":[],"relativePath":"vi_VN/guide/unofficially-support-devices.md","filePath":"vi_VN/guide/unofficially-support-devices.md"}'),b={name:"vi_VN/guide/unofficially-support-devices.md"},N=Object.assign(b,{setup(g){return(k,m)=>(n(),r("div",null,[o,t("table",null,[_,t("tbody",null,[(n(!0),r(a,null,l(c(h),e=>(n(),r("tr",{key:e.devices},[t("td",null,[t("a",{href:e.maintainer_link,target:"_blank",rel:"noreferrer"},i(e.maintainer),9,d)]),t("td",null,[t("a",{href:e.kernel_link,target:"_blank",rel:"noreferrer"},i(e.kernel_name),9,u)]),t("td",null,i(e.devices),1)]))),128))])])]))}});export{v as __pageData,N as default}; +import{d as h}from"./chunks/repos.9a031e32.js";import{o as n,c as r,k as t,F as a,D as l,l as c,Q as s,t as i}from"./chunks/framework.ec8f7e8e.js";const o=s("",3),_=t("thead",null,[t("tr",null,[t("th",null,"Người bảo trì"),t("th",null,"Kho lưu trữ"),t("th",null,"Thiết bị hỗ trợ")])],-1),d=["href"],u=["href"],v=JSON.parse('{"title":"Thiết bị hỗ trợ không chính thức","description":"","frontmatter":{},"headers":[],"relativePath":"vi_VN/guide/unofficially-support-devices.md","filePath":"vi_VN/guide/unofficially-support-devices.md"}'),g={name:"vi_VN/guide/unofficially-support-devices.md"},N=Object.assign(g,{setup(k){return(b,m)=>(n(),r("div",null,[o,t("table",null,[_,t("tbody",null,[(n(!0),r(a,null,l(c(h),e=>(n(),r("tr",{key:e.devices},[t("td",null,[t("a",{href:e.maintainer_link,target:"_blank",rel:"noreferrer"},i(e.maintainer),9,d)]),t("td",null,[t("a",{href:e.kernel_link,target:"_blank",rel:"noreferrer"},i(e.kernel_name),9,u)]),t("td",null,i(e.devices),1)]))),128))])])]))}});export{v as __pageData,N as default}; diff --git a/assets/vi_VN_guide_what-is-kernelsu.md.bea8d4b7.js b/assets/vi_VN_guide_what-is-kernelsu.md.48965b2f.js similarity index 95% rename from assets/vi_VN_guide_what-is-kernelsu.md.bea8d4b7.js rename to assets/vi_VN_guide_what-is-kernelsu.md.48965b2f.js index bc8e3aa6eb00..04ac7c4837bf 100644 --- a/assets/vi_VN_guide_what-is-kernelsu.md.bea8d4b7.js +++ b/assets/vi_VN_guide_what-is-kernelsu.md.48965b2f.js @@ -1 +1 @@ -import{_ as n,o as e,c as a,O as t}from"./chunks/framework.43781440.js";const _=JSON.parse('{"title":"KernelSU là gì?","description":"","frontmatter":{},"headers":[],"relativePath":"vi_VN/guide/what-is-kernelsu.md","filePath":"vi_VN/guide/what-is-kernelsu.md"}'),h={name:"vi_VN/guide/what-is-kernelsu.md"},r=t('

KernelSU là gì?

KernelSU là một giải pháp root cho các thiết bị Android GKI, nó hoạt động ở chế độ kernel và cấp quyền root cho ứng dụng không gian người dùng trực tiếp trong không gian kernel.

Tính năng

Tính năng chính của KernelSU là Kernel-based (dựa trên Kernel). KernelSU hoạt động ở chế độ kernel nên nó có thể cung cấp giao diện kernel mà chúng ta chưa từng có trước đây. Ví dụ: chúng ta có thể thêm điểm dừng phần cứng vào bất kỳ quy trình nào ở chế độ kernel; Chúng ta có thể truy cập bộ nhớ vật lý của bất kỳ quy trình nào mà không bị phát hiện; Chúng ta còn có thể chặn bất kỳ syscall nào trong không gian kernel; v.v.

Ngoài ra, KernelSU còn cung cấp hệ thống mô-đun thông qua lớp phủ, cho phép bạn tải plugin tùy chỉnh vào hệ thống. Nó cũng cung cấp một cơ chế để sửa đổi các tập tin trong phân vùng /system.

Hướng dẫn sử dụng

Xin hãy xem: Cách cài đặt

Cách để build

Cách để build

Thảo luận

',11),l=[r];function c(i,o,g,d,s,u){return e(),a("div",null,l)}const m=n(h,[["render",c]]);export{_ as __pageData,m as default}; +import{_ as n,o as e,c as a,Q as t}from"./chunks/framework.ec8f7e8e.js";const _=JSON.parse('{"title":"KernelSU là gì?","description":"","frontmatter":{},"headers":[],"relativePath":"vi_VN/guide/what-is-kernelsu.md","filePath":"vi_VN/guide/what-is-kernelsu.md"}'),h={name:"vi_VN/guide/what-is-kernelsu.md"},r=t('

KernelSU là gì?

KernelSU là một giải pháp root cho các thiết bị Android GKI, nó hoạt động ở chế độ kernel và cấp quyền root cho ứng dụng không gian người dùng trực tiếp trong không gian kernel.

Tính năng

Tính năng chính của KernelSU là Kernel-based (dựa trên Kernel). KernelSU hoạt động ở chế độ kernel nên nó có thể cung cấp giao diện kernel mà chúng ta chưa từng có trước đây. Ví dụ: chúng ta có thể thêm điểm dừng phần cứng vào bất kỳ quy trình nào ở chế độ kernel; Chúng ta có thể truy cập bộ nhớ vật lý của bất kỳ quy trình nào mà không bị phát hiện; Chúng ta còn có thể chặn bất kỳ syscall nào trong không gian kernel; v.v.

Ngoài ra, KernelSU còn cung cấp hệ thống mô-đun thông qua lớp phủ, cho phép bạn tải plugin tùy chỉnh vào hệ thống. Nó cũng cung cấp một cơ chế để sửa đổi các tập tin trong phân vùng /system.

Hướng dẫn sử dụng

Xin hãy xem: Cách cài đặt

Cách để build

Cách để build

Thảo luận

',11),l=[r];function c(i,o,g,d,s,u){return e(),a("div",null,l)}const m=n(h,[["render",c]]);export{_ as __pageData,m as default}; diff --git a/assets/vi_VN_guide_what-is-kernelsu.md.bea8d4b7.lean.js b/assets/vi_VN_guide_what-is-kernelsu.md.48965b2f.lean.js similarity index 71% rename from assets/vi_VN_guide_what-is-kernelsu.md.bea8d4b7.lean.js rename to assets/vi_VN_guide_what-is-kernelsu.md.48965b2f.lean.js index f6512aa6e061..9a3206552feb 100644 --- a/assets/vi_VN_guide_what-is-kernelsu.md.bea8d4b7.lean.js +++ b/assets/vi_VN_guide_what-is-kernelsu.md.48965b2f.lean.js @@ -1 +1 @@ -import{_ as n,o as e,c as a,O as t}from"./chunks/framework.43781440.js";const _=JSON.parse('{"title":"KernelSU là gì?","description":"","frontmatter":{},"headers":[],"relativePath":"vi_VN/guide/what-is-kernelsu.md","filePath":"vi_VN/guide/what-is-kernelsu.md"}'),h={name:"vi_VN/guide/what-is-kernelsu.md"},r=t("",11),l=[r];function c(i,o,g,d,s,u){return e(),a("div",null,l)}const m=n(h,[["render",c]]);export{_ as __pageData,m as default}; +import{_ as n,o as e,c as a,Q as t}from"./chunks/framework.ec8f7e8e.js";const _=JSON.parse('{"title":"KernelSU là gì?","description":"","frontmatter":{},"headers":[],"relativePath":"vi_VN/guide/what-is-kernelsu.md","filePath":"vi_VN/guide/what-is-kernelsu.md"}'),h={name:"vi_VN/guide/what-is-kernelsu.md"},r=t("",11),l=[r];function c(i,o,g,d,s,u){return e(),a("div",null,l)}const m=n(h,[["render",c]]);export{_ as __pageData,m as default}; diff --git a/assets/vi_VN_index.md.a84edf49.js b/assets/vi_VN_index.md.f75cd4bf.js similarity index 95% rename from assets/vi_VN_index.md.a84edf49.js rename to assets/vi_VN_index.md.f75cd4bf.js index dffb5c3f5d88..b0fc76866bab 100644 --- a/assets/vi_VN_index.md.a84edf49.js +++ b/assets/vi_VN_index.md.f75cd4bf.js @@ -1 +1 @@ -import{_ as e,o as t,c as n}from"./chunks/framework.43781440.js";const u=JSON.parse('{"title":"Giải pháp root dựa trên kernel dành cho Android","description":"","frontmatter":{"layout":"home","title":"Giải pháp root dựa trên kernel dành cho Android","hero":{"name":"KernelSU","text":"Giải pháp root dựa trên kernel dành cho Android","tagline":"","image":{"src":"/logo.png","alt":"KernelSU"},"actions":[{"theme":"brand","text":"Bắt Đầu","link":"/guide/what-is-kernelsu"},{"theme":"alt","text":"Xem trên GitHub","link":"https://github.com/tiann/KernelSU"}]},"features":[{"title":"Dựa trên Kernel","details":"KernelSU đang hoạt động ở chế độ kernel Linux, nó có nhiều quyền kiểm soát hơn đối với các ứng dụng userspace."},{"title":"Kiểm soát truy cập bằng whitelist","details":"Chỉ ứng dụng được cấp quyền root mới có thể truy cập `su`, các ứng dụng khác không thể nhận được su."},{"title":"Quyền root bị hạn chế","details":"KernelSU cho phép bạn tùy chỉnh uid, gid, group, capabilities và các quy tắc SELinux của su. Giới hạn sức mạnh của root."},{"title":"Mô-đun & Nguồn mở","details":"KernelSU hỗ trợ sửa đổi /system một cách systemless bằng overlayfs và nó có nguồn mở theo GPL-3."}]},"headers":[],"relativePath":"vi_VN/index.md","filePath":"vi_VN/index.md"}'),i={name:"vi_VN/index.md"};function r(o,c,a,s,h,l){return t(),n("div")}const p=e(i,[["render",r]]);export{u as __pageData,p as default}; +import{_ as e,o as t,c as n}from"./chunks/framework.ec8f7e8e.js";const u=JSON.parse('{"title":"Giải pháp root dựa trên kernel dành cho Android","description":"","frontmatter":{"layout":"home","title":"Giải pháp root dựa trên kernel dành cho Android","hero":{"name":"KernelSU","text":"Giải pháp root dựa trên kernel dành cho Android","tagline":"","image":{"src":"/logo.png","alt":"KernelSU"},"actions":[{"theme":"brand","text":"Bắt Đầu","link":"/guide/what-is-kernelsu"},{"theme":"alt","text":"Xem trên GitHub","link":"https://github.com/tiann/KernelSU"}]},"features":[{"title":"Dựa trên Kernel","details":"KernelSU đang hoạt động ở chế độ kernel Linux, nó có nhiều quyền kiểm soát hơn đối với các ứng dụng userspace."},{"title":"Kiểm soát truy cập bằng whitelist","details":"Chỉ ứng dụng được cấp quyền root mới có thể truy cập `su`, các ứng dụng khác không thể nhận được su."},{"title":"Quyền root bị hạn chế","details":"KernelSU cho phép bạn tùy chỉnh uid, gid, group, capabilities và các quy tắc SELinux của su. Giới hạn sức mạnh của root."},{"title":"Mô-đun & Nguồn mở","details":"KernelSU hỗ trợ sửa đổi /system một cách systemless bằng overlayfs và nó có nguồn mở theo GPL-3."}]},"headers":[],"relativePath":"vi_VN/index.md","filePath":"vi_VN/index.md"}'),i={name:"vi_VN/index.md"};function r(o,c,a,s,h,l){return t(),n("div")}const p=e(i,[["render",r]]);export{u as __pageData,p as default}; diff --git a/assets/vi_VN_index.md.a84edf49.lean.js b/assets/vi_VN_index.md.f75cd4bf.lean.js similarity index 95% rename from assets/vi_VN_index.md.a84edf49.lean.js rename to assets/vi_VN_index.md.f75cd4bf.lean.js index dffb5c3f5d88..b0fc76866bab 100644 --- a/assets/vi_VN_index.md.a84edf49.lean.js +++ b/assets/vi_VN_index.md.f75cd4bf.lean.js @@ -1 +1 @@ -import{_ as e,o as t,c as n}from"./chunks/framework.43781440.js";const u=JSON.parse('{"title":"Giải pháp root dựa trên kernel dành cho Android","description":"","frontmatter":{"layout":"home","title":"Giải pháp root dựa trên kernel dành cho Android","hero":{"name":"KernelSU","text":"Giải pháp root dựa trên kernel dành cho Android","tagline":"","image":{"src":"/logo.png","alt":"KernelSU"},"actions":[{"theme":"brand","text":"Bắt Đầu","link":"/guide/what-is-kernelsu"},{"theme":"alt","text":"Xem trên GitHub","link":"https://github.com/tiann/KernelSU"}]},"features":[{"title":"Dựa trên Kernel","details":"KernelSU đang hoạt động ở chế độ kernel Linux, nó có nhiều quyền kiểm soát hơn đối với các ứng dụng userspace."},{"title":"Kiểm soát truy cập bằng whitelist","details":"Chỉ ứng dụng được cấp quyền root mới có thể truy cập `su`, các ứng dụng khác không thể nhận được su."},{"title":"Quyền root bị hạn chế","details":"KernelSU cho phép bạn tùy chỉnh uid, gid, group, capabilities và các quy tắc SELinux của su. Giới hạn sức mạnh của root."},{"title":"Mô-đun & Nguồn mở","details":"KernelSU hỗ trợ sửa đổi /system một cách systemless bằng overlayfs và nó có nguồn mở theo GPL-3."}]},"headers":[],"relativePath":"vi_VN/index.md","filePath":"vi_VN/index.md"}'),i={name:"vi_VN/index.md"};function r(o,c,a,s,h,l){return t(),n("div")}const p=e(i,[["render",r]]);export{u as __pageData,p as default}; +import{_ as e,o as t,c as n}from"./chunks/framework.ec8f7e8e.js";const u=JSON.parse('{"title":"Giải pháp root dựa trên kernel dành cho Android","description":"","frontmatter":{"layout":"home","title":"Giải pháp root dựa trên kernel dành cho Android","hero":{"name":"KernelSU","text":"Giải pháp root dựa trên kernel dành cho Android","tagline":"","image":{"src":"/logo.png","alt":"KernelSU"},"actions":[{"theme":"brand","text":"Bắt Đầu","link":"/guide/what-is-kernelsu"},{"theme":"alt","text":"Xem trên GitHub","link":"https://github.com/tiann/KernelSU"}]},"features":[{"title":"Dựa trên Kernel","details":"KernelSU đang hoạt động ở chế độ kernel Linux, nó có nhiều quyền kiểm soát hơn đối với các ứng dụng userspace."},{"title":"Kiểm soát truy cập bằng whitelist","details":"Chỉ ứng dụng được cấp quyền root mới có thể truy cập `su`, các ứng dụng khác không thể nhận được su."},{"title":"Quyền root bị hạn chế","details":"KernelSU cho phép bạn tùy chỉnh uid, gid, group, capabilities và các quy tắc SELinux của su. Giới hạn sức mạnh của root."},{"title":"Mô-đun & Nguồn mở","details":"KernelSU hỗ trợ sửa đổi /system một cách systemless bằng overlayfs và nó có nguồn mở theo GPL-3."}]},"headers":[],"relativePath":"vi_VN/index.md","filePath":"vi_VN/index.md"}'),i={name:"vi_VN/index.md"};function r(o,c,a,s,h,l){return t(),n("div")}const p=e(i,[["render",r]]);export{u as __pageData,p as default}; diff --git a/assets/zh_CN_guide_app-profile.md.557e9b58.js b/assets/zh_CN_guide_app-profile.md.557e9b58.js deleted file mode 100644 index 42aaf09b676a..000000000000 --- a/assets/zh_CN_guide_app-profile.md.557e9b58.js +++ /dev/null @@ -1,5 +0,0 @@ -import{_ as o,o as s,c as e,O as a}from"./chunks/framework.43781440.js";const u=JSON.parse('{"title":"App Profile","description":"","frontmatter":{},"headers":[],"relativePath":"zh_CN/guide/app-profile.md","filePath":"zh_CN/guide/app-profile.md"}'),l={name:"zh_CN/guide/app-profile.md"},p=a(`

App Profile

App Profile 是 KernelSU 提供的一种针对各种应用自定义其使用配置的机制。

对授予了 root 权限(也即可以使用 su)的应用来说,App Profile 也可以称之为 Root Profile,它可以自定义 suuid, gid, groups, capabilities 以及 SELinux 规则,从而限制 root 用户的权限;比如可以针对防火墙应用仅授予网络权限,而不授予文件访问权限,针对冻结类应用仅授予 shell 权限而不是直接给 root;通过最小化权限原则把权力关进笼子里

对于没有被授予 root 权限的普通应用,App Profile 可以控制内核以及模块系统对此应用的行为;比如是否需要针对此应用卸载模块造成的修改等。内核和模块系统可以通过此配置决定是否要做一些类似“隐藏痕迹”类的操作。

Root Profile

UID、GID 和 groups

Linux 系统中有用户和组两个概念。每个用户都有一个用户 ID(UID),一个用户可以属于多个组,每个组也有组 ID(GID)。该 ID 用于识别系统的用户并确定用户可以访问哪些系统资源。

UID 为 0 的用户被称之为 root 用户,GID 为 0 的组被称之为 root 组;root 用户组通常拥有系统的最高权限。

对于 Android 系统来说,每一个 App 都是一个单独的用户(不考虑 share uid 的情况),拥有一个唯一的 UID。比如 0 是 root 用户,1000system2000 是 ADB shell,10000-19999 的是普通用户。

INFO

此处的 UID 跟 Android 系统的多用户,或者说工作资料(Work Profile),不是一个概念。工作资料实际上是对 UID 进行分片实现的,比如 10000-19999 是主用户,110000-119999 是工作资料;他们中的任何一个普通应用都拥有自己独有的 UID。

每一个 App 可以有若干个组,GID 使其主要的组,通常与 UID 一致;其他的组被称之为补充组(groups)。某些权限是通过组控制的,比如网络访问,蓝牙等。

例如,如果我们在 ADB shell 中执行 id 命令,会得到如下输出:

sh
oriole:/ $ id
-uid=2000(shell) gid=2000(shell) groups=2000(shell),1004(input),1007(log),1011(adb),1015(sdcard_rw),1028(sdcard_r),1078(ext_data_rw),1079(ext_obb_rw),3001(net_bt_admin),3002(net_bt),3003(inet),3006(net_bw_stats),3009(readproc),3011(uhid),3012(readtracefs) context=u:r:shell:s0

其中,UID 为 2000,GID 也即主要组 ID 也为 2000;除此之外它还在很多补充组里面,例如 inet 组代表可以创建 AF_INETAF_INET6 的 socket(访问网络),sdcard_rw 代表可以读写 sdcard 等。

KernelSU 的 Root Profile 可以自定义执行 su 后 root 进程的 UID, GID 和 groups。例如,你可以设置某个 root 应用的 Root Profile 其 UID 为 2000,这意味着此应用在使用 su 的时候,它的实际权限是 ADB Shell 级别;你可以去掉 groups 中的 inet,这样这个 su 就无法访问网络。

注意

App Profile 仅仅是控制 root 应用使用 su 后的权限,它并非控制 App 本身的权限!如果 App 本身申请了网络访问权限,那么它即使不使用 su 也可以访问网络;为 su 去掉 inet 组仅仅是让 su 无法访问网络。

与应用通过 su 主动切换用户或者组不同,Root Profile 是在内核中强制实施的,不依赖 root 应用的自觉行为,su 权限的授予完全取决于用户而非开发者。

Capabilities

Capabilities 是 Linux 的一种分权机制。

传统的 UNIX 系统为了执行权限检查,将进程分为两类:特权进程(其有效用户 ID 为 0,称为超级用户或 root)和非特权进程(其有效 UID 为非零)。特权进程会绕过所有内核权限检查,而非特权进程则根据其凭据(通常是有效UID、有效GID和补充组列表)进行完整的权限检查。

从 Linux 2.2开始,Linux 将传统上与超级用户关联的特权分解为独立的单元,称为 Capabilities(有的也翻译为“权能”),它们可以独立启用和禁用。

每一个 Capability 代表一个或者一类权限。比如 CAP_DAC_READ_SEARCH 就代表是否有能力绕过文件读取权限检查和目录读取和执行权限检查。如果一个有效 UID 为 0 的用户(root 用户)没有 CAP_DAC_READ_SEARCH 或者更高 Capalities,这意味着即使它是 root 也不能随意读取文件。

KernelSU 的 Root Profile 可以自定义执行 su 后 root 进程的 Capabilities,从而实现只授予“部分 root 权限”。与上面介绍的 UID, GID 不同,某些 root 应用就是需要 su 后 UID 是 0,此时我们可以通过限制这个 UID 为 0 的 root 用户的 Capabilities,就可以限制它能够执行的操作。

强烈建议

Linux 系统关于 Capability 的 官方文档,解释了每一项 Capability 所代表的能力,写的非常详细,如果你想要自定义 Capabilities,请务必先阅读此文档。

SELinux

SELinux 是一种强大的强制性权限访问控制(MAC)机制。它按照默认拒绝的原则运行:任何未经明确允许的行为都会被拒绝。

SELinux 可按两种全局模式运行:

  1. 宽容模式:权限拒绝事件会被记录下来,但不会被强制执行。
  2. 强制模式:权限拒绝事件会被记录下来强制执行。

警告

现代的 Android 系统极度依赖 SELinux 来保障整个系统的安全性,我们强烈建议您不要使用任何以“宽容模式”运行的自定义系统,因为那样与裸奔没什么区别。

SELinux 的完整概念比较复杂,我们这里不打算讲解它的具体工作方式,建议你先通过以下资料来了解其工作原理:

  1. wikipedia
  2. Redhat: what-is-selinux
  3. ArchLinux: SELinux

KernelSU 的 Root Profile 可以自定义执行 su 后 root 进程的 SELinux context,并且可以针对这个 context 设置特定的访问控制规则,从而更加精细化地控制 root 权限。

通常情况下,应用执行 su 后,会将进程切换到一个 不受任何限制 的 SELinux 域,比如 u:r:su:s0,通过 Root Profile,我们可以将它切换到一个自定义的域,比如 u:r:app1:s0,然后为这个域制定一系列规则:

sh
type app1
-enforce app1
-typeattribute app1 mlstrustedsubject
-allow app1 * * *

注意:此处的 allow app1 * * * 仅仅作为演示方便而使用,实际过程中不应使用这个规则,因为它跟 permissive 区别不大。

逃逸

如果 Root Profile 的配置不合理,那么可能会发生逃逸的情况:Root Profile 的限制会意外失效。

比如,如果你为 ADB shell 用户设置允许 root 权限(这是相当常见的情况);然后你给某个普通应用允许 root 权限,但是配置它的 root profile 中的 UID 为 2000(ADB shell 用户的 UID);那么此时,这个 App 可以通过执行两次 su 来获得完整的 root 权限:

  1. 第一次执行 su,由于 App Profile 强制生效,会正常切换到 UID 为 2000(adb shell) 而非 0(root)
  2. 第二次执行 su,由于此时它 UID 是 2000,而你给 2000(adb shell) 配置了允许 root,它会获得完整的 root 权限!

注意

这是完全符合预期的行为,并非 BUG!因此我们建议:

如果你的确需要给 adb 授予 root 权限(比如你是开发者),那么不建议你在配置 Root Profile 的时候将 UID 改成 2000,用 1000(system) 会更好。

Non Root Profile

卸载模块

KernelSU 提供了一种 systemless 的方式来修改系统分区,这是通过挂载 overlayfs 来实现的。但有些情况下,App 可能会对这种行为比较敏感;因此,我们可以通过设置“卸载模块”来卸载挂载在这些 App 上的模块。

另外,KernelSU 管理器的设置界面还提供了一个“默认卸载模块”的开关,这个开关默认情况下是开启的,这意味着如果不对 App 做额外的设置,默认情况下 KernelSU 或者某些模块会对此 App 执行卸载操作。当然,如果你不喜欢这个设置或者这个设置会影响某些 App,可以有如下选择:

  1. 保持“默认卸载模块”的开关,然后针对不需要“卸载模块”的 App 进行单独的设置,在 App Profile 中关闭“卸载模块”;(相当于“白名单“)。
  2. 关闭“默认卸载模块”的开关,然后针对需要“卸载模块”的 App 进行单独的设置,在 App Profile 中开启“卸载模块”;(相当于“黑名单“)。

INFO

KernelSU 在 5.10 及以上内核上,内核会执行“卸载模块”的操作;但在 5.10 以下的设备上,这个开关仅仅是一个“配置项”,KernelSU 本身不会做任何动作,一些模块(如 Zygisksu 会通过这个模块决定是否需要卸载)

`,46),n=[p];function t(r,c,i,d,D,F){return s(),e("div",null,n)}const C=o(l,[["render",t]]);export{u as __pageData,C as default}; diff --git a/assets/zh_CN_guide_app-profile.md.557e9b58.lean.js b/assets/zh_CN_guide_app-profile.md.557e9b58.lean.js deleted file mode 100644 index 0e8310e168e1..000000000000 --- a/assets/zh_CN_guide_app-profile.md.557e9b58.lean.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as o,o as s,c as e,O as a}from"./chunks/framework.43781440.js";const u=JSON.parse('{"title":"App Profile","description":"","frontmatter":{},"headers":[],"relativePath":"zh_CN/guide/app-profile.md","filePath":"zh_CN/guide/app-profile.md"}'),l={name:"zh_CN/guide/app-profile.md"},p=a("",46),n=[p];function t(r,c,i,d,D,F){return s(),e("div",null,n)}const C=o(l,[["render",t]]);export{u as __pageData,C as default}; diff --git a/assets/zh_CN_guide_app-profile.md.cabe50a9.js b/assets/zh_CN_guide_app-profile.md.cabe50a9.js new file mode 100644 index 000000000000..7b1966a199a9 --- /dev/null +++ b/assets/zh_CN_guide_app-profile.md.cabe50a9.js @@ -0,0 +1,9 @@ +import{_ as s,o,c as a,Q as p}from"./chunks/framework.ec8f7e8e.js";const u=JSON.parse('{"title":"App Profile","description":"","frontmatter":{},"headers":[],"relativePath":"zh_CN/guide/app-profile.md","filePath":"zh_CN/guide/app-profile.md"}'),l={name:"zh_CN/guide/app-profile.md"},e=p(`

App Profile

App Profile 是 KernelSU 提供的一种针对各种应用自定义其使用配置的机制。

对授予了 root 权限(也即可以使用 su)的应用来说,App Profile 也可以称之为 Root Profile,它可以自定义 suuid, gid, groups, capabilities 以及 SELinux 规则,从而限制 root 用户的权限;比如可以针对防火墙应用仅授予网络权限,而不授予文件访问权限,针对冻结类应用仅授予 shell 权限而不是直接给 root;通过最小化权限原则把权力关进笼子里

对于没有被授予 root 权限的普通应用,App Profile 可以控制内核以及模块系统对此应用的行为;比如是否需要针对此应用卸载模块造成的修改等。内核和模块系统可以通过此配置决定是否要做一些类似“隐藏痕迹”类的操作。

Root Profile

UID、GID 和 groups

Linux 系统中有用户和组两个概念。每个用户都有一个用户 ID(UID),一个用户可以属于多个组,每个组也有组 ID(GID)。该 ID 用于识别系统的用户并确定用户可以访问哪些系统资源。

UID 为 0 的用户被称之为 root 用户,GID 为 0 的组被称之为 root 组;root 用户组通常拥有系统的最高权限。

对于 Android 系统来说,每一个 App 都是一个单独的用户(不考虑 share uid 的情况),拥有一个唯一的 UID。比如 0 是 root 用户,1000system2000 是 ADB shell,10000-19999 的是普通用户。

INFO

此处的 UID 跟 Android 系统的多用户,或者说工作资料(Work Profile),不是一个概念。工作资料实际上是对 UID 进行分片实现的,比如 10000-19999 是主用户,110000-119999 是工作资料;他们中的任何一个普通应用都拥有自己独有的 UID。

每一个 App 可以有若干个组,GID 使其主要的组,通常与 UID 一致;其他的组被称之为补充组(groups)。某些权限是通过组控制的,比如网络访问,蓝牙等。

例如,如果我们在 ADB shell 中执行 id 命令,会得到如下输出:

sh
oriole:/ $ id
+uid=2000(shell) gid=2000(shell) groups=2000(shell),1004(input),1007(log),1011(adb),1015(sdcard_rw),1028(sdcard_r),1078(ext_data_rw),1079(ext_obb_rw),3001(net_bt_admin),3002(net_bt),3003(inet),3006(net_bw_stats),3009(readproc),3011(uhid),3012(readtracefs) context=u:r:shell:s0
oriole:/ $ id
+uid=2000(shell) gid=2000(shell) groups=2000(shell),1004(input),1007(log),1011(adb),1015(sdcard_rw),1028(sdcard_r),1078(ext_data_rw),1079(ext_obb_rw),3001(net_bt_admin),3002(net_bt),3003(inet),3006(net_bw_stats),3009(readproc),3011(uhid),3012(readtracefs) context=u:r:shell:s0

其中,UID 为 2000,GID 也即主要组 ID 也为 2000;除此之外它还在很多补充组里面,例如 inet 组代表可以创建 AF_INETAF_INET6 的 socket(访问网络),sdcard_rw 代表可以读写 sdcard 等。

KernelSU 的 Root Profile 可以自定义执行 su 后 root 进程的 UID, GID 和 groups。例如,你可以设置某个 root 应用的 Root Profile 其 UID 为 2000,这意味着此应用在使用 su 的时候,它的实际权限是 ADB Shell 级别;你可以去掉 groups 中的 inet,这样这个 su 就无法访问网络。

注意

App Profile 仅仅是控制 root 应用使用 su 后的权限,它并非控制 App 本身的权限!如果 App 本身申请了网络访问权限,那么它即使不使用 su 也可以访问网络;为 su 去掉 inet 组仅仅是让 su 无法访问网络。

与应用通过 su 主动切换用户或者组不同,Root Profile 是在内核中强制实施的,不依赖 root 应用的自觉行为,su 权限的授予完全取决于用户而非开发者。

Capabilities

Capabilities 是 Linux 的一种分权机制。

传统的 UNIX 系统为了执行权限检查,将进程分为两类:特权进程(其有效用户 ID 为 0,称为超级用户或 root)和非特权进程(其有效 UID 为非零)。特权进程会绕过所有内核权限检查,而非特权进程则根据其凭据(通常是有效UID、有效GID和补充组列表)进行完整的权限检查。

从 Linux 2.2开始,Linux 将传统上与超级用户关联的特权分解为独立的单元,称为 Capabilities(有的也翻译为“权能”),它们可以独立启用和禁用。

每一个 Capability 代表一个或者一类权限。比如 CAP_DAC_READ_SEARCH 就代表是否有能力绕过文件读取权限检查和目录读取和执行权限检查。如果一个有效 UID 为 0 的用户(root 用户)没有 CAP_DAC_READ_SEARCH 或者更高 Capalities,这意味着即使它是 root 也不能随意读取文件。

KernelSU 的 Root Profile 可以自定义执行 su 后 root 进程的 Capabilities,从而实现只授予“部分 root 权限”。与上面介绍的 UID, GID 不同,某些 root 应用就是需要 su 后 UID 是 0,此时我们可以通过限制这个 UID 为 0 的 root 用户的 Capabilities,就可以限制它能够执行的操作。

强烈建议

Linux 系统关于 Capability 的 官方文档,解释了每一项 Capability 所代表的能力,写的非常详细,如果你想要自定义 Capabilities,请务必先阅读此文档。

SELinux

SELinux 是一种强大的强制性权限访问控制(MAC)机制。它按照默认拒绝的原则运行:任何未经明确允许的行为都会被拒绝。

SELinux 可按两种全局模式运行:

  1. 宽容模式:权限拒绝事件会被记录下来,但不会被强制执行。
  2. 强制模式:权限拒绝事件会被记录下来强制执行。

警告

现代的 Android 系统极度依赖 SELinux 来保障整个系统的安全性,我们强烈建议您不要使用任何以“宽容模式”运行的自定义系统,因为那样与裸奔没什么区别。

SELinux 的完整概念比较复杂,我们这里不打算讲解它的具体工作方式,建议你先通过以下资料来了解其工作原理:

  1. wikipedia
  2. Redhat: what-is-selinux
  3. ArchLinux: SELinux

KernelSU 的 Root Profile 可以自定义执行 su 后 root 进程的 SELinux context,并且可以针对这个 context 设置特定的访问控制规则,从而更加精细化地控制 root 权限。

通常情况下,应用执行 su 后,会将进程切换到一个 不受任何限制 的 SELinux 域,比如 u:r:su:s0,通过 Root Profile,我们可以将它切换到一个自定义的域,比如 u:r:app1:s0,然后为这个域制定一系列规则:

sh
type app1
+enforce app1
+typeattribute app1 mlstrustedsubject
+allow app1 * * *
type app1
+enforce app1
+typeattribute app1 mlstrustedsubject
+allow app1 * * *

注意:此处的 allow app1 * * * 仅仅作为演示方便而使用,实际过程中不应使用这个规则,因为它跟 permissive 区别不大。

逃逸

如果 Root Profile 的配置不合理,那么可能会发生逃逸的情况:Root Profile 的限制会意外失效。

比如,如果你为 ADB shell 用户设置允许 root 权限(这是相当常见的情况);然后你给某个普通应用允许 root 权限,但是配置它的 root profile 中的 UID 为 2000(ADB shell 用户的 UID);那么此时,这个 App 可以通过执行两次 su 来获得完整的 root 权限:

  1. 第一次执行 su,由于 App Profile 强制生效,会正常切换到 UID 为 2000(adb shell) 而非 0(root)
  2. 第二次执行 su,由于此时它 UID 是 2000,而你给 2000(adb shell) 配置了允许 root,它会获得完整的 root 权限!

注意

这是完全符合预期的行为,并非 BUG!因此我们建议:

如果你的确需要给 adb 授予 root 权限(比如你是开发者),那么不建议你在配置 Root Profile 的时候将 UID 改成 2000,用 1000(system) 会更好。

Non Root Profile

卸载模块

KernelSU 提供了一种 systemless 的方式来修改系统分区,这是通过挂载 overlayfs 来实现的。但有些情况下,App 可能会对这种行为比较敏感;因此,我们可以通过设置“卸载模块”来卸载挂载在这些 App 上的模块。

另外,KernelSU 管理器的设置界面还提供了一个“默认卸载模块”的开关,这个开关默认情况下是开启的,这意味着如果不对 App 做额外的设置,默认情况下 KernelSU 或者某些模块会对此 App 执行卸载操作。当然,如果你不喜欢这个设置或者这个设置会影响某些 App,可以有如下选择:

  1. 保持“默认卸载模块”的开关,然后针对不需要“卸载模块”的 App 进行单独的设置,在 App Profile 中关闭“卸载模块”;(相当于“白名单“)。
  2. 关闭“默认卸载模块”的开关,然后针对需要“卸载模块”的 App 进行单独的设置,在 App Profile 中开启“卸载模块”;(相当于“黑名单“)。

INFO

KernelSU 在 5.10 及以上内核上,内核会执行“卸载模块”的操作;但在 5.10 以下的设备上,这个开关仅仅是一个“配置项”,KernelSU 本身不会做任何动作,一些模块(如 Zygisksu 会通过这个模块决定是否需要卸载)

`,46),n=[e];function t(r,c,i,d,E,y){return o(),a("div",null,n)}const h=s(l,[["render",t]]);export{u as __pageData,h as default}; diff --git a/assets/zh_CN_guide_app-profile.md.cabe50a9.lean.js b/assets/zh_CN_guide_app-profile.md.cabe50a9.lean.js new file mode 100644 index 000000000000..3d42d82bc055 --- /dev/null +++ b/assets/zh_CN_guide_app-profile.md.cabe50a9.lean.js @@ -0,0 +1 @@ +import{_ as s,o,c as a,Q as p}from"./chunks/framework.ec8f7e8e.js";const u=JSON.parse('{"title":"App Profile","description":"","frontmatter":{},"headers":[],"relativePath":"zh_CN/guide/app-profile.md","filePath":"zh_CN/guide/app-profile.md"}'),l={name:"zh_CN/guide/app-profile.md"},e=p("",46),n=[e];function t(r,c,i,d,E,y){return o(),a("div",null,n)}const h=s(l,[["render",t]]);export{u as __pageData,h as default}; diff --git a/assets/zh_CN_guide_difference-with-magisk.md.223f61d1.js b/assets/zh_CN_guide_difference-with-magisk.md.d3fa9085.js similarity index 96% rename from assets/zh_CN_guide_difference-with-magisk.md.223f61d1.js rename to assets/zh_CN_guide_difference-with-magisk.md.d3fa9085.js index 5627a8ee7d50..c108e2cfbf0b 100644 --- a/assets/zh_CN_guide_difference-with-magisk.md.223f61d1.js +++ b/assets/zh_CN_guide_difference-with-magisk.md.d3fa9085.js @@ -1 +1 @@ -import{_ as e,o as i,c as o,O as s}from"./chunks/framework.43781440.js";const u=JSON.parse('{"title":"KernelSU 模块与 Magisk 的差异","description":"","frontmatter":{},"headers":[],"relativePath":"zh_CN/guide/difference-with-magisk.md","filePath":"zh_CN/guide/difference-with-magisk.md"}'),a={name:"zh_CN/guide/difference-with-magisk.md"},l=s('

KernelSU 模块与 Magisk 的差异

虽然 KernelSU 模块与 Magisk 模块有很多相似之处,但由于它们的实现机制完全不同,因此不可避免地会有一些差异;如果你希望你的模块能同时在 Magisk 与 KernelSU 中运行,那么你必须了解这些差异。

相同之处

  • 模块文件格式: 都以 zip 的方式组织模块,并且模块的格式几乎相同
  • 模块安装目录: 都在 /data/adb/modules
  • systemless: 都支持通过模块的形式以 systemless 修改 /system
  • post-fs-data.sh: 执行时机完全一致,语义也完全一致
  • service.sh: 执行时机完全一致,语义也完全一致
  • system.prop: 完全相同
  • sepolicy.rule: 完全相同
  • BusyBox:脚本都在 BusyBox 中以“独立模式”运行

不同之处

在了解不同之处之前,你需要知道如何区分你的模块是运行在 KernelSU 还是运行在 Magisk 之中;在所有你可以运行模块脚本的地方(customize.sh, post-fs-data.sh, service.sh),你都可以通过环境变量KSU 来区分,在 KernelSU 中,这个环境变量将被设置为 true

以下是一些不同之处:

  1. KernelSU 的模块不支持在 Recovery 中安装。
  2. KernelSU 的模块没有内置的 Zygisk 支持(但你可以通过 ZygiskNext 来使用 Zygisk 模块)。
  3. KernelSU 模块替换或者删除文件与 Magisk 完全不同。KernelSU 不支持 .replace 方式,相反,你需要通过 mknod filename c 0 0 创建同名文件夹来删除对应文件。
  4. BusyBox 的目录不同;KernelSU 内置的 BusyBox 在 /data/adb/ksu/bin/busybox 而 Magisk 在 /data/adb/magisk/busybox注意此为 KernelSU 内部行为,未来可能会更改!
  5. KernelSU 不支持 .replace 文件;但 KernelSU 支持 REPLACEREMOVE 变量。
  6. KernelSU 新增了一种脚本 boot-completed.sh,以便在 Android 系统启动后运行某些任务。
  7. KernelSU 新增了一种脚本 post-mount.sh,以便在 Overlayfs 挂载后运行某些任务。
',8),d=[l];function t(r,c,n,h,f,_){return i(),o("div",null,d)}const p=e(a,[["render",t]]);export{u as __pageData,p as default}; +import{_ as e,o as i,c as o,Q as s}from"./chunks/framework.ec8f7e8e.js";const u=JSON.parse('{"title":"KernelSU 模块与 Magisk 的差异","description":"","frontmatter":{},"headers":[],"relativePath":"zh_CN/guide/difference-with-magisk.md","filePath":"zh_CN/guide/difference-with-magisk.md"}'),a={name:"zh_CN/guide/difference-with-magisk.md"},l=s('

KernelSU 模块与 Magisk 的差异

虽然 KernelSU 模块与 Magisk 模块有很多相似之处,但由于它们的实现机制完全不同,因此不可避免地会有一些差异;如果你希望你的模块能同时在 Magisk 与 KernelSU 中运行,那么你必须了解这些差异。

相同之处

  • 模块文件格式: 都以 zip 的方式组织模块,并且模块的格式几乎相同
  • 模块安装目录: 都在 /data/adb/modules
  • systemless: 都支持通过模块的形式以 systemless 修改 /system
  • post-fs-data.sh: 执行时机完全一致,语义也完全一致
  • service.sh: 执行时机完全一致,语义也完全一致
  • system.prop: 完全相同
  • sepolicy.rule: 完全相同
  • BusyBox:脚本都在 BusyBox 中以“独立模式”运行

不同之处

在了解不同之处之前,你需要知道如何区分你的模块是运行在 KernelSU 还是运行在 Magisk 之中;在所有你可以运行模块脚本的地方(customize.sh, post-fs-data.sh, service.sh),你都可以通过环境变量KSU 来区分,在 KernelSU 中,这个环境变量将被设置为 true

以下是一些不同之处:

  1. KernelSU 的模块不支持在 Recovery 中安装。
  2. KernelSU 的模块没有内置的 Zygisk 支持(但你可以通过 ZygiskNext 来使用 Zygisk 模块)。
  3. KernelSU 模块替换或者删除文件与 Magisk 完全不同。KernelSU 不支持 .replace 方式,相反,你需要通过 mknod filename c 0 0 创建同名文件夹来删除对应文件。
  4. BusyBox 的目录不同;KernelSU 内置的 BusyBox 在 /data/adb/ksu/bin/busybox 而 Magisk 在 /data/adb/magisk/busybox注意此为 KernelSU 内部行为,未来可能会更改!
  5. KernelSU 不支持 .replace 文件;但 KernelSU 支持 REPLACEREMOVE 变量。
  6. KernelSU 新增了一种脚本 boot-completed.sh,以便在 Android 系统启动后运行某些任务。
  7. KernelSU 新增了一种脚本 post-mount.sh,以便在 Overlayfs 挂载后运行某些任务。
',8),d=[l];function t(r,c,n,h,f,_){return i(),o("div",null,d)}const p=e(a,[["render",t]]);export{u as __pageData,p as default}; diff --git a/assets/zh_CN_guide_difference-with-magisk.md.223f61d1.lean.js b/assets/zh_CN_guide_difference-with-magisk.md.d3fa9085.lean.js similarity index 73% rename from assets/zh_CN_guide_difference-with-magisk.md.223f61d1.lean.js rename to assets/zh_CN_guide_difference-with-magisk.md.d3fa9085.lean.js index 22d4fe55cda0..6b54fd19a90f 100644 --- a/assets/zh_CN_guide_difference-with-magisk.md.223f61d1.lean.js +++ b/assets/zh_CN_guide_difference-with-magisk.md.d3fa9085.lean.js @@ -1 +1 @@ -import{_ as e,o as i,c as o,O as s}from"./chunks/framework.43781440.js";const u=JSON.parse('{"title":"KernelSU 模块与 Magisk 的差异","description":"","frontmatter":{},"headers":[],"relativePath":"zh_CN/guide/difference-with-magisk.md","filePath":"zh_CN/guide/difference-with-magisk.md"}'),a={name:"zh_CN/guide/difference-with-magisk.md"},l=s("",8),d=[l];function t(r,c,n,h,f,_){return i(),o("div",null,d)}const p=e(a,[["render",t]]);export{u as __pageData,p as default}; +import{_ as e,o as i,c as o,Q as s}from"./chunks/framework.ec8f7e8e.js";const u=JSON.parse('{"title":"KernelSU 模块与 Magisk 的差异","description":"","frontmatter":{},"headers":[],"relativePath":"zh_CN/guide/difference-with-magisk.md","filePath":"zh_CN/guide/difference-with-magisk.md"}'),a={name:"zh_CN/guide/difference-with-magisk.md"},l=s("",8),d=[l];function t(r,c,n,h,f,_){return i(),o("div",null,d)}const p=e(a,[["render",t]]);export{u as __pageData,p as default}; diff --git a/assets/zh_CN_guide_faq.md.30eb8f8e.js b/assets/zh_CN_guide_faq.md.ac23a8b3.js similarity index 98% rename from assets/zh_CN_guide_faq.md.30eb8f8e.js rename to assets/zh_CN_guide_faq.md.ac23a8b3.js index 1617eff434c0..129eb400b11b 100644 --- a/assets/zh_CN_guide_faq.md.30eb8f8e.js +++ b/assets/zh_CN_guide_faq.md.ac23a8b3.js @@ -1 +1 @@ -import{_ as e,o as a,c as r,O as n}from"./chunks/framework.43781440.js";const m=JSON.parse('{"title":"常见问题","description":"","frontmatter":{},"headers":[],"relativePath":"zh_CN/guide/faq.md","filePath":"zh_CN/guide/faq.md"}'),o={name:"zh_CN/guide/faq.md"},l=n('

常见问题

KernelSU 是否支持我的设备 ?

首先,您的设备应该能够解锁 bootloader。 如果不能,则不支持。

然后在你的设备上安装 KernelSU 管理器并打开它,如果它显示 不支持 ,那么你的设备没有官方支持的开箱即用的 boot image;但你可以自己编译内核集成 KernelSU 进而使用它。

KernelSU 是否需要解锁 Bootloader ?

当然需要。

KernelSU 是否支持模块 ?

支持,但它是早期版本,可能有问题。请等待它稳定 😃

KernelSU 是否支持 Xposed ?

支持。DreamlandTaiChi 可以正常运行。LSPosed 可以在 ZygiskNext 的支持下正常运行。

KernelSU 支持 Zygisk 吗?

KernelSU 本体不支持 Zygisk,但是你可以用 ZygiskNext 来使用 Zygisk 模块。

KernelSU 与 Magisk 兼容吗 ?

KernelSU 的模块系统与 Magisk 的 magic mount 有冲突,如果 KernelSU 中启用了任何模块,那么整个 Magisk 将无法工作。

但是如果你只使用 KernelSU 的 su,那么它会和 Magisk 一起工作:KernelSU 修改 kernel 、 Magisk 修改 ramdisk,它们可以一起工作。

KernelSU 会替代 Magisk 吗?

我们不这么认为,这也不是我们的目标。Magisk 对于用户空间 root 解决方案来说已经足够好了,它会存活很久。KernelSU 的目标是为用户提供内核接口,而不是替代 Magisk。

KernelSU 可以支持非 GKI 设备吗?

可以。但是你应该下载内核源代码并将 KernelSU 集成到源代码树中并自己编译内核。

KernelSU 支持 Android 12 以下的设备吗?

影响 KernelSU 兼容性的是设备内核的版本,它与设备的 Android 版本没有直接的关系。唯一有关联的是:出厂 Android 12 的设备,一定是 5.10 或更高的内核(GKI设备);因此结论如下:

  1. 出厂 Android 12 的设备必定是支持的(GKI 设备)
  2. 旧版本内核的设备(即使是 Android 12,也可能是旧内核)是兼容的(你需要自己编译内核)

KernelSU 可以支持旧内核吗?

可以,目前最低支持到 4.14;更低的版本你需要手动移植它,欢迎 PR !

如何为旧内核集成 KernelSU?

参考教程

为什么我手机系统是 Android 13,但内核版本却是 "android12-5.10"?

内核版本与 Android 版本无关,如果你需要刷入 KernelSU,请永远使用内核版本而非 Android 版本,如果你为 "android12-5.10" 的设备刷入 Android 13 的内核,等待你的将是 bootloop.

KernelSU 支持 --mount-master/全局挂载命名空间吗?

目前没有(未来可能会支持),但实际上有很多种办法手动进入全局命名空间,无需 su 内置支持,比如:

  1. nsenter -t 1 -m sh 可以获得一个全局 mount namespace 的 shell.
  2. 在你要执行的命令之前添加 nsenter --mount=/proc/1/ns/mnt 就可以让此命令在全局 mount namespace 下执行。 KernelSU 本身也使用了 这种方法

我是 GKI1.0, 能用 KernelSU 吗?

GKI1 跟 GKI2 完全是两个东西,所以你需要自行编译内核。

',33),t=[l];function i(s,d,h,u,k,c){return a(),r("div",null,t)}const g=e(o,[["render",i]]);export{m as __pageData,g as default}; +import{_ as e,o as a,c as r,Q as n}from"./chunks/framework.ec8f7e8e.js";const m=JSON.parse('{"title":"常见问题","description":"","frontmatter":{},"headers":[],"relativePath":"zh_CN/guide/faq.md","filePath":"zh_CN/guide/faq.md"}'),o={name:"zh_CN/guide/faq.md"},l=n('

常见问题

KernelSU 是否支持我的设备 ?

首先,您的设备应该能够解锁 bootloader。 如果不能,则不支持。

然后在你的设备上安装 KernelSU 管理器并打开它,如果它显示 不支持 ,那么你的设备没有官方支持的开箱即用的 boot image;但你可以自己编译内核集成 KernelSU 进而使用它。

KernelSU 是否需要解锁 Bootloader ?

当然需要。

KernelSU 是否支持模块 ?

支持,但它是早期版本,可能有问题。请等待它稳定 😃

KernelSU 是否支持 Xposed ?

支持。DreamlandTaiChi 可以正常运行。LSPosed 可以在 ZygiskNext 的支持下正常运行。

KernelSU 支持 Zygisk 吗?

KernelSU 本体不支持 Zygisk,但是你可以用 ZygiskNext 来使用 Zygisk 模块。

KernelSU 与 Magisk 兼容吗 ?

KernelSU 的模块系统与 Magisk 的 magic mount 有冲突,如果 KernelSU 中启用了任何模块,那么整个 Magisk 将无法工作。

但是如果你只使用 KernelSU 的 su,那么它会和 Magisk 一起工作:KernelSU 修改 kernel 、 Magisk 修改 ramdisk,它们可以一起工作。

KernelSU 会替代 Magisk 吗?

我们不这么认为,这也不是我们的目标。Magisk 对于用户空间 root 解决方案来说已经足够好了,它会存活很久。KernelSU 的目标是为用户提供内核接口,而不是替代 Magisk。

KernelSU 可以支持非 GKI 设备吗?

可以。但是你应该下载内核源代码并将 KernelSU 集成到源代码树中并自己编译内核。

KernelSU 支持 Android 12 以下的设备吗?

影响 KernelSU 兼容性的是设备内核的版本,它与设备的 Android 版本没有直接的关系。唯一有关联的是:出厂 Android 12 的设备,一定是 5.10 或更高的内核(GKI设备);因此结论如下:

  1. 出厂 Android 12 的设备必定是支持的(GKI 设备)
  2. 旧版本内核的设备(即使是 Android 12,也可能是旧内核)是兼容的(你需要自己编译内核)

KernelSU 可以支持旧内核吗?

可以,目前最低支持到 4.14;更低的版本你需要手动移植它,欢迎 PR !

如何为旧内核集成 KernelSU?

参考教程

为什么我手机系统是 Android 13,但内核版本却是 "android12-5.10"?

内核版本与 Android 版本无关,如果你需要刷入 KernelSU,请永远使用内核版本而非 Android 版本,如果你为 "android12-5.10" 的设备刷入 Android 13 的内核,等待你的将是 bootloop.

KernelSU 支持 --mount-master/全局挂载命名空间吗?

目前没有(未来可能会支持),但实际上有很多种办法手动进入全局命名空间,无需 su 内置支持,比如:

  1. nsenter -t 1 -m sh 可以获得一个全局 mount namespace 的 shell.
  2. 在你要执行的命令之前添加 nsenter --mount=/proc/1/ns/mnt 就可以让此命令在全局 mount namespace 下执行。 KernelSU 本身也使用了 这种方法

我是 GKI1.0, 能用 KernelSU 吗?

GKI1 跟 GKI2 完全是两个东西,所以你需要自行编译内核。

',33),t=[l];function i(s,d,h,u,k,c){return a(),r("div",null,t)}const g=e(o,[["render",i]]);export{m as __pageData,g as default}; diff --git a/assets/zh_CN_guide_faq.md.30eb8f8e.lean.js b/assets/zh_CN_guide_faq.md.ac23a8b3.lean.js similarity index 67% rename from assets/zh_CN_guide_faq.md.30eb8f8e.lean.js rename to assets/zh_CN_guide_faq.md.ac23a8b3.lean.js index dfc7b4bd2f9d..b5616ab94f72 100644 --- a/assets/zh_CN_guide_faq.md.30eb8f8e.lean.js +++ b/assets/zh_CN_guide_faq.md.ac23a8b3.lean.js @@ -1 +1 @@ -import{_ as e,o as a,c as r,O as n}from"./chunks/framework.43781440.js";const m=JSON.parse('{"title":"常见问题","description":"","frontmatter":{},"headers":[],"relativePath":"zh_CN/guide/faq.md","filePath":"zh_CN/guide/faq.md"}'),o={name:"zh_CN/guide/faq.md"},l=n("",33),t=[l];function i(s,d,h,u,k,c){return a(),r("div",null,t)}const g=e(o,[["render",i]]);export{m as __pageData,g as default}; +import{_ as e,o as a,c as r,Q as n}from"./chunks/framework.ec8f7e8e.js";const m=JSON.parse('{"title":"常见问题","description":"","frontmatter":{},"headers":[],"relativePath":"zh_CN/guide/faq.md","filePath":"zh_CN/guide/faq.md"}'),o={name:"zh_CN/guide/faq.md"},l=n("",33),t=[l];function i(s,d,h,u,k,c){return a(),r("div",null,t)}const g=e(o,[["render",i]]);export{m as __pageData,g as default}; diff --git a/assets/zh_CN_guide_hidden-features.md.cdd5624f.js b/assets/zh_CN_guide_hidden-features.md.34a0e2c0.js similarity index 86% rename from assets/zh_CN_guide_hidden-features.md.cdd5624f.js rename to assets/zh_CN_guide_hidden-features.md.34a0e2c0.js index e3189c0b5fee..c11ffce9a130 100644 --- a/assets/zh_CN_guide_hidden-features.md.cdd5624f.js +++ b/assets/zh_CN_guide_hidden-features.md.34a0e2c0.js @@ -1 +1 @@ -import{_ as e,o as a,c as t,O as s}from"./chunks/framework.43781440.js";const p=JSON.parse('{"title":"隐藏功能","description":"","frontmatter":{},"headers":[],"relativePath":"zh_CN/guide/hidden-features.md","filePath":"zh_CN/guide/hidden-features.md"}'),c={name:"zh_CN/guide/hidden-features.md"},d=s('

隐藏功能

ksurc

默认情况下,/system/bin/sh 会加载 /system/etc/mkshrc

可以通过创建 /data/adb/ksu/.ksurc 文件来让 su 加载该文件而不是 /system/etc/mkshrc

',4),r=[d];function o(_,i,n,h,u,l){return a(),t("div",null,r)}const f=e(c,[["render",o]]);export{p as __pageData,f as default}; +import{_ as e,o as a,c as t,Q as s}from"./chunks/framework.ec8f7e8e.js";const p=JSON.parse('{"title":"隐藏功能","description":"","frontmatter":{},"headers":[],"relativePath":"zh_CN/guide/hidden-features.md","filePath":"zh_CN/guide/hidden-features.md"}'),c={name:"zh_CN/guide/hidden-features.md"},d=s('

隐藏功能

ksurc

默认情况下,/system/bin/sh 会加载 /system/etc/mkshrc

可以通过创建 /data/adb/ksu/.ksurc 文件来让 su 加载该文件而不是 /system/etc/mkshrc

',4),r=[d];function o(_,i,n,h,u,l){return a(),t("div",null,r)}const f=e(c,[["render",o]]);export{p as __pageData,f as default}; diff --git a/assets/zh_CN_guide_hidden-features.md.cdd5624f.lean.js b/assets/zh_CN_guide_hidden-features.md.34a0e2c0.lean.js similarity index 70% rename from assets/zh_CN_guide_hidden-features.md.cdd5624f.lean.js rename to assets/zh_CN_guide_hidden-features.md.34a0e2c0.lean.js index 343ae3eed724..dc61249d13a9 100644 --- a/assets/zh_CN_guide_hidden-features.md.cdd5624f.lean.js +++ b/assets/zh_CN_guide_hidden-features.md.34a0e2c0.lean.js @@ -1 +1 @@ -import{_ as e,o as a,c as t,O as s}from"./chunks/framework.43781440.js";const p=JSON.parse('{"title":"隐藏功能","description":"","frontmatter":{},"headers":[],"relativePath":"zh_CN/guide/hidden-features.md","filePath":"zh_CN/guide/hidden-features.md"}'),c={name:"zh_CN/guide/hidden-features.md"},d=s("",4),r=[d];function o(_,i,n,h,u,l){return a(),t("div",null,r)}const f=e(c,[["render",o]]);export{p as __pageData,f as default}; +import{_ as e,o as a,c as t,Q as s}from"./chunks/framework.ec8f7e8e.js";const p=JSON.parse('{"title":"隐藏功能","description":"","frontmatter":{},"headers":[],"relativePath":"zh_CN/guide/hidden-features.md","filePath":"zh_CN/guide/hidden-features.md"}'),c={name:"zh_CN/guide/hidden-features.md"},d=s("",4),r=[d];function o(_,i,n,h,u,l){return a(),t("div",null,r)}const f=e(c,[["render",o]]);export{p as __pageData,f as default}; diff --git a/assets/zh_CN_guide_how-to-build.md.709f65fb.js b/assets/zh_CN_guide_how-to-build.md.709f65fb.js new file mode 100644 index 000000000000..74236a7a3822 --- /dev/null +++ b/assets/zh_CN_guide_how-to-build.md.709f65fb.js @@ -0,0 +1,7 @@ +import{_ as s,o as a,c as n,Q as l}from"./chunks/framework.ec8f7e8e.js";const u=JSON.parse('{"title":"如何构建 KernelSU?","description":"","frontmatter":{},"headers":[],"relativePath":"zh_CN/guide/how-to-build.md","filePath":"zh_CN/guide/how-to-build.md"}'),o={name:"zh_CN/guide/how-to-build.md"},e=l(`

如何构建 KernelSU?

首先,您应该阅读内核构建的 Android 官方文档:

  1. 构建内核
  2. 通用内核映像 (GKI) 发布构建

WARNING

本文档适用于 GKI 设备,如果你是旧内核,请参考如何为非GKI设备集成 KernelSU

构建内核

同步内核源码

sh
repo init -u https://android.googlesource.com/kernel/manifest
+mv <kernel_manifest.xml> .repo/manifests
+repo init -m manifest.xml
+repo sync
repo init -u https://android.googlesource.com/kernel/manifest
+mv <kernel_manifest.xml> .repo/manifests
+repo init -m manifest.xml
+repo sync

<kernel_manifest.xml> 是一个可以唯一确定构建的清单文件,您可以使用该清单进行可重新预测的构建。 您应该从 通用内核映像 (GKI) 发布构建 下载清单文件

构建

请先查看 官方文档

例如,我们需要构建 aarch64 内核镜像:

sh
LTO=thin BUILD_CONFIG=common/build.config.gki.aarch64 build/build.sh
LTO=thin BUILD_CONFIG=common/build.config.gki.aarch64 build/build.sh

不要忘记添加 LTO=thin, 否则,如果您的计算机内存小于 24GB,构建可能会失败.

从 Android 13 开始,内核由 bazel 构建:

sh
tools/bazel build --config=fast //common:kernel_aarch64_dist
tools/bazel build --config=fast //common:kernel_aarch64_dist

使用 KernelSU 构建内核

如果您可以成功构建内核,那么构建 KernelSU 就很容易,根据自己的需求在内核源代码根目录中运行以下任一命令:

  • 最新tag(稳定版本)
sh
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -
  • main分支(开发版本)
sh
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -s main
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -s main
  • 指定tag(比如v0.5.2)
sh
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -s v0.5.2
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -s v0.5.2

然后重建内核,您将获得带有 KernelSU 的内核映像!

`,24),p=[e];function t(r,c,i,y,d,E){return a(),n("div",null,p)}const F=s(o,[["render",t]]);export{u as __pageData,F as default}; diff --git a/assets/zh_CN_guide_how-to-build.md.709f65fb.lean.js b/assets/zh_CN_guide_how-to-build.md.709f65fb.lean.js new file mode 100644 index 000000000000..04c14afe4d67 --- /dev/null +++ b/assets/zh_CN_guide_how-to-build.md.709f65fb.lean.js @@ -0,0 +1 @@ +import{_ as s,o as a,c as n,Q as l}from"./chunks/framework.ec8f7e8e.js";const u=JSON.parse('{"title":"如何构建 KernelSU?","description":"","frontmatter":{},"headers":[],"relativePath":"zh_CN/guide/how-to-build.md","filePath":"zh_CN/guide/how-to-build.md"}'),o={name:"zh_CN/guide/how-to-build.md"},e=l("",24),p=[e];function t(r,c,i,y,d,E){return a(),n("div",null,p)}const F=s(o,[["render",t]]);export{u as __pageData,F as default}; diff --git a/assets/zh_CN_guide_how-to-build.md.f4f2a6dd.js b/assets/zh_CN_guide_how-to-build.md.f4f2a6dd.js deleted file mode 100644 index d8adb4529032..000000000000 --- a/assets/zh_CN_guide_how-to-build.md.f4f2a6dd.js +++ /dev/null @@ -1,4 +0,0 @@ -import{_ as s,o as a,c as l,O as n}from"./chunks/framework.43781440.js";const u=JSON.parse('{"title":"如何构建 KernelSU?","description":"","frontmatter":{},"headers":[],"relativePath":"zh_CN/guide/how-to-build.md","filePath":"zh_CN/guide/how-to-build.md"}'),e={name:"zh_CN/guide/how-to-build.md"},o=n(`

如何构建 KernelSU?

首先,您应该阅读内核构建的 Android 官方文档:

  1. 构建内核
  2. 通用内核映像 (GKI) 发布构建

WARNING

本文档适用于 GKI 设备,如果你是旧内核,请参考如何为非GKI设备集成 KernelSU

构建内核

同步内核源码

sh
repo init -u https://android.googlesource.com/kernel/manifest
-mv <kernel_manifest.xml> .repo/manifests
-repo init -m manifest.xml
-repo sync

<kernel_manifest.xml> 是一个可以唯一确定构建的清单文件,您可以使用该清单进行可重新预测的构建。 您应该从 通用内核映像 (GKI) 发布构建 下载清单文件

构建

请先查看 官方文档

例如,我们需要构建 aarch64 内核镜像:

sh
LTO=thin BUILD_CONFIG=common/build.config.gki.aarch64 build/build.sh

不要忘记添加 LTO=thin, 否则,如果您的计算机内存小于 24GB,构建可能会失败.

从 Android 13 开始,内核由 bazel 构建:

sh
tools/bazel build --config=fast //common:kernel_aarch64_dist

使用 KernelSU 构建内核

如果您可以成功构建内核,那么构建 KernelSU 就很容易,根据自己的需求在内核源代码根目录中运行以下任一命令:

  • 最新tag(稳定版本)
sh
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -
  • main分支(开发版本)
sh
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -s main
  • 指定tag(比如v0.5.2)
sh
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -s v0.5.2

然后重建内核,您将获得带有 KernelSU 的内核映像!

`,24),p=[o];function t(r,c,i,C,d,h){return a(),l("div",null,p)}const D=s(e,[["render",t]]);export{u as __pageData,D as default}; diff --git a/assets/zh_CN_guide_how-to-build.md.f4f2a6dd.lean.js b/assets/zh_CN_guide_how-to-build.md.f4f2a6dd.lean.js deleted file mode 100644 index 7cfa774dc8a0..000000000000 --- a/assets/zh_CN_guide_how-to-build.md.f4f2a6dd.lean.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as s,o as a,c as l,O as n}from"./chunks/framework.43781440.js";const u=JSON.parse('{"title":"如何构建 KernelSU?","description":"","frontmatter":{},"headers":[],"relativePath":"zh_CN/guide/how-to-build.md","filePath":"zh_CN/guide/how-to-build.md"}'),e={name:"zh_CN/guide/how-to-build.md"},o=n("",24),p=[o];function t(r,c,i,C,d,h){return a(),l("div",null,p)}const D=s(e,[["render",t]]);export{u as __pageData,D as default}; diff --git a/assets/zh_CN_guide_how-to-integrate-for-non-gki.md.891eeb81.js b/assets/zh_CN_guide_how-to-integrate-for-non-gki.md.891eeb81.js deleted file mode 100644 index 9e61e6813015..000000000000 --- a/assets/zh_CN_guide_how-to-integrate-for-non-gki.md.891eeb81.js +++ /dev/null @@ -1,152 +0,0 @@ -import{_ as s,o as n,c as a,O as l}from"./chunks/framework.43781440.js";const d=JSON.parse('{"title":"如何为非 GKI 内核集成 KernelSU","description":"","frontmatter":{},"headers":[],"relativePath":"zh_CN/guide/how-to-integrate-for-non-gki.md","filePath":"zh_CN/guide/how-to-integrate-for-non-gki.md"}'),p={name:"zh_CN/guide/how-to-integrate-for-non-gki.md"},e=l(`

如何为非 GKI 内核集成 KernelSU

KernelSU 可以被集成到非 GKI 内核中,现在它最低支持到内核 4.14 版本;理论上也可以支持更低的版本。

由于非 GKI 内核的碎片化极其严重,因此通常没有统一的方法来编译它,所以我们也无法为非 GKI 设备提供 boot 镜像。但你完全可以自己集成 KernelSU 然后编译内核使用。

首先,你必须有能力从你设备的内核源码编译出一个可以开机并且能正常使用的内核,如果内核不开源,这通常难以做到。

如果你已经做好了上述准备,那有两个方法来集成 KernelSU 到你的内核之中。

  1. 借助 kprobe 自动集成
  2. 手动修改内核源码

使用 kprobe 集成

KernelSU 使用 kprobe 机制来做内核的相关 hook,如果 kprobe 可以在你编译的内核中正常运行,那么推荐用这个方法来集成。

首先,把 KernelSU 添加到你的内核源码树,在内核的根目录执行以下命令:

  • 最新tag(稳定版本)
sh
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -
  • main分支(开发版本)
sh
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -s main
  • 指定tag(比如v0.5.2)
sh
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -s v0.5.2

然后,你需要检查你的内核是否开启了 kprobe 相关的配置,如果没有开启,需要添加以下配置:

CONFIG_KPROBES=y
-CONFIG_HAVE_KPROBES=y
-CONFIG_KPROBE_EVENTS=y

最后,重新编译你的内核即可。

如果你发现KPROBES仍未生效,很有可能是因为它的依赖项CONFIG_MODULES没有被启用(如果还是未生效请键入make menuconfig搜索KPROBES 的其它依赖并启用 )

如果你在集成 KernelSU 之后手机无法启动,那么很可能你的内核中 kprobe 工作不正常,你需要修复这个 bug 或者用第二种方法。

如何验证是否是 kprobe 的问题?

注释掉 KernelSU/kernel/ksu.cksu_enable_sucompat()ksu_enable_ksud(),如果正常开机,那么就是 kprobe 的问题;或者你可以手动尝试使用 kprobe 功能,如果不正常,手机会直接重启。

手动修改内核源码

如果 kprobe 工作不正常(通常是上游的 bug 或者内核版本过低),那你可以尝试这种方法:

首先,把 KernelSU 添加到你的内核源码树,在内核的根目录执行以下命令:

sh
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -

然后,手动修改内核源码,你可以参考下面这个 patch:

diff
diff --git a/fs/exec.c b/fs/exec.c
-index ac59664eaecf..bdd585e1d2cc 100644
---- a/fs/exec.c
-+++ b/fs/exec.c
-@@ -1890,11 +1890,14 @@ static int __do_execve_file(int fd, struct filename *filename,
- 	return retval;
- }
- 
-+extern bool ksu_execveat_hook __read_mostly;
-+extern int ksu_handle_execveat(int *fd, struct filename **filename_ptr, void *argv,
-+			void *envp, int *flags);
-+extern int ksu_handle_execveat_sucompat(int *fd, struct filename **filename_ptr,
-+				 void *argv, void *envp, int *flags);
- static int do_execveat_common(int fd, struct filename *filename,
- 			      struct user_arg_ptr argv,
- 			      struct user_arg_ptr envp,
- 			      int flags)
- {
-+	if (unlikely(ksu_execveat_hook))
-+		ksu_handle_execveat(&fd, &filename, &argv, &envp, &flags);
-+	else
-+		ksu_handle_execveat_sucompat(&fd, &filename, &argv, &envp, &flags);
- 	return __do_execve_file(fd, filename, argv, envp, flags, NULL);
- }
- 
-diff --git a/fs/open.c b/fs/open.c
-index 05036d819197..965b84d486b8 100644
---- a/fs/open.c
-+++ b/fs/open.c
-@@ -348,6 +348,8 @@ SYSCALL_DEFINE4(fallocate, int, fd, int, mode, loff_t, offset, loff_t, len)
- 	return ksys_fallocate(fd, mode, offset, len);
- }
- 
-+extern int ksu_handle_faccessat(int *dfd, const char __user **filename_user, int *mode,
-+			 int *flags);
- /*
-  * access() needs to use the real uid/gid, not the effective uid/gid.
-  * We do this by temporarily clearing all FS-related capabilities and
-@@ -355,6 +357,7 @@ SYSCALL_DEFINE4(fallocate, int, fd, int, mode, loff_t, offset, loff_t, len)
-  */
- long do_faccessat(int dfd, const char __user *filename, int mode)
- {
-+	ksu_handle_faccessat(&dfd, &filename, &mode, NULL);
- 	const struct cred *old_cred;
- 	struct cred *override_cred;
- 	struct path path;
-diff --git a/fs/read_write.c b/fs/read_write.c
-index 650fc7e0f3a6..55be193913b6 100644
---- a/fs/read_write.c
-+++ b/fs/read_write.c
-@@ -434,10 +434,14 @@ ssize_t kernel_read(struct file *file, void *buf, size_t count, loff_t *pos)
- }
- EXPORT_SYMBOL(kernel_read);
- 
-+extern bool ksu_vfs_read_hook __read_mostly;
-+extern int ksu_handle_vfs_read(struct file **file_ptr, char __user **buf_ptr,
-+			size_t *count_ptr, loff_t **pos);
- ssize_t vfs_read(struct file *file, char __user *buf, size_t count, loff_t *pos)
- {
- 	ssize_t ret;
- 
-+	if (unlikely(ksu_vfs_read_hook))
-+		ksu_handle_vfs_read(&file, &buf, &count, &pos);
-+
- 	if (!(file->f_mode & FMODE_READ))
- 		return -EBADF;
- 	if (!(file->f_mode & FMODE_CAN_READ))
-diff --git a/fs/stat.c b/fs/stat.c
-index 376543199b5a..82adcef03ecc 100644
---- a/fs/stat.c
-+++ b/fs/stat.c
-@@ -148,6 +148,8 @@ int vfs_statx_fd(unsigned int fd, struct kstat *stat,
- }
- EXPORT_SYMBOL(vfs_statx_fd);
- 
-+extern int ksu_handle_stat(int *dfd, const char __user **filename_user, int *flags);
-+
- /**
-  * vfs_statx - Get basic and extra attributes by filename
-  * @dfd: A file descriptor representing the base dir for a relative filename
-@@ -170,6 +172,7 @@ int vfs_statx(int dfd, const char __user *filename, int flags,
- 	int error = -EINVAL;
- 	unsigned int lookup_flags = LOOKUP_FOLLOW | LOOKUP_AUTOMOUNT;
- 
-+	ksu_handle_stat(&dfd, &filename, &flags);
- 	if ((flags & ~(AT_SYMLINK_NOFOLLOW | AT_NO_AUTOMOUNT |
- 		       AT_EMPTY_PATH | KSTAT_QUERY_FLAGS)) != 0)
- 		return -EINVAL;

主要是要改四个地方:

  1. do_faccessat,通常位于 fs/open.c
  2. do_execveat_common,通常位于 fs/exec.c
  3. vfs_read,通常位于 fs/read_write.c
  4. vfs_statx,通常位于 fs/stat.c

如果你的内核没有 vfs_statx, 使用 vfs_fstatat 来代替它:

diff
diff --git a/fs/stat.c b/fs/stat.c
-index 068fdbcc9e26..5348b7bb9db2 100644
---- a/fs/stat.c
-+++ b/fs/stat.c
-@@ -87,6 +87,8 @@ int vfs_fstat(unsigned int fd, struct kstat *stat)
- }
- EXPORT_SYMBOL(vfs_fstat);
- 
-+extern int ksu_handle_stat(int *dfd, const char __user **filename_user, int *flags);
-+
- int vfs_fstatat(int dfd, const char __user *filename, struct kstat *stat,
- 		int flag)
- {
-@@ -94,6 +96,8 @@ int vfs_fstatat(int dfd, const char __user *filename, struct kstat *stat,
- 	int error = -EINVAL;
- 	unsigned int lookup_flags = 0;
- 
-+	ksu_handle_stat(&dfd, &filename, &flag);
-+
- 	if ((flag & ~(AT_SYMLINK_NOFOLLOW | AT_NO_AUTOMOUNT |
- 		      AT_EMPTY_PATH)) != 0)
- 		goto out;

对于早于 4.17 的内核,如果没有 do_faccessat,可以直接找到 faccessat 系统调用的定义然后修改:

diff
diff --git a/fs/open.c b/fs/open.c
-index 2ff887661237..e758d7db7663 100644
---- a/fs/open.c
-+++ b/fs/open.c
-@@ -355,6 +355,9 @@ SYSCALL_DEFINE4(fallocate, int, fd, int, mode, loff_t, offset, loff_t, len)
- 	return error;
- }
- 
-+extern int ksu_handle_faccessat(int *dfd, const char __user **filename_user, int *mode,
-+			        int *flags);
-+
- /*
-  * access() needs to use the real uid/gid, not the effective uid/gid.
-  * We do this by temporarily clearing all FS-related capabilities and
-@@ -370,6 +373,8 @@ SYSCALL_DEFINE3(faccessat, int, dfd, const char __user *, filename, int, mode)
- 	int res;
- 	unsigned int lookup_flags = LOOKUP_FOLLOW;
- 
-+	ksu_handle_faccessat(&dfd, &filename, &mode, NULL);
-+
- 	if (mode & ~S_IRWXO)	/* where's F_OK, X_OK, W_OK, R_OK? */
- 		return -EINVAL;

要使用 KernelSU 内置的安全模式,你还需要修改 drivers/input/input.c 中的 input_handle_event 方法:

TIP

强烈建议开启此功能,对用户救砖会非常有帮助!

diff
diff --git a/drivers/input/input.c b/drivers/input/input.c
-index 45306f9ef247..815091ebfca4 100755
---- a/drivers/input/input.c
-+++ b/drivers/input/input.c
-@@ -367,10 +367,13 @@ static int input_get_disposition(struct input_dev *dev,
- 	return disposition;
- }
- 
-+extern bool ksu_input_hook __read_mostly;
-+extern int ksu_handle_input_handle_event(unsigned int *type, unsigned int *code, int *value);
-+
- static void input_handle_event(struct input_dev *dev,
- 			       unsigned int type, unsigned int code, int value)
- {
-	int disposition = input_get_disposition(dev, type, code, &value);
-+
-+	if (unlikely(ksu_input_hook))
-+		ksu_handle_input_handle_event(&type, &code, &value);
- 
- 	if (disposition != INPUT_IGNORE_EVENT && type != EV_SYN)
- 		add_input_randomness(type, code, value);

改完之后重新编译内核即可。

`,37),o=[e];function t(c,r,i,C,D,A){return n(),a("div",null,o)}const f=s(p,[["render",t]]);export{d as __pageData,f as default}; diff --git a/assets/zh_CN_guide_how-to-integrate-for-non-gki.md.891eeb81.lean.js b/assets/zh_CN_guide_how-to-integrate-for-non-gki.md.891eeb81.lean.js deleted file mode 100644 index 2d59d4239fd1..000000000000 --- a/assets/zh_CN_guide_how-to-integrate-for-non-gki.md.891eeb81.lean.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as s,o as n,c as a,O as l}from"./chunks/framework.43781440.js";const d=JSON.parse('{"title":"如何为非 GKI 内核集成 KernelSU","description":"","frontmatter":{},"headers":[],"relativePath":"zh_CN/guide/how-to-integrate-for-non-gki.md","filePath":"zh_CN/guide/how-to-integrate-for-non-gki.md"}'),p={name:"zh_CN/guide/how-to-integrate-for-non-gki.md"},e=l("",37),o=[e];function t(c,r,i,C,D,A){return n(),a("div",null,o)}const f=s(p,[["render",t]]);export{d as __pageData,f as default}; diff --git a/assets/zh_CN_guide_how-to-integrate-for-non-gki.md.fc1a10e8.js b/assets/zh_CN_guide_how-to-integrate-for-non-gki.md.fc1a10e8.js new file mode 100644 index 000000000000..98bdc738d50d --- /dev/null +++ b/assets/zh_CN_guide_how-to-integrate-for-non-gki.md.fc1a10e8.js @@ -0,0 +1,303 @@ +import{_ as s,o as n,c as a,Q as l}from"./chunks/framework.ec8f7e8e.js";const _=JSON.parse('{"title":"如何为非 GKI 内核集成 KernelSU","description":"","frontmatter":{},"headers":[],"relativePath":"zh_CN/guide/how-to-integrate-for-non-gki.md","filePath":"zh_CN/guide/how-to-integrate-for-non-gki.md"}'),p={name:"zh_CN/guide/how-to-integrate-for-non-gki.md"},e=l(`

如何为非 GKI 内核集成 KernelSU

KernelSU 可以被集成到非 GKI 内核中,现在它最低支持到内核 4.14 版本;理论上也可以支持更低的版本。

由于非 GKI 内核的碎片化极其严重,因此通常没有统一的方法来编译它,所以我们也无法为非 GKI 设备提供 boot 镜像。但你完全可以自己集成 KernelSU 然后编译内核使用。

首先,你必须有能力从你设备的内核源码编译出一个可以开机并且能正常使用的内核,如果内核不开源,这通常难以做到。

如果你已经做好了上述准备,那有两个方法来集成 KernelSU 到你的内核之中。

  1. 借助 kprobe 自动集成
  2. 手动修改内核源码

使用 kprobe 集成

KernelSU 使用 kprobe 机制来做内核的相关 hook,如果 kprobe 可以在你编译的内核中正常运行,那么推荐用这个方法来集成。

首先,把 KernelSU 添加到你的内核源码树,在内核的根目录执行以下命令:

  • 最新tag(稳定版本)
sh
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -
  • main分支(开发版本)
sh
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -s main
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -s main
  • 指定tag(比如v0.5.2)
sh
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -s v0.5.2
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -s v0.5.2

然后,你需要检查你的内核是否开启了 kprobe 相关的配置,如果没有开启,需要添加以下配置:

CONFIG_KPROBES=y
+CONFIG_HAVE_KPROBES=y
+CONFIG_KPROBE_EVENTS=y
CONFIG_KPROBES=y
+CONFIG_HAVE_KPROBES=y
+CONFIG_KPROBE_EVENTS=y

最后,重新编译你的内核即可。

如果你发现KPROBES仍未生效,很有可能是因为它的依赖项CONFIG_MODULES没有被启用(如果还是未生效请键入make menuconfig搜索KPROBES 的其它依赖并启用 )

如果你在集成 KernelSU 之后手机无法启动,那么很可能你的内核中 kprobe 工作不正常,你需要修复这个 bug 或者用第二种方法。

如何验证是否是 kprobe 的问题?

注释掉 KernelSU/kernel/ksu.cksu_enable_sucompat()ksu_enable_ksud(),如果正常开机,那么就是 kprobe 的问题;或者你可以手动尝试使用 kprobe 功能,如果不正常,手机会直接重启。

手动修改内核源码

如果 kprobe 工作不正常(通常是上游的 bug 或者内核版本过低),那你可以尝试这种方法:

首先,把 KernelSU 添加到你的内核源码树,在内核的根目录执行以下命令:

sh
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -

然后,手动修改内核源码,你可以参考下面这个 patch:

diff
diff --git a/fs/exec.c b/fs/exec.c
+index ac59664eaecf..bdd585e1d2cc 100644
+--- a/fs/exec.c
++++ b/fs/exec.c
+@@ -1890,11 +1890,14 @@ static int __do_execve_file(int fd, struct filename *filename,
+ 	return retval;
+ }
+ 
++extern bool ksu_execveat_hook __read_mostly;
++extern int ksu_handle_execveat(int *fd, struct filename **filename_ptr, void *argv,
++			void *envp, int *flags);
++extern int ksu_handle_execveat_sucompat(int *fd, struct filename **filename_ptr,
++				 void *argv, void *envp, int *flags);
+ static int do_execveat_common(int fd, struct filename *filename,
+ 			      struct user_arg_ptr argv,
+ 			      struct user_arg_ptr envp,
+ 			      int flags)
+ {
++	if (unlikely(ksu_execveat_hook))
++		ksu_handle_execveat(&fd, &filename, &argv, &envp, &flags);
++	else
++		ksu_handle_execveat_sucompat(&fd, &filename, &argv, &envp, &flags);
+ 	return __do_execve_file(fd, filename, argv, envp, flags, NULL);
+ }
+ 
+diff --git a/fs/open.c b/fs/open.c
+index 05036d819197..965b84d486b8 100644
+--- a/fs/open.c
++++ b/fs/open.c
+@@ -348,6 +348,8 @@ SYSCALL_DEFINE4(fallocate, int, fd, int, mode, loff_t, offset, loff_t, len)
+ 	return ksys_fallocate(fd, mode, offset, len);
+ }
+ 
++extern int ksu_handle_faccessat(int *dfd, const char __user **filename_user, int *mode,
++			 int *flags);
+ /*
+  * access() needs to use the real uid/gid, not the effective uid/gid.
+  * We do this by temporarily clearing all FS-related capabilities and
+@@ -355,6 +357,7 @@ SYSCALL_DEFINE4(fallocate, int, fd, int, mode, loff_t, offset, loff_t, len)
+  */
+ long do_faccessat(int dfd, const char __user *filename, int mode)
+ {
++	ksu_handle_faccessat(&dfd, &filename, &mode, NULL);
+ 	const struct cred *old_cred;
+ 	struct cred *override_cred;
+ 	struct path path;
+diff --git a/fs/read_write.c b/fs/read_write.c
+index 650fc7e0f3a6..55be193913b6 100644
+--- a/fs/read_write.c
++++ b/fs/read_write.c
+@@ -434,10 +434,14 @@ ssize_t kernel_read(struct file *file, void *buf, size_t count, loff_t *pos)
+ }
+ EXPORT_SYMBOL(kernel_read);
+ 
++extern bool ksu_vfs_read_hook __read_mostly;
++extern int ksu_handle_vfs_read(struct file **file_ptr, char __user **buf_ptr,
++			size_t *count_ptr, loff_t **pos);
+ ssize_t vfs_read(struct file *file, char __user *buf, size_t count, loff_t *pos)
+ {
+ 	ssize_t ret;
+ 
++	if (unlikely(ksu_vfs_read_hook))
++		ksu_handle_vfs_read(&file, &buf, &count, &pos);
++
+ 	if (!(file->f_mode & FMODE_READ))
+ 		return -EBADF;
+ 	if (!(file->f_mode & FMODE_CAN_READ))
+diff --git a/fs/stat.c b/fs/stat.c
+index 376543199b5a..82adcef03ecc 100644
+--- a/fs/stat.c
++++ b/fs/stat.c
+@@ -148,6 +148,8 @@ int vfs_statx_fd(unsigned int fd, struct kstat *stat,
+ }
+ EXPORT_SYMBOL(vfs_statx_fd);
+ 
++extern int ksu_handle_stat(int *dfd, const char __user **filename_user, int *flags);
++
+ /**
+  * vfs_statx - Get basic and extra attributes by filename
+  * @dfd: A file descriptor representing the base dir for a relative filename
+@@ -170,6 +172,7 @@ int vfs_statx(int dfd, const char __user *filename, int flags,
+ 	int error = -EINVAL;
+ 	unsigned int lookup_flags = LOOKUP_FOLLOW | LOOKUP_AUTOMOUNT;
+ 
++	ksu_handle_stat(&dfd, &filename, &flags);
+ 	if ((flags & ~(AT_SYMLINK_NOFOLLOW | AT_NO_AUTOMOUNT |
+ 		       AT_EMPTY_PATH | KSTAT_QUERY_FLAGS)) != 0)
+ 		return -EINVAL;
diff --git a/fs/exec.c b/fs/exec.c
+index ac59664eaecf..bdd585e1d2cc 100644
+--- a/fs/exec.c
++++ b/fs/exec.c
+@@ -1890,11 +1890,14 @@ static int __do_execve_file(int fd, struct filename *filename,
+ 	return retval;
+ }
+ 
++extern bool ksu_execveat_hook __read_mostly;
++extern int ksu_handle_execveat(int *fd, struct filename **filename_ptr, void *argv,
++			void *envp, int *flags);
++extern int ksu_handle_execveat_sucompat(int *fd, struct filename **filename_ptr,
++				 void *argv, void *envp, int *flags);
+ static int do_execveat_common(int fd, struct filename *filename,
+ 			      struct user_arg_ptr argv,
+ 			      struct user_arg_ptr envp,
+ 			      int flags)
+ {
++	if (unlikely(ksu_execveat_hook))
++		ksu_handle_execveat(&fd, &filename, &argv, &envp, &flags);
++	else
++		ksu_handle_execveat_sucompat(&fd, &filename, &argv, &envp, &flags);
+ 	return __do_execve_file(fd, filename, argv, envp, flags, NULL);
+ }
+ 
+diff --git a/fs/open.c b/fs/open.c
+index 05036d819197..965b84d486b8 100644
+--- a/fs/open.c
++++ b/fs/open.c
+@@ -348,6 +348,8 @@ SYSCALL_DEFINE4(fallocate, int, fd, int, mode, loff_t, offset, loff_t, len)
+ 	return ksys_fallocate(fd, mode, offset, len);
+ }
+ 
++extern int ksu_handle_faccessat(int *dfd, const char __user **filename_user, int *mode,
++			 int *flags);
+ /*
+  * access() needs to use the real uid/gid, not the effective uid/gid.
+  * We do this by temporarily clearing all FS-related capabilities and
+@@ -355,6 +357,7 @@ SYSCALL_DEFINE4(fallocate, int, fd, int, mode, loff_t, offset, loff_t, len)
+  */
+ long do_faccessat(int dfd, const char __user *filename, int mode)
+ {
++	ksu_handle_faccessat(&dfd, &filename, &mode, NULL);
+ 	const struct cred *old_cred;
+ 	struct cred *override_cred;
+ 	struct path path;
+diff --git a/fs/read_write.c b/fs/read_write.c
+index 650fc7e0f3a6..55be193913b6 100644
+--- a/fs/read_write.c
++++ b/fs/read_write.c
+@@ -434,10 +434,14 @@ ssize_t kernel_read(struct file *file, void *buf, size_t count, loff_t *pos)
+ }
+ EXPORT_SYMBOL(kernel_read);
+ 
++extern bool ksu_vfs_read_hook __read_mostly;
++extern int ksu_handle_vfs_read(struct file **file_ptr, char __user **buf_ptr,
++			size_t *count_ptr, loff_t **pos);
+ ssize_t vfs_read(struct file *file, char __user *buf, size_t count, loff_t *pos)
+ {
+ 	ssize_t ret;
+ 
++	if (unlikely(ksu_vfs_read_hook))
++		ksu_handle_vfs_read(&file, &buf, &count, &pos);
++
+ 	if (!(file->f_mode & FMODE_READ))
+ 		return -EBADF;
+ 	if (!(file->f_mode & FMODE_CAN_READ))
+diff --git a/fs/stat.c b/fs/stat.c
+index 376543199b5a..82adcef03ecc 100644
+--- a/fs/stat.c
++++ b/fs/stat.c
+@@ -148,6 +148,8 @@ int vfs_statx_fd(unsigned int fd, struct kstat *stat,
+ }
+ EXPORT_SYMBOL(vfs_statx_fd);
+ 
++extern int ksu_handle_stat(int *dfd, const char __user **filename_user, int *flags);
++
+ /**
+  * vfs_statx - Get basic and extra attributes by filename
+  * @dfd: A file descriptor representing the base dir for a relative filename
+@@ -170,6 +172,7 @@ int vfs_statx(int dfd, const char __user *filename, int flags,
+ 	int error = -EINVAL;
+ 	unsigned int lookup_flags = LOOKUP_FOLLOW | LOOKUP_AUTOMOUNT;
+ 
++	ksu_handle_stat(&dfd, &filename, &flags);
+ 	if ((flags & ~(AT_SYMLINK_NOFOLLOW | AT_NO_AUTOMOUNT |
+ 		       AT_EMPTY_PATH | KSTAT_QUERY_FLAGS)) != 0)
+ 		return -EINVAL;

主要是要改四个地方:

  1. do_faccessat,通常位于 fs/open.c
  2. do_execveat_common,通常位于 fs/exec.c
  3. vfs_read,通常位于 fs/read_write.c
  4. vfs_statx,通常位于 fs/stat.c

如果你的内核没有 vfs_statx, 使用 vfs_fstatat 来代替它:

diff
diff --git a/fs/stat.c b/fs/stat.c
+index 068fdbcc9e26..5348b7bb9db2 100644
+--- a/fs/stat.c
++++ b/fs/stat.c
+@@ -87,6 +87,8 @@ int vfs_fstat(unsigned int fd, struct kstat *stat)
+ }
+ EXPORT_SYMBOL(vfs_fstat);
+ 
++extern int ksu_handle_stat(int *dfd, const char __user **filename_user, int *flags);
++
+ int vfs_fstatat(int dfd, const char __user *filename, struct kstat *stat,
+ 		int flag)
+ {
+@@ -94,6 +96,8 @@ int vfs_fstatat(int dfd, const char __user *filename, struct kstat *stat,
+ 	int error = -EINVAL;
+ 	unsigned int lookup_flags = 0;
+ 
++	ksu_handle_stat(&dfd, &filename, &flag);
++
+ 	if ((flag & ~(AT_SYMLINK_NOFOLLOW | AT_NO_AUTOMOUNT |
+ 		      AT_EMPTY_PATH)) != 0)
+ 		goto out;
diff --git a/fs/stat.c b/fs/stat.c
+index 068fdbcc9e26..5348b7bb9db2 100644
+--- a/fs/stat.c
++++ b/fs/stat.c
+@@ -87,6 +87,8 @@ int vfs_fstat(unsigned int fd, struct kstat *stat)
+ }
+ EXPORT_SYMBOL(vfs_fstat);
+ 
++extern int ksu_handle_stat(int *dfd, const char __user **filename_user, int *flags);
++
+ int vfs_fstatat(int dfd, const char __user *filename, struct kstat *stat,
+ 		int flag)
+ {
+@@ -94,6 +96,8 @@ int vfs_fstatat(int dfd, const char __user *filename, struct kstat *stat,
+ 	int error = -EINVAL;
+ 	unsigned int lookup_flags = 0;
+ 
++	ksu_handle_stat(&dfd, &filename, &flag);
++
+ 	if ((flag & ~(AT_SYMLINK_NOFOLLOW | AT_NO_AUTOMOUNT |
+ 		      AT_EMPTY_PATH)) != 0)
+ 		goto out;

对于早于 4.17 的内核,如果没有 do_faccessat,可以直接找到 faccessat 系统调用的定义然后修改:

diff
diff --git a/fs/open.c b/fs/open.c
+index 2ff887661237..e758d7db7663 100644
+--- a/fs/open.c
++++ b/fs/open.c
+@@ -355,6 +355,9 @@ SYSCALL_DEFINE4(fallocate, int, fd, int, mode, loff_t, offset, loff_t, len)
+ 	return error;
+ }
+ 
++extern int ksu_handle_faccessat(int *dfd, const char __user **filename_user, int *mode,
++			        int *flags);
++
+ /*
+  * access() needs to use the real uid/gid, not the effective uid/gid.
+  * We do this by temporarily clearing all FS-related capabilities and
+@@ -370,6 +373,8 @@ SYSCALL_DEFINE3(faccessat, int, dfd, const char __user *, filename, int, mode)
+ 	int res;
+ 	unsigned int lookup_flags = LOOKUP_FOLLOW;
+ 
++	ksu_handle_faccessat(&dfd, &filename, &mode, NULL);
++
+ 	if (mode & ~S_IRWXO)	/* where's F_OK, X_OK, W_OK, R_OK? */
+ 		return -EINVAL;
diff --git a/fs/open.c b/fs/open.c
+index 2ff887661237..e758d7db7663 100644
+--- a/fs/open.c
++++ b/fs/open.c
+@@ -355,6 +355,9 @@ SYSCALL_DEFINE4(fallocate, int, fd, int, mode, loff_t, offset, loff_t, len)
+ 	return error;
+ }
+ 
++extern int ksu_handle_faccessat(int *dfd, const char __user **filename_user, int *mode,
++			        int *flags);
++
+ /*
+  * access() needs to use the real uid/gid, not the effective uid/gid.
+  * We do this by temporarily clearing all FS-related capabilities and
+@@ -370,6 +373,8 @@ SYSCALL_DEFINE3(faccessat, int, dfd, const char __user *, filename, int, mode)
+ 	int res;
+ 	unsigned int lookup_flags = LOOKUP_FOLLOW;
+ 
++	ksu_handle_faccessat(&dfd, &filename, &mode, NULL);
++
+ 	if (mode & ~S_IRWXO)	/* where's F_OK, X_OK, W_OK, R_OK? */
+ 		return -EINVAL;

要使用 KernelSU 内置的安全模式,你还需要修改 drivers/input/input.c 中的 input_handle_event 方法:

TIP

强烈建议开启此功能,对用户救砖会非常有帮助!

diff
diff --git a/drivers/input/input.c b/drivers/input/input.c
+index 45306f9ef247..815091ebfca4 100755
+--- a/drivers/input/input.c
++++ b/drivers/input/input.c
+@@ -367,10 +367,13 @@ static int input_get_disposition(struct input_dev *dev,
+ 	return disposition;
+ }
+ 
++extern bool ksu_input_hook __read_mostly;
++extern int ksu_handle_input_handle_event(unsigned int *type, unsigned int *code, int *value);
++
+ static void input_handle_event(struct input_dev *dev,
+ 			       unsigned int type, unsigned int code, int value)
+ {
+	int disposition = input_get_disposition(dev, type, code, &value);
++
++	if (unlikely(ksu_input_hook))
++		ksu_handle_input_handle_event(&type, &code, &value);
+ 
+ 	if (disposition != INPUT_IGNORE_EVENT && type != EV_SYN)
+ 		add_input_randomness(type, code, value);
diff --git a/drivers/input/input.c b/drivers/input/input.c
+index 45306f9ef247..815091ebfca4 100755
+--- a/drivers/input/input.c
++++ b/drivers/input/input.c
+@@ -367,10 +367,13 @@ static int input_get_disposition(struct input_dev *dev,
+ 	return disposition;
+ }
+ 
++extern bool ksu_input_hook __read_mostly;
++extern int ksu_handle_input_handle_event(unsigned int *type, unsigned int *code, int *value);
++
+ static void input_handle_event(struct input_dev *dev,
+ 			       unsigned int type, unsigned int code, int value)
+ {
+	int disposition = input_get_disposition(dev, type, code, &value);
++
++	if (unlikely(ksu_input_hook))
++		ksu_handle_input_handle_event(&type, &code, &value);
+ 
+ 	if (disposition != INPUT_IGNORE_EVENT && type != EV_SYN)
+ 		add_input_randomness(type, code, value);

改完之后重新编译内核即可。

`,37),t=[e];function o(c,i,r,E,d,f){return n(),a("div",null,t)}const u=s(p,[["render",o]]);export{_ as __pageData,u as default}; diff --git a/assets/zh_CN_guide_how-to-integrate-for-non-gki.md.fc1a10e8.lean.js b/assets/zh_CN_guide_how-to-integrate-for-non-gki.md.fc1a10e8.lean.js new file mode 100644 index 000000000000..6c157de0cbe1 --- /dev/null +++ b/assets/zh_CN_guide_how-to-integrate-for-non-gki.md.fc1a10e8.lean.js @@ -0,0 +1 @@ +import{_ as s,o as n,c as a,Q as l}from"./chunks/framework.ec8f7e8e.js";const _=JSON.parse('{"title":"如何为非 GKI 内核集成 KernelSU","description":"","frontmatter":{},"headers":[],"relativePath":"zh_CN/guide/how-to-integrate-for-non-gki.md","filePath":"zh_CN/guide/how-to-integrate-for-non-gki.md"}'),p={name:"zh_CN/guide/how-to-integrate-for-non-gki.md"},e=l("",37),t=[e];function o(c,i,r,E,d,f){return n(),a("div",null,t)}const u=s(p,[["render",o]]);export{_ as __pageData,u as default}; diff --git a/assets/zh_CN_guide_installation.md.612797b7.lean.js b/assets/zh_CN_guide_installation.md.612797b7.lean.js deleted file mode 100644 index ee839bf78c62..000000000000 --- a/assets/zh_CN_guide_installation.md.612797b7.lean.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as a,o as l,c as t,O as o,z as e,a as i}from"./chunks/framework.43781440.js";const y=JSON.parse('{"title":"安装","description":"","frontmatter":{},"headers":[],"relativePath":"zh_CN/guide/installation.md","filePath":"zh_CN/guide/installation.md"}'),r={name:"zh_CN/guide/installation.md"},n=o("",58),s=e("h3",{id:"using",magiskboot:"",tabindex:"-1"},[i("使用 magiskboot "),e("a",{class:"header-anchor",href:"#using","aria-label":'Permalink to "使用 magiskboot {#using magiskboot}"'},"​")],-1),d=o("",4),c=[n,s,d];function p(h,b,g,m,u,k){return l(),t("div",null,c)}const K=a(r,[["render",p]]);export{y as __pageData,K as default}; diff --git a/assets/zh_CN_guide_installation.md.612797b7.js b/assets/zh_CN_guide_installation.md.b6f3f2dc.js similarity index 56% rename from assets/zh_CN_guide_installation.md.612797b7.js rename to assets/zh_CN_guide_installation.md.b6f3f2dc.js index 50d59c3b896f..c770b38d82ea 100644 --- a/assets/zh_CN_guide_installation.md.612797b7.js +++ b/assets/zh_CN_guide_installation.md.b6f3f2dc.js @@ -1,3 +1,5 @@ -import{_ as a,o as l,c as t,O as o,z as e,a as i}from"./chunks/framework.43781440.js";const y=JSON.parse('{"title":"安装","description":"","frontmatter":{},"headers":[],"relativePath":"zh_CN/guide/installation.md","filePath":"zh_CN/guide/installation.md"}'),r={name:"zh_CN/guide/installation.md"},n=o(`

安装

检查您的设备是否被支持

GitHub Releases酷安 下载 KernelSU 管理器应用,然后将应用程序安装到设备并打开:

  • 如果应用程序显示 “不支持”,则表示您的设备不支持 KernelSU,你需要自己编译设备的内核才能使用,KernelSU 官方不会也永远不会为你提供一个可以刷写的 boot 镜像。
  • 如果应用程序显示 “未安装”,那么 KernelSU 支持您的设备;可以进行下一步操作。

INFO

对于显示“不支持”的设备,这里有一个非官方支持设备列表,你可以用这个列表里面的内核自行编译。

备份你的 boot.img

在进行刷机操作之前,你必须先备份好自己的原厂 boot.img。如果你后续刷机出现了任何问题,你都可以通过使用 fastboot 刷回原厂 boot 来恢复系统。

WARNING

任何刷机操作都是有风险的,请务必做好这一步再进行下一步操作!!必要时你还可以备份你手机的所有数据。

必备知识

ADB 和 fastboot

此教程默认你会使用 ADB 和 fastboot 工具,如果你没有了解过,建议使用搜索引擎先学习相关知识。

KMI

KMI 全称 Kernel Module Interface,相同 KMI 的内核版本是兼容的 这也是 GKI 中“通用”的含义所在;反之,如果 KMI 不同,那么这些内核之间无法互相兼容,刷入与你设备 KMI 不同的内核镜像可能会导致死机。

具体来说,对 GKI 的设备,其内核版本格式应该如下:

txt
KernelRelease :=
-Version.PatchLevel.SubLevel-AndroidRelease-KmiGeneration-suffix
-w      .x         .y       -zzz           -k            -something

其中,w.x-zzz-k 为 KMI 版本。例如,一个设备内核版本为5.10.101-android12-9-g30979850fc20,那么它的 KMI 为 5.10-android12-9;理论上刷入其他这个 KMI 的内核也能正常开机。

TIP

请注意,内核版本中的 SubLevel 不属于 KMI 的范畴!也就是说 5.10.101-android12-9-g30979850fc205.10.137-android12-9-g30979850fc20 的 KMI 相同!

内核版本与 Android 版本

请注意:内核版本与 Android 版本并不一定相同!

如果您发现您的内核版本是 android12-5.10.101,然而你 Android 系统的版本为 Android 13 或者其他;请不要觉得奇怪,因为 Android 系统的版本与 Linux 内核的版本号不一定是一致的;Linux 内核的版本号一般与设备出厂的时候自带的 Android 系统的版本一致,如果后续 Android 系统升级,内核版本一般不会发生变化。如果你需要刷机,请以内核版本为准!!

安装介绍

KernelSU 的安装方法有如下几种,各自适用于不同的场景,请按需选择:

  1. 使用自定义 Recovery(如 TWRP)安装
  2. 使用内核刷写 App,如 (Franco Kernel Manager)安装
  3. 使用 KernelSU 提供的 boot.img 使用 fastboot 安装
  4. 手动修补 boot.img 然后安装

使用自定义 Recovery 安装

前提:你的设备必须有自定义的 Recovery,如 TWRP;如果没有或者只有官方 Recovery,请使用其他方法。

步骤:

  1. 在 KernelSU 的 Release 页面 下载与你手机版本匹配的以 AnyKernel3 开头的 zip 刷机包;例如,手机内核版本为 android12-5.10.66,那么你应该下载 AnyKernel3-android12-5.10.66_yyyy-MM.zip 这个文件(其中 yyyy 为年份,MM 为月份)。
  2. 重启手机进入 TWRP。
  3. 使用 adb 将 AnyKernel3-*.zip 放到手机 /sdcard 然后在 TWRP 图形界面选择安装;或者你也可以直接 adb sideload AnyKernel-*.zip 安装。

PS. 这种方法适用于任何情况下的安装(不限于初次安装或者后续升级),只要你用 TWRP 就可以操作。

使用内核刷写 App 安装

前提:你的设备必须已经 root。例如你已经安装了 Magisk 获取了 root,或者你已经安装了旧版本的 KernelSU 需要升级到其他版本的 KernelSU;如果你的设备无 root,请尝试其他方法。

步骤:

  1. 下载 AnyKernel3 的刷机包;下载方法参考 使用自定义 Recovery 安装那一节的内容。
  2. 打开内核刷写 App 使用提供的 AnyKernel3 刷机包刷入。

如果你之前没有用过内核刷写 App,那么下面几个是比较流行的:

  1. Kernel Flasher
  2. Franco Kernel Manager
  3. Ex Kernel Manager

PS. 这种方法在升级 KernelSU 的时候较为方便,无需电脑即可完成(注意备份!)。

使用 KernelSU 提供的 boot.img 安装

这种方法无需你有 TWRP,也不需要你的手机有 root 权限;适用于你初次安装 KernelSU。

找到合适的 boot.img

KernelSU 为 GKI 设备提供了通用的 boot.img,您应该将 boot.img 刷写到设备的 boot 分区。

您可以从 GitHub Release 下载 boot.img, 请注意您应该使用正确版本的 boot.img. 例如,如果您的设备显示内核是 android12-5.10.101, 需要下载 android-5.10.101_yyyy-MM.boot-<format>.img.

其中 <format> 指的是你的官方 boot.img 的内核压缩格式,请检查您原有 boot.img 的内核压缩格式,您应该使用正确的格式,例如 lz4gz;如果是用不正确的压缩格式,刷入 boot 后可能无法开机。

INFO

  1. 您可以通过 magiskboot 来获取你原来 boot 的压缩格式;当然您也可以询问与您机型相同的其他更有经验的童鞋。另外,内核的压缩格式通常不会发生变化,如果您使用某个压缩格式成功开机,后续可优先尝试这个格式。
  2. 小米设备通常使用 gz 或者 不压缩
  3. Pixel 设备有些特殊,请查看下面的教程。

将 boot.img 刷入设备

使用 adb 连接您的设备,然后执行 adb reboot bootloader 进入 fastboot 模式,然后使用此命令刷入 KernelSU:

sh
fastboot flash boot boot.img

INFO

如果你的设备支持 fastboot boot,可以先使用 fastboot boot boot.img 来先尝试使用 boot.img 引导系统,如果出现意外,再重启一次即可开机。

重启

刷入完成后,您应该重新启动您的设备:

sh
fastboot reboot

手动修补 boot.img

对于某些设备来说,其 boot.img 格式不那么常见,比如不是 lz4, gz 和未压缩;最典型的就是 Pixel,它 boot.img 的格式是 lz4_legacy 压缩,ramdisk 可能是 gz 也可能是 lz4_legacy 压缩;此时如果你直接刷入 KernelSU 提供的 boot.img,手机可能无法开机;这时候,你可以通过手动修补 boot.img 来实现。

修补方法总体有两种:

  1. Android-Image-Kitchen
  2. magiskboot

其中,Android-Image-Kitchen 适用于 PC 上操作,magiskboot 需要手机配合。

准备

  1. 获取你手机的原厂 boot.img;你可以通过你手机的线刷包解压后之间获取,如果你是卡刷包,那你也许需要payload-dumper-go
  2. 下载 KernelSU 提供的与你设备 KMI 版本一致的 AnyKernel3 刷机包(可以参考 自定义 TWRP 刷入一节)。
  3. 解压缩 AnyKernel3 刷机包,获取其中的 Image 文件,此文件为 KernelSU 的内核文件。

使用 Android-Image-Kitchen

  1. 下载 Android-Image-Kitchen 至你电脑
  2. 将手机原厂 boot.img 放入 Android-Image-Kitchen 根目录
  3. 在 Android-Image-Kitchen 根目录执行 ./unpackimg.sh boot.img;此命名会将 boot.img 拆开,你会得到若干文件。
  4. split_img 目录中的 boot.img-kernel 替换为你从 AnyKernel3 解压出来的 Image(注意名字改为 boot.img-kernel)。
  5. 在 Android-Image-Kitchecn 根目录执行 ./repackimg.sh;此时你会得到一个 image-new.img 的文件;使用此 boot.img 通过 fastboot 刷入即可(刷入方法参考上一节)。
`,58),s=e("h3",{id:"using",magiskboot:"",tabindex:"-1"},[i("使用 magiskboot "),e("a",{class:"header-anchor",href:"#using","aria-label":'Permalink to "使用 magiskboot {#using magiskboot}"'},"​")],-1),d=o('
  1. 在 Magisk 的 Release 页面 下载最新的 Magisk 安装包。
  2. 将 Magisk-*.apk 重命名为 Magisk-vesion.zip 然后解压缩。
  3. 将解压后的 Magisk-v25.2/lib/arm64-v8a/libmagiskboot.so 文件,使用 adb push 到手机:adb push Magisk-v25.2/lib/arm64-v8a/libmagiskboot.so /data/local/tmp/magiskboot
  4. 使用 adb 将原厂 boot.img 和 AnyKernel3 中的 Image 推送到手机
  5. adb shell 进入 /data/local/tmp/ 目录,然后赋予刚 push 文件的可执行权限 chmod +x magiskboot
  6. adb shell 进入 /data/local/tmp/ 目录,执行 ./magiskboot unpack boot.img 此时会解包 boot.img 得到一个叫做 kernel 的文件,这个文件为你原厂的 kernel
  7. 使用 Image 替换 kernel: mv -f Image kernel
  8. 执行 ./magiskboot repack boot.img 打包 img,此时你会得到一个 new-boot.img 的文件,使用这个文件 fastboot 刷入设备即可。

其他变通方法

其实所有这些安装方法的主旨只有一个,那就是替换原厂的内核为 KernelSU 提供的内核;只要能实现这个目的,就可以安装;比如以下是其他可行的方法:

  1. 首先安装 Magisk,通过 Magisk 获取 root 权限后使用内核刷写器刷入 KernelSU 的 AnyKernel 包。
  2. 使用某些 PC 上的刷机工具箱刷入 KernelSU 提供的内核。
',4),c=[n,s,d];function p(h,b,g,m,u,k){return l(),t("div",null,c)}const K=a(r,[["render",p]]);export{y as __pageData,K as default}; +import{_ as a,o as l,c as t,Q as o,k as e,a as i}from"./chunks/framework.ec8f7e8e.js";const y=JSON.parse('{"title":"安装","description":"","frontmatter":{},"headers":[],"relativePath":"zh_CN/guide/installation.md","filePath":"zh_CN/guide/installation.md"}'),r={name:"zh_CN/guide/installation.md"},s=o(`

安装

检查您的设备是否被支持

GitHub Releases酷安 下载 KernelSU 管理器应用,然后将应用程序安装到设备并打开:

  • 如果应用程序显示 “不支持”,则表示您的设备不支持 KernelSU,你需要自己编译设备的内核才能使用,KernelSU 官方不会也永远不会为你提供一个可以刷写的 boot 镜像。
  • 如果应用程序显示 “未安装”,那么 KernelSU 支持您的设备;可以进行下一步操作。

INFO

对于显示“不支持”的设备,这里有一个非官方支持设备列表,你可以用这个列表里面的内核自行编译。

备份你的 boot.img

在进行刷机操作之前,你必须先备份好自己的原厂 boot.img。如果你后续刷机出现了任何问题,你都可以通过使用 fastboot 刷回原厂 boot 来恢复系统。

WARNING

任何刷机操作都是有风险的,请务必做好这一步再进行下一步操作!!必要时你还可以备份你手机的所有数据。

必备知识

ADB 和 fastboot

此教程默认你会使用 ADB 和 fastboot 工具,如果你没有了解过,建议使用搜索引擎先学习相关知识。

KMI

KMI 全称 Kernel Module Interface,相同 KMI 的内核版本是兼容的 这也是 GKI 中“通用”的含义所在;反之,如果 KMI 不同,那么这些内核之间无法互相兼容,刷入与你设备 KMI 不同的内核镜像可能会导致死机。

具体来说,对 GKI 的设备,其内核版本格式应该如下:

txt
KernelRelease :=
+Version.PatchLevel.SubLevel-AndroidRelease-KmiGeneration-suffix
+w      .x         .y       -zzz           -k            -something
KernelRelease :=
+Version.PatchLevel.SubLevel-AndroidRelease-KmiGeneration-suffix
+w      .x         .y       -zzz           -k            -something

其中,w.x-zzz-k 为 KMI 版本。例如,一个设备内核版本为5.10.101-android12-9-g30979850fc20,那么它的 KMI 为 5.10-android12-9;理论上刷入其他这个 KMI 的内核也能正常开机。

TIP

请注意,内核版本中的 SubLevel 不属于 KMI 的范畴!也就是说 5.10.101-android12-9-g30979850fc205.10.137-android12-9-g30979850fc20 的 KMI 相同!

内核版本与 Android 版本

请注意:内核版本与 Android 版本并不一定相同!

如果您发现您的内核版本是 android12-5.10.101,然而你 Android 系统的版本为 Android 13 或者其他;请不要觉得奇怪,因为 Android 系统的版本与 Linux 内核的版本号不一定是一致的;Linux 内核的版本号一般与设备出厂的时候自带的 Android 系统的版本一致,如果后续 Android 系统升级,内核版本一般不会发生变化。如果你需要刷机,请以内核版本为准!!

安装介绍

KernelSU 的安装方法有如下几种,各自适用于不同的场景,请按需选择:

  1. 使用自定义 Recovery(如 TWRP)安装
  2. 使用内核刷写 App,如 (Franco Kernel Manager)安装
  3. 使用 KernelSU 提供的 boot.img 使用 fastboot 安装
  4. 手动修补 boot.img 然后安装

使用自定义 Recovery 安装

前提:你的设备必须有自定义的 Recovery,如 TWRP;如果没有或者只有官方 Recovery,请使用其他方法。

步骤:

  1. 在 KernelSU 的 Release 页面 下载与你手机版本匹配的以 AnyKernel3 开头的 zip 刷机包;例如,手机内核版本为 android12-5.10.66,那么你应该下载 AnyKernel3-android12-5.10.66_yyyy-MM.zip 这个文件(其中 yyyy 为年份,MM 为月份)。
  2. 重启手机进入 TWRP。
  3. 使用 adb 将 AnyKernel3-*.zip 放到手机 /sdcard 然后在 TWRP 图形界面选择安装;或者你也可以直接 adb sideload AnyKernel-*.zip 安装。

PS. 这种方法适用于任何情况下的安装(不限于初次安装或者后续升级),只要你用 TWRP 就可以操作。

使用内核刷写 App 安装

前提:你的设备必须已经 root。例如你已经安装了 Magisk 获取了 root,或者你已经安装了旧版本的 KernelSU 需要升级到其他版本的 KernelSU;如果你的设备无 root,请尝试其他方法。

步骤:

  1. 下载 AnyKernel3 的刷机包;下载方法参考 使用自定义 Recovery 安装那一节的内容。
  2. 打开内核刷写 App 使用提供的 AnyKernel3 刷机包刷入。

如果你之前没有用过内核刷写 App,那么下面几个是比较流行的:

  1. Kernel Flasher
  2. Franco Kernel Manager
  3. Ex Kernel Manager

PS. 这种方法在升级 KernelSU 的时候较为方便,无需电脑即可完成(注意备份!)。

使用 KernelSU 提供的 boot.img 安装

这种方法无需你有 TWRP,也不需要你的手机有 root 权限;适用于你初次安装 KernelSU。

找到合适的 boot.img

KernelSU 为 GKI 设备提供了通用的 boot.img,您应该将 boot.img 刷写到设备的 boot 分区。

您可以从 GitHub Release 下载 boot.img, 请注意您应该使用正确版本的 boot.img. 例如,如果您的设备显示内核是 android12-5.10.101, 需要下载 android-5.10.101_yyyy-MM.boot-<format>.img.

其中 <format> 指的是你的官方 boot.img 的内核压缩格式,请检查您原有 boot.img 的内核压缩格式,您应该使用正确的格式,例如 lz4gz;如果是用不正确的压缩格式,刷入 boot 后可能无法开机。

INFO

  1. 您可以通过 magiskboot 来获取你原来 boot 的压缩格式;当然您也可以询问与您机型相同的其他更有经验的童鞋。另外,内核的压缩格式通常不会发生变化,如果您使用某个压缩格式成功开机,后续可优先尝试这个格式。
  2. 小米设备通常使用 gz 或者 不压缩
  3. Pixel 设备有些特殊,请查看下面的教程。

将 boot.img 刷入设备

使用 adb 连接您的设备,然后执行 adb reboot bootloader 进入 fastboot 模式,然后使用此命令刷入 KernelSU:

sh
fastboot flash boot boot.img
fastboot flash boot boot.img

INFO

如果你的设备支持 fastboot boot,可以先使用 fastboot boot boot.img 来先尝试使用 boot.img 引导系统,如果出现意外,再重启一次即可开机。

重启

刷入完成后,您应该重新启动您的设备:

sh
fastboot reboot
fastboot reboot

手动修补 boot.img

对于某些设备来说,其 boot.img 格式不那么常见,比如不是 lz4, gz 和未压缩;最典型的就是 Pixel,它 boot.img 的格式是 lz4_legacy 压缩,ramdisk 可能是 gz 也可能是 lz4_legacy 压缩;此时如果你直接刷入 KernelSU 提供的 boot.img,手机可能无法开机;这时候,你可以通过手动修补 boot.img 来实现。

修补方法总体有两种:

  1. Android-Image-Kitchen
  2. magiskboot

其中,Android-Image-Kitchen 适用于 PC 上操作,magiskboot 需要手机配合。

准备

  1. 获取你手机的原厂 boot.img;你可以通过你手机的线刷包解压后之间获取,如果你是卡刷包,那你也许需要payload-dumper-go
  2. 下载 KernelSU 提供的与你设备 KMI 版本一致的 AnyKernel3 刷机包(可以参考 自定义 TWRP 刷入一节)。
  3. 解压缩 AnyKernel3 刷机包,获取其中的 Image 文件,此文件为 KernelSU 的内核文件。

使用 Android-Image-Kitchen

  1. 下载 Android-Image-Kitchen 至你电脑
  2. 将手机原厂 boot.img 放入 Android-Image-Kitchen 根目录
  3. 在 Android-Image-Kitchen 根目录执行 ./unpackimg.sh boot.img;此命名会将 boot.img 拆开,你会得到若干文件。
  4. split_img 目录中的 boot.img-kernel 替换为你从 AnyKernel3 解压出来的 Image(注意名字改为 boot.img-kernel)。
  5. 在 Android-Image-Kitchecn 根目录执行 ./repackimg.sh;此时你会得到一个 image-new.img 的文件;使用此 boot.img 通过 fastboot 刷入即可(刷入方法参考上一节)。
`,58),n=e("h3",{id:"using",magiskboot:"",tabindex:"-1"},[i("使用 magiskboot "),e("a",{class:"header-anchor",href:"#using","aria-label":'Permalink to "使用 magiskboot {#using magiskboot}"'},"​")],-1),c=o('
  1. 在 Magisk 的 Release 页面 下载最新的 Magisk 安装包。
  2. 将 Magisk-*.apk 重命名为 Magisk-vesion.zip 然后解压缩。
  3. 将解压后的 Magisk-v25.2/lib/arm64-v8a/libmagiskboot.so 文件,使用 adb push 到手机:adb push Magisk-v25.2/lib/arm64-v8a/libmagiskboot.so /data/local/tmp/magiskboot
  4. 使用 adb 将原厂 boot.img 和 AnyKernel3 中的 Image 推送到手机
  5. adb shell 进入 /data/local/tmp/ 目录,然后赋予刚 push 文件的可执行权限 chmod +x magiskboot
  6. adb shell 进入 /data/local/tmp/ 目录,执行 ./magiskboot unpack boot.img 此时会解包 boot.img 得到一个叫做 kernel 的文件,这个文件为你原厂的 kernel
  7. 使用 Image 替换 kernel: mv -f Image kernel
  8. 执行 ./magiskboot repack boot.img 打包 img,此时你会得到一个 new-boot.img 的文件,使用这个文件 fastboot 刷入设备即可。

其他变通方法

其实所有这些安装方法的主旨只有一个,那就是替换原厂的内核为 KernelSU 提供的内核;只要能实现这个目的,就可以安装;比如以下是其他可行的方法:

  1. 首先安装 Magisk,通过 Magisk 获取 root 权限后使用内核刷写器刷入 KernelSU 的 AnyKernel 包。
  2. 使用某些 PC 上的刷机工具箱刷入 KernelSU 提供的内核。
',4),d=[s,n,c];function p(h,b,g,m,k,u){return l(),t("div",null,d)}const K=a(r,[["render",p]]);export{y as __pageData,K as default}; diff --git a/assets/zh_CN_guide_installation.md.b6f3f2dc.lean.js b/assets/zh_CN_guide_installation.md.b6f3f2dc.lean.js new file mode 100644 index 000000000000..d0629f6e3335 --- /dev/null +++ b/assets/zh_CN_guide_installation.md.b6f3f2dc.lean.js @@ -0,0 +1 @@ +import{_ as a,o as l,c as t,Q as o,k as e,a as i}from"./chunks/framework.ec8f7e8e.js";const y=JSON.parse('{"title":"安装","description":"","frontmatter":{},"headers":[],"relativePath":"zh_CN/guide/installation.md","filePath":"zh_CN/guide/installation.md"}'),r={name:"zh_CN/guide/installation.md"},s=o("",58),n=e("h3",{id:"using",magiskboot:"",tabindex:"-1"},[i("使用 magiskboot "),e("a",{class:"header-anchor",href:"#using","aria-label":'Permalink to "使用 magiskboot {#using magiskboot}"'},"​")],-1),c=o("",4),d=[s,n,c];function p(h,b,g,m,k,u){return l(),t("div",null,d)}const K=a(r,[["render",p]]);export{y as __pageData,K as default}; diff --git a/assets/zh_CN_guide_module.md.88839cd5.lean.js b/assets/zh_CN_guide_module.md.88839cd5.lean.js deleted file mode 100644 index 381b6db0331c..000000000000 --- a/assets/zh_CN_guide_module.md.88839cd5.lean.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as s,o as e,c as a,O as l}from"./chunks/framework.43781440.js";const C=JSON.parse('{"title":"模块开发指南","description":"","frontmatter":{},"headers":[],"relativePath":"zh_CN/guide/module.md","filePath":"zh_CN/guide/module.md"}'),n={name:"zh_CN/guide/module.md"},o=l("",58),t=[o];function p(c,i,r,d,u,A){return e(),a("div",null,t)}const m=s(n,[["render",p]]);export{C as __pageData,m as default}; diff --git a/assets/zh_CN_guide_module.md.88839cd5.js b/assets/zh_CN_guide_module.md.95d10721.js similarity index 50% rename from assets/zh_CN_guide_module.md.88839cd5.js rename to assets/zh_CN_guide_module.md.95d10721.js index d6b910b3f99a..1afacb560f6c 100644 --- a/assets/zh_CN_guide_module.md.88839cd5.js +++ b/assets/zh_CN_guide_module.md.95d10721.js @@ -1,86 +1,171 @@ -import{_ as s,o as e,c as a,O as l}from"./chunks/framework.43781440.js";const C=JSON.parse('{"title":"模块开发指南","description":"","frontmatter":{},"headers":[],"relativePath":"zh_CN/guide/module.md","filePath":"zh_CN/guide/module.md"}'),n={name:"zh_CN/guide/module.md"},o=l(`

模块开发指南

KernelSU 提供了一个模块机制,它可以在保持系统分区完整性的同时达到修改系统分区的效果;这种机制通常被称之为 systemless。

KernelSU 的模块运作机制与 Magisk 几乎是一样的,如果你熟悉 Magisk 模块的开发,那么开发 KernelSU 的模块大同小异,你可以跳过下面有关模块的介绍,只需要了解 KernelSU 模块与 Magisk 模块的异同

Busybox

KernelSU 提供了一个功能完备的 BusyBox 二进制文件(包括完整的SELinux支持)。可执行文件位于 /data/adb/ksu/bin/busybox。 KernelSU 的 BusyBox 支持运行时可切换的 "ASH Standalone Shell Mode"。 这种独立模式意味着在运行 BusyBox 的 ash shell 时,每个命令都会直接使用 BusyBox 中内置的应用程序,而不管 PATH 设置为什么。 例如,lsrmchmod 等命令将不会使用 PATH 中设置的命令(在Android的情况下,默认情况下分别为 /system/bin/ls/system/bin/rm/system/bin/chmod),而是直接调用 BusyBox 内置的应用程序。 这确保了脚本始终在可预测的环境中运行,并始终具有完整的命令套件,无论它运行在哪个Android版本上。 要强制一个命令不使用BusyBox,你必须使用完整路径调用可执行文件。

在 KernelSU 上下文中运行的每个 shell 脚本都将在 BusyBox 的 ash shell 中以独立模式运行。对于第三方开发者相关的内容,包括所有启动脚本和模块安装脚本。

对于想要在 KernelSU 之外使用这个“独立模式”功能的用户,有两种启用方法:

  1. 设置环境变量 ASH_STANDALONE1。例如:ASH_STANDALONE=1 /data/adb/ksu/bin/busybox sh <script>
  2. 使用命令行选项切换:/data/adb/ksu/bin/busybox sh -o standalone <script>

为了确保所有后续的 sh shell 都在独立模式下执行,第一种是首选方法(这也是 KernelSU 和 KernelSU 管理器内部使用的方法),因为环境变量会被继承到子进程中。

与 Magisk 的差异

KernelSU 的 BusyBox 现在是直接使用 Magisk 项目编译的二进制文件,感谢 Magisk! 因此,你完全不用担心 BusyBox 脚本与在 Magisk 和 KernelSU 之间的兼容问题,因为他们是完全一样的!

KernelSU 模块

KernelSU 模块就是一个放置在 /data/adb/modules 内且满足如下结构的文件夹:

txt
/data/adb/modules
-├── .
-├── .
-|
-├── $MODID                  <--- 模块的文件夹名称与模块 ID 相同
-│   │
-│   │      *** 模块配置文件 ***
-│   │
-│   ├── module.prop         <--- 此文件保存模块相关的一些配置,如模块 ID、版本等
-│   │
-│   │      *** 模块内容 ***
-│   │
-│   ├── system              <--- 这个文件夹通常会被挂载到系统
-│   │   ├── ...
-│   │   ├── ...
-│   │   └── ...
-│   │
-│   │      *** 标记文件 ***
-│   │
-│   ├── skip_mount          <--- 如果这个文件存在,那么模块的 \`/system\` 将不会被挂载
-│   ├── disable             <--- 如果这个文件存在,那么模块会被禁用
-│   ├── remove              <--- 如果这个文件存在,下次重启的时候模块会被移除
-│   │
-│   │      *** 可选文件 ***
-│   │
-│   ├── post-fs-data.sh     <--- 这个脚本将会在 post-fs-data 模式下运行
-│   ├── post-mount.sh       <--- 这个脚本将会在 post-mount 模式下运行
-│   ├── service.sh          <--- 这个脚本将会在 late_start 服务模式下运行
-│   ├── boot-completed.sh   <--- 这个脚本将会在 Android 系统启动完毕后以服务模式运行
-|   ├── uninstall.sh        <--- 这个脚本将会在模块被卸载时运行
-│   ├── system.prop         <--- 这个文件中指定的属性将会在系统启动时通过 resetprop 更改
-│   ├── sepolicy.rule       <--- 这个文件中的 SELinux 策略将会在系统启动时加载
-│   │
-│   │      *** 自动生成的目录,不要手动创建或者修改! ***
-│   │
-│   ├── vendor              <--- A symlink to $MODID/system/vendor
-│   ├── product             <--- A symlink to $MODID/system/product
-│   ├── system_ext          <--- A symlink to $MODID/system/system_ext
-│   │
-│   │      *** Any additional files / folders are allowed ***
-│   │
-│   ├── ...
-│   └── ...
-|
-├── another_module
-│   ├── .
-│   └── .
-├── .
-├── .

与 Magisk 的差异

KernelSU 没有内置的针对 Zygisk 的支持,因此模块中没有 Zygisk 相关的内容,但你可以通过 ZygiskNext 来支持 Zygisk 模块,此时 Zygisk 模块的内容与 Magisk 所支持的 Zygisk 是完全相同的。

module.prop

module.prop 是一个模块的配置文件,在 KernelSU 中如果模块中不包含此文件,那么它将不被认为是一个模块;此文件的格式如下:

txt
id=<string>
-name=<string>
-version=<string>
-versionCode=<int>
-author=<string>
-description=<string>
  • id 必须与这个正则表达式匹配:^[a-zA-Z][a-zA-Z0-9._-]+$ 例如:✓ a_module,✓ a.module,✓ module-101,✗ a module,✗ 1_module,✗ -a-module。这是您的模块的唯一标识符,发布后不应更改。
  • versionCode 必须是一个整数,用于比较版本。
  • 其他未在上面提到的内容可以是任何单行字符串。
  • 请确保使用 UNIX(LF)换行类型,而不是Windows(CR + LF)或 Macintosh(CR)。

Shell 脚本

请阅读 启动脚本 一节,以了解 post-fs-data.sh, post-mount.sh, service.shboot-completed.sh 之间的区别。对于大多数模块开发者来说,如果您只需要运行一个启动脚本,service.sh 应该已经足够了。

在您的模块的所有脚本中,请使用 MODDIR=\${0%/*}来获取您的模块的基本目录路径;请勿在脚本中硬编码您的模块路径。

与 Magisk 的差异

你可以通过环境变量 KSU 来判断脚本是运行在 KernelSU 还是 Magisk 中,如果运行在 KernelSU,这个值会被设置为 true

system 目录

这个目录的内容会在系统启动后,以 overlayfs 的方式叠加在系统的 /system 分区之上,这意味着:

  1. 系统中对应目录的同名文件会被此目录的文件覆盖。
  2. 系统中对应目录的同名文件夹会与此目录的文件夹合并。

如果你想删掉系统原来目录某个文件或者文件夹,你需要在模块目录通过 mknod filename c 0 0 来创建一个 filename 的同名文件;这样 overlayfs 系统会自动 whiteout 等效删除此文件(/system 分区并没有被更改)。

你也可以在 customize.sh 中声明一个名为 REMOVE 并且包含一系列目录的变量来执行删除操作,KernelSU 会自动为你在模块对应目录执行 mknod <TARGET> c 0 0。例如:

sh
REMOVE="
-/system/app/YouTube
-/system/app/Bloatware
-"

上面的这个列表将会执行: mknod $MODPATH/system/app/YouTuBe c 0 0mknod $MODPATH/system/app/Bloatware c 0 0;并且 /system/app/YouTube/system/app/Bloatware 将会在模块生效后被删除。

如果你想替换掉系统的某个目录,你需要在模块目录创建一个相同路径的目录,然后为此目录设置此属性:setfattr -n trusted.overlay.opaque -v y <TARGET>;这样 overlayfs 系统会自动将系统内相应目录替换(/system 分区并没有被更改)。

你可以在 customize.sh 中声明一个名为 REPLACE 并且包含一系列目录的变量来执行替换操作,KernelSU 会自动为你在模块对应目录执行相关操作。例如:

sh
REPLACE="
-/system/app/YouTube
-/system/app/Bloatware
-"

上面这个列表将会:自动创建目录 $MODPATH/system/app/YouTube$MODPATH//system/app/Bloatware,然后执行 setfattr -n trusted.overlay.opaque -v y $$MODPATH/system/app/YouTubesetfattr -n trusted.overlay.opaque -v y $$MODPATH/system/app/Bloatware;并且 /system/app/YouTube/system/app/Bloatware 将会在模块生效后替换为空目录。

与 Magisk 的差异

KernelSU 的 systemless 机制是通过内核的 overlayfs 实现的,而 Magisk 当前则是通过 magic mount (bind mount),二者实现方式有着巨大的差异,但最终的目标实际上是一致的:不修改物理的 /system 分区但实现修改 /system 文件。

如果你对 overlayfs 感兴趣,建议阅读 Linux Kernel 关于 overlayfs 的文档

system.prop

这个文件的格式与 build.prop 完全相同:每一行都是 [key]=[value] 的形式。

sepolicy.rule

如果您的模块需要一些额外的 SELinux 策略补丁,请将这些规则添加到此文件中。这个文件中的每一行都将被视为一个策略语句。

模块安装包

KernelSU 的模块安装包就是一个可以通过 KernelSU 管理器 APP 刷入的 zip 文件,此 zip 文件的格式如下:

txt
module.zip
-
-├── customize.sh                       <--- (Optional, more details later)
-│                                           This script will be sourced by update-binary
-├── ...
-├── ...  /* 其他模块文件 */
-

WARNING

KernelSU 模块不支持在 Recovery 中安装!!

定制安装过程

如果你想控制模块的安装过程,可以在模块的目录下创建一个名为 customize.sh 的文件,这个脚本将会在模块被解压后导入到当前 shell 中,如果你的模块需要根据设备的 API 版本或者设备构架做一些额外的操作,那这个脚本将非常有用。

如果你想完全控制脚本的安装过程,你可以在 customize.sh 中声明 SKIPUNZIP=1 来跳过所有的默认安装步骤;此时,你需要自行处理所有安装过程(如解压模块,设置权限等)

customize.sh 脚本以“独立模式”运行在 KernelSU 的 BusyBox ash shell 中。你可以使用如下变量和函数:

变量

  • KSU (bool): 标记此脚本运行在 KernelSU 环境下,此变量的值将永远为 true,你可以通过它区分 Magisk。
  • KSU_VER (string): KernelSU 当前的版本名字 (如: v0.4.0)
  • KSU_VER_CODE (int): KernelSU 用户空间当前的版本号 (如. 10672)
  • KSU_KERNEL_VER_CODE (int): KernelSU 内核空间当前的版本号 (如. 10672)
  • BOOTMODE (bool): 此变量在 KernelSU 中永远为 true
  • MODPATH (path): 当前模块的安装目录
  • TMPDIR (path): 可以存放临时文件的目录
  • ZIPFILE (path): 当前模块的安装包文件
  • ARCH (string): 设备的 CPU 构架,有如下几种: arm, arm64, x86, or x64
  • IS64BIT (bool): 是否是 64 位设备
  • API (int): 当前设备的 Android API 版本 (如:Android 6.0 上为 23)

WARNING

MAGISK_VER_CODE 在 KernelSU 中永远为 25200MAGISK_VER 则为 v25.2,请不要通过这两个变量来判断是否是 KernelSU!

函数

txt
ui_print <msg>
-    print <msg> to console
-    Avoid using 'echo' as it will not display in custom recovery's console
-
-abort <msg>
-    print error message <msg> to console and terminate the installation
-    Avoid using 'exit' as it will skip the termination cleanup steps
-
-set_perm <target> <owner> <group> <permission> [context]
-    if [context] is not set, the default is "u:object_r:system_file:s0"
-    this function is a shorthand for the following commands:
-       chown owner.group target
-       chmod permission target
-       chcon context target
-
-set_perm_recursive <directory> <owner> <group> <dirpermission> <filepermission> [context]
-    if [context] is not set, the default is "u:object_r:system_file:s0"
-    for all files in <directory>, it will call:
-       set_perm file owner group filepermission context
-    for all directories in <directory> (including itself), it will call:
-       set_perm dir owner group dirpermission context

启动脚本

在 KernelSU 中,根据脚本运行模式的不同分为两种:post-fs-data 模式和 late_start 服务模式。

  • post-fs-data 模式

    • 这个阶段是阻塞的。在执行完成之前或者 10 秒钟之后,启动过程会暂停。
    • 脚本在任何模块被挂载之前运行。这使得模块开发者可以在模块被挂载之前动态地调整它们的模块。
    • 这个阶段发生在 Zygote 启动之前。
    • 使用 setprop 会导致启动过程死锁!请使用 resetprop -n <prop_name> <prop_value> 代替。
    • 只有在必要时才在此模式下运行脚本
  • late_start 服务模式

    • 这个阶段是非阻塞的。你的脚本会与其余的启动过程并行运行。
    • 大多数脚本都建议在这种模式下运行

在 KernelSU 中,启动脚本根据存放位置的不同还分为两种:通用脚本和模块脚本。

  • 通用脚本

    • 放置在 /data/adb/post-fs-data.d, /data/adb/post-mount.d, /data/adb/service.d/data/adb/boot-completed.d 中。
    • 只有在脚本被设置为可执行(chmod +x script.sh)时才会被执行。
    • post-fs-data.d 中的脚本以 post-fs-data 模式运行,在 service.d 中的脚本以 late_start 服务模式运行。
    • 模块不应在安装过程中添加通用脚本。
  • 模块脚本

    • 放置在模块自己的文件夹中。
    • 只有当模块被启用时才会执行。
    • post-fs-data.sh 以 post-fs-data 模式运行,post-mount.sh 以 post-mount 模式运行,而 service.sh 则以 late_start 服务模式运行,boot-completed 在 Android 系统启动完毕后以服务模式运行。

所有启动脚本都将在 KernelSU 的 BusyBox ash shell 中运行,并启用“独立模式”。

`,58),t=[o];function p(c,i,r,d,u,A){return e(),a("div",null,t)}const m=s(n,[["render",p]]);export{C as __pageData,m as default}; +import{_ as s,o as e,c as n,Q as a}from"./chunks/framework.ec8f7e8e.js";const h=JSON.parse('{"title":"模块开发指南","description":"","frontmatter":{},"headers":[],"relativePath":"zh_CN/guide/module.md","filePath":"zh_CN/guide/module.md"}'),l={name:"zh_CN/guide/module.md"},o=a(`

模块开发指南

KernelSU 提供了一个模块机制,它可以在保持系统分区完整性的同时达到修改系统分区的效果;这种机制通常被称之为 systemless。

KernelSU 的模块运作机制与 Magisk 几乎是一样的,如果你熟悉 Magisk 模块的开发,那么开发 KernelSU 的模块大同小异,你可以跳过下面有关模块的介绍,只需要了解 KernelSU 模块与 Magisk 模块的异同

Busybox

KernelSU 提供了一个功能完备的 BusyBox 二进制文件(包括完整的SELinux支持)。可执行文件位于 /data/adb/ksu/bin/busybox。 KernelSU 的 BusyBox 支持运行时可切换的 "ASH Standalone Shell Mode"。 这种独立模式意味着在运行 BusyBox 的 ash shell 时,每个命令都会直接使用 BusyBox 中内置的应用程序,而不管 PATH 设置为什么。 例如,lsrmchmod 等命令将不会使用 PATH 中设置的命令(在Android的情况下,默认情况下分别为 /system/bin/ls/system/bin/rm/system/bin/chmod),而是直接调用 BusyBox 内置的应用程序。 这确保了脚本始终在可预测的环境中运行,并始终具有完整的命令套件,无论它运行在哪个Android版本上。 要强制一个命令不使用BusyBox,你必须使用完整路径调用可执行文件。

在 KernelSU 上下文中运行的每个 shell 脚本都将在 BusyBox 的 ash shell 中以独立模式运行。对于第三方开发者相关的内容,包括所有启动脚本和模块安装脚本。

对于想要在 KernelSU 之外使用这个“独立模式”功能的用户,有两种启用方法:

  1. 设置环境变量 ASH_STANDALONE1。例如:ASH_STANDALONE=1 /data/adb/ksu/bin/busybox sh <script>
  2. 使用命令行选项切换:/data/adb/ksu/bin/busybox sh -o standalone <script>

为了确保所有后续的 sh shell 都在独立模式下执行,第一种是首选方法(这也是 KernelSU 和 KernelSU 管理器内部使用的方法),因为环境变量会被继承到子进程中。

与 Magisk 的差异

KernelSU 的 BusyBox 现在是直接使用 Magisk 项目编译的二进制文件,感谢 Magisk! 因此,你完全不用担心 BusyBox 脚本与在 Magisk 和 KernelSU 之间的兼容问题,因为他们是完全一样的!

KernelSU 模块

KernelSU 模块就是一个放置在 /data/adb/modules 内且满足如下结构的文件夹:

txt
/data/adb/modules
+├── .
+├── .
+|
+├── $MODID                  <--- 模块的文件夹名称与模块 ID 相同
+│   │
+│   │      *** 模块配置文件 ***
+│   │
+│   ├── module.prop         <--- 此文件保存模块相关的一些配置,如模块 ID、版本等
+│   │
+│   │      *** 模块内容 ***
+│   │
+│   ├── system              <--- 这个文件夹通常会被挂载到系统
+│   │   ├── ...
+│   │   ├── ...
+│   │   └── ...
+│   │
+│   │      *** 标记文件 ***
+│   │
+│   ├── skip_mount          <--- 如果这个文件存在,那么模块的 \`/system\` 将不会被挂载
+│   ├── disable             <--- 如果这个文件存在,那么模块会被禁用
+│   ├── remove              <--- 如果这个文件存在,下次重启的时候模块会被移除
+│   │
+│   │      *** 可选文件 ***
+│   │
+│   ├── post-fs-data.sh     <--- 这个脚本将会在 post-fs-data 模式下运行
+│   ├── post-mount.sh       <--- 这个脚本将会在 post-mount 模式下运行
+│   ├── service.sh          <--- 这个脚本将会在 late_start 服务模式下运行
+│   ├── boot-completed.sh   <--- 这个脚本将会在 Android 系统启动完毕后以服务模式运行
+|   ├── uninstall.sh        <--- 这个脚本将会在模块被卸载时运行
+│   ├── system.prop         <--- 这个文件中指定的属性将会在系统启动时通过 resetprop 更改
+│   ├── sepolicy.rule       <--- 这个文件中的 SELinux 策略将会在系统启动时加载
+│   │
+│   │      *** 自动生成的目录,不要手动创建或者修改! ***
+│   │
+│   ├── vendor              <--- A symlink to $MODID/system/vendor
+│   ├── product             <--- A symlink to $MODID/system/product
+│   ├── system_ext          <--- A symlink to $MODID/system/system_ext
+│   │
+│   │      *** Any additional files / folders are allowed ***
+│   │
+│   ├── ...
+│   └── ...
+|
+├── another_module
+│   ├── .
+│   └── .
+├── .
+├── .
/data/adb/modules
+├── .
+├── .
+|
+├── $MODID                  <--- 模块的文件夹名称与模块 ID 相同
+│   │
+│   │      *** 模块配置文件 ***
+│   │
+│   ├── module.prop         <--- 此文件保存模块相关的一些配置,如模块 ID、版本等
+│   │
+│   │      *** 模块内容 ***
+│   │
+│   ├── system              <--- 这个文件夹通常会被挂载到系统
+│   │   ├── ...
+│   │   ├── ...
+│   │   └── ...
+│   │
+│   │      *** 标记文件 ***
+│   │
+│   ├── skip_mount          <--- 如果这个文件存在,那么模块的 \`/system\` 将不会被挂载
+│   ├── disable             <--- 如果这个文件存在,那么模块会被禁用
+│   ├── remove              <--- 如果这个文件存在,下次重启的时候模块会被移除
+│   │
+│   │      *** 可选文件 ***
+│   │
+│   ├── post-fs-data.sh     <--- 这个脚本将会在 post-fs-data 模式下运行
+│   ├── post-mount.sh       <--- 这个脚本将会在 post-mount 模式下运行
+│   ├── service.sh          <--- 这个脚本将会在 late_start 服务模式下运行
+│   ├── boot-completed.sh   <--- 这个脚本将会在 Android 系统启动完毕后以服务模式运行
+|   ├── uninstall.sh        <--- 这个脚本将会在模块被卸载时运行
+│   ├── system.prop         <--- 这个文件中指定的属性将会在系统启动时通过 resetprop 更改
+│   ├── sepolicy.rule       <--- 这个文件中的 SELinux 策略将会在系统启动时加载
+│   │
+│   │      *** 自动生成的目录,不要手动创建或者修改! ***
+│   │
+│   ├── vendor              <--- A symlink to $MODID/system/vendor
+│   ├── product             <--- A symlink to $MODID/system/product
+│   ├── system_ext          <--- A symlink to $MODID/system/system_ext
+│   │
+│   │      *** Any additional files / folders are allowed ***
+│   │
+│   ├── ...
+│   └── ...
+|
+├── another_module
+│   ├── .
+│   └── .
+├── .
+├── .

与 Magisk 的差异

KernelSU 没有内置的针对 Zygisk 的支持,因此模块中没有 Zygisk 相关的内容,但你可以通过 ZygiskNext 来支持 Zygisk 模块,此时 Zygisk 模块的内容与 Magisk 所支持的 Zygisk 是完全相同的。

module.prop

module.prop 是一个模块的配置文件,在 KernelSU 中如果模块中不包含此文件,那么它将不被认为是一个模块;此文件的格式如下:

txt
id=<string>
+name=<string>
+version=<string>
+versionCode=<int>
+author=<string>
+description=<string>
id=<string>
+name=<string>
+version=<string>
+versionCode=<int>
+author=<string>
+description=<string>
  • id 必须与这个正则表达式匹配:^[a-zA-Z][a-zA-Z0-9._-]+$ 例如:✓ a_module,✓ a.module,✓ module-101,✗ a module,✗ 1_module,✗ -a-module。这是您的模块的唯一标识符,发布后不应更改。
  • versionCode 必须是一个整数,用于比较版本。
  • 其他未在上面提到的内容可以是任何单行字符串。
  • 请确保使用 UNIX(LF)换行类型,而不是Windows(CR + LF)或 Macintosh(CR)。

Shell 脚本

请阅读 启动脚本 一节,以了解 post-fs-data.sh, post-mount.sh, service.shboot-completed.sh 之间的区别。对于大多数模块开发者来说,如果您只需要运行一个启动脚本,service.sh 应该已经足够了。

在您的模块的所有脚本中,请使用 MODDIR=\${0%/*}来获取您的模块的基本目录路径;请勿在脚本中硬编码您的模块路径。

与 Magisk 的差异

你可以通过环境变量 KSU 来判断脚本是运行在 KernelSU 还是 Magisk 中,如果运行在 KernelSU,这个值会被设置为 true

system 目录

这个目录的内容会在系统启动后,以 overlayfs 的方式叠加在系统的 /system 分区之上,这意味着:

  1. 系统中对应目录的同名文件会被此目录的文件覆盖。
  2. 系统中对应目录的同名文件夹会与此目录的文件夹合并。

如果你想删掉系统原来目录某个文件或者文件夹,你需要在模块目录通过 mknod filename c 0 0 来创建一个 filename 的同名文件;这样 overlayfs 系统会自动 whiteout 等效删除此文件(/system 分区并没有被更改)。

你也可以在 customize.sh 中声明一个名为 REMOVE 并且包含一系列目录的变量来执行删除操作,KernelSU 会自动为你在模块对应目录执行 mknod <TARGET> c 0 0。例如:

sh
REMOVE="
+/system/app/YouTube
+/system/app/Bloatware
+"
REMOVE="
+/system/app/YouTube
+/system/app/Bloatware
+"

上面的这个列表将会执行: mknod $MODPATH/system/app/YouTuBe c 0 0mknod $MODPATH/system/app/Bloatware c 0 0;并且 /system/app/YouTube/system/app/Bloatware 将会在模块生效后被删除。

如果你想替换掉系统的某个目录,你需要在模块目录创建一个相同路径的目录,然后为此目录设置此属性:setfattr -n trusted.overlay.opaque -v y <TARGET>;这样 overlayfs 系统会自动将系统内相应目录替换(/system 分区并没有被更改)。

你可以在 customize.sh 中声明一个名为 REPLACE 并且包含一系列目录的变量来执行替换操作,KernelSU 会自动为你在模块对应目录执行相关操作。例如:

sh
REPLACE="
+/system/app/YouTube
+/system/app/Bloatware
+"
REPLACE="
+/system/app/YouTube
+/system/app/Bloatware
+"

上面这个列表将会:自动创建目录 $MODPATH/system/app/YouTube$MODPATH//system/app/Bloatware,然后执行 setfattr -n trusted.overlay.opaque -v y $$MODPATH/system/app/YouTubesetfattr -n trusted.overlay.opaque -v y $$MODPATH/system/app/Bloatware;并且 /system/app/YouTube/system/app/Bloatware 将会在模块生效后替换为空目录。

与 Magisk 的差异

KernelSU 的 systemless 机制是通过内核的 overlayfs 实现的,而 Magisk 当前则是通过 magic mount (bind mount),二者实现方式有着巨大的差异,但最终的目标实际上是一致的:不修改物理的 /system 分区但实现修改 /system 文件。

如果你对 overlayfs 感兴趣,建议阅读 Linux Kernel 关于 overlayfs 的文档

system.prop

这个文件的格式与 build.prop 完全相同:每一行都是 [key]=[value] 的形式。

sepolicy.rule

如果您的模块需要一些额外的 SELinux 策略补丁,请将这些规则添加到此文件中。这个文件中的每一行都将被视为一个策略语句。

模块安装包

KernelSU 的模块安装包就是一个可以通过 KernelSU 管理器 APP 刷入的 zip 文件,此 zip 文件的格式如下:

txt
module.zip
+
+├── customize.sh                       <--- (Optional, more details later)
+│                                           This script will be sourced by update-binary
+├── ...
+├── ...  /* 其他模块文件 */
+
module.zip
+
+├── customize.sh                       <--- (Optional, more details later)
+│                                           This script will be sourced by update-binary
+├── ...
+├── ...  /* 其他模块文件 */
+

WARNING

KernelSU 模块不支持在 Recovery 中安装!!

定制安装过程

如果你想控制模块的安装过程,可以在模块的目录下创建一个名为 customize.sh 的文件,这个脚本将会在模块被解压后导入到当前 shell 中,如果你的模块需要根据设备的 API 版本或者设备构架做一些额外的操作,那这个脚本将非常有用。

如果你想完全控制脚本的安装过程,你可以在 customize.sh 中声明 SKIPUNZIP=1 来跳过所有的默认安装步骤;此时,你需要自行处理所有安装过程(如解压模块,设置权限等)

customize.sh 脚本以“独立模式”运行在 KernelSU 的 BusyBox ash shell 中。你可以使用如下变量和函数:

变量

  • KSU (bool): 标记此脚本运行在 KernelSU 环境下,此变量的值将永远为 true,你可以通过它区分 Magisk。
  • KSU_VER (string): KernelSU 当前的版本名字 (如: v0.4.0)
  • KSU_VER_CODE (int): KernelSU 用户空间当前的版本号 (如. 10672)
  • KSU_KERNEL_VER_CODE (int): KernelSU 内核空间当前的版本号 (如. 10672)
  • BOOTMODE (bool): 此变量在 KernelSU 中永远为 true
  • MODPATH (path): 当前模块的安装目录
  • TMPDIR (path): 可以存放临时文件的目录
  • ZIPFILE (path): 当前模块的安装包文件
  • ARCH (string): 设备的 CPU 构架,有如下几种: arm, arm64, x86, or x64
  • IS64BIT (bool): 是否是 64 位设备
  • API (int): 当前设备的 Android API 版本 (如:Android 6.0 上为 23)

WARNING

MAGISK_VER_CODE 在 KernelSU 中永远为 25200MAGISK_VER 则为 v25.2,请不要通过这两个变量来判断是否是 KernelSU!

函数

txt
ui_print <msg>
+    print <msg> to console
+    Avoid using 'echo' as it will not display in custom recovery's console
+
+abort <msg>
+    print error message <msg> to console and terminate the installation
+    Avoid using 'exit' as it will skip the termination cleanup steps
+
+set_perm <target> <owner> <group> <permission> [context]
+    if [context] is not set, the default is "u:object_r:system_file:s0"
+    this function is a shorthand for the following commands:
+       chown owner.group target
+       chmod permission target
+       chcon context target
+
+set_perm_recursive <directory> <owner> <group> <dirpermission> <filepermission> [context]
+    if [context] is not set, the default is "u:object_r:system_file:s0"
+    for all files in <directory>, it will call:
+       set_perm file owner group filepermission context
+    for all directories in <directory> (including itself), it will call:
+       set_perm dir owner group dirpermission context
ui_print <msg>
+    print <msg> to console
+    Avoid using 'echo' as it will not display in custom recovery's console
+
+abort <msg>
+    print error message <msg> to console and terminate the installation
+    Avoid using 'exit' as it will skip the termination cleanup steps
+
+set_perm <target> <owner> <group> <permission> [context]
+    if [context] is not set, the default is "u:object_r:system_file:s0"
+    this function is a shorthand for the following commands:
+       chown owner.group target
+       chmod permission target
+       chcon context target
+
+set_perm_recursive <directory> <owner> <group> <dirpermission> <filepermission> [context]
+    if [context] is not set, the default is "u:object_r:system_file:s0"
+    for all files in <directory>, it will call:
+       set_perm file owner group filepermission context
+    for all directories in <directory> (including itself), it will call:
+       set_perm dir owner group dirpermission context

启动脚本

在 KernelSU 中,根据脚本运行模式的不同分为两种:post-fs-data 模式和 late_start 服务模式。

  • post-fs-data 模式

    • 这个阶段是阻塞的。在执行完成之前或者 10 秒钟之后,启动过程会暂停。
    • 脚本在任何模块被挂载之前运行。这使得模块开发者可以在模块被挂载之前动态地调整它们的模块。
    • 这个阶段发生在 Zygote 启动之前。
    • 使用 setprop 会导致启动过程死锁!请使用 resetprop -n <prop_name> <prop_value> 代替。
    • 只有在必要时才在此模式下运行脚本
  • late_start 服务模式

    • 这个阶段是非阻塞的。你的脚本会与其余的启动过程并行运行。
    • 大多数脚本都建议在这种模式下运行

在 KernelSU 中,启动脚本根据存放位置的不同还分为两种:通用脚本和模块脚本。

  • 通用脚本

    • 放置在 /data/adb/post-fs-data.d, /data/adb/post-mount.d, /data/adb/service.d/data/adb/boot-completed.d 中。
    • 只有在脚本被设置为可执行(chmod +x script.sh)时才会被执行。
    • post-fs-data.d 中的脚本以 post-fs-data 模式运行,在 service.d 中的脚本以 late_start 服务模式运行。
    • 模块不应在安装过程中添加通用脚本。
  • 模块脚本

    • 放置在模块自己的文件夹中。
    • 只有当模块被启用时才会执行。
    • post-fs-data.sh 以 post-fs-data 模式运行,post-mount.sh 以 post-mount 模式运行,而 service.sh 则以 late_start 服务模式运行,boot-completed 在 Android 系统启动完毕后以服务模式运行。

所有启动脚本都将在 KernelSU 的 BusyBox ash shell 中运行,并启用“独立模式”。

`,58),p=[o];function t(c,i,r,d,y,u){return e(),n("div",null,p)}const g=s(l,[["render",t]]);export{h as __pageData,g as default}; diff --git a/assets/zh_CN_guide_module.md.95d10721.lean.js b/assets/zh_CN_guide_module.md.95d10721.lean.js new file mode 100644 index 000000000000..ab09071a8231 --- /dev/null +++ b/assets/zh_CN_guide_module.md.95d10721.lean.js @@ -0,0 +1 @@ +import{_ as s,o as e,c as n,Q as a}from"./chunks/framework.ec8f7e8e.js";const h=JSON.parse('{"title":"模块开发指南","description":"","frontmatter":{},"headers":[],"relativePath":"zh_CN/guide/module.md","filePath":"zh_CN/guide/module.md"}'),l={name:"zh_CN/guide/module.md"},o=a("",58),p=[o];function t(c,i,r,d,y,u){return e(),n("div",null,p)}const g=s(l,[["render",t]]);export{h as __pageData,g as default}; diff --git a/assets/zh_CN_guide_rescue-from-bootloop.md.3d907e03.js b/assets/zh_CN_guide_rescue-from-bootloop.md.fa76b90c.js similarity index 97% rename from assets/zh_CN_guide_rescue-from-bootloop.md.3d907e03.js rename to assets/zh_CN_guide_rescue-from-bootloop.md.fa76b90c.js index e98f48723376..0545d3fa8dd7 100644 --- a/assets/zh_CN_guide_rescue-from-bootloop.md.3d907e03.js +++ b/assets/zh_CN_guide_rescue-from-bootloop.md.fa76b90c.js @@ -1 +1 @@ -import{_ as o,o as e,c as a,O as t}from"./chunks/framework.43781440.js";const _=JSON.parse('{"title":"救砖","description":"","frontmatter":{},"headers":[],"relativePath":"zh_CN/guide/rescue-from-bootloop.md","filePath":"zh_CN/guide/rescue-from-bootloop.md"}'),r={name:"zh_CN/guide/rescue-from-bootloop.md"},l=t('

救砖

在刷机的时候我们可能会遇到设备“变砖”的情况,理论上讲,如果你只是使用 fastboot 刷入 boot 分区或者安装不合适的模块导致设备无法启动,那么这都可以通过合适的操作恢复手机;本文档旨在提供一些急救方法让你可以在“变砖”中恢复。

刷入 boot 变砖

在 KernelSU 中,刷入 boot 变砖有如下可能:

  1. 你刷入了错误格式的 boot 镜像。比如你的手机 boot 格式是 gz 的,但你刷入了 lz4 格式的镜像,那么此时手机无法启动。
  2. 你的手机需要关闭 avb 验证才能正常启动(注意这通常意味着需要清除手机所有数据)。
  3. 你的 kernel 有某些 bug 或者你的 kernel 不适合你这个手机刷入。

无论哪种情况,你都可以通过刷入原厂 boot恢复;因此,在安装教程最开始,我们已经强烈建议大家,在刷机之前备份自己的原厂 boot!如果你没有备份,那么你可以通过其他跟你相同设备的童鞋或者官方固件包获取原厂 boot。

刷入模块变砖

刷入模块变砖可能是大家遇到更常见的情况,但是这里必须郑重告诉大家:请勿刷入来路不明的模块!!。因为模块其实是有 root 权限的,它完全可能导致你的设备发生不可逆的损坏!

普通模块变砖

如果大家刷入某些开源的或者被证明是安全的模块使得手机无法启动,那么这种情况在 KernelSU 中非常容易恢复,完全无需担心。KernelSU 内置了如下两种机制来救砖:

  1. AB 更新
  2. 音量键救砖

AB 更新

KernelSU 的模块更新借鉴了 Android 系统 OTA 更新时的 AB 更新机制,如果你安装了新模块或者对已有模块有更新操作,不会直接操作当前使用的模块文件,而是会把所有模块构建成另外一个 update 镜像;系统重启之后,会使用这个 update 镜像尝试启动一次,如果 Android 系统成功启动,才会真正更新模块。

因此,最简单最常用的救砖方法就是:强制重启一次。如果你在刷某个模块之后系统无法启动,你可以长按电源键超过 10 秒,系统会自动重启;重启之后会回滚到更新模块之前的状态,之前更新的模块会被自动禁用。

音量键救砖

如果 AB 更新依然无法解决,你可以尝试使用安全模式。进入安全模式之后,所有的模块都会被禁用。

进入安全模式的方法有两种:

  1. 某些系统自带的安全模式;有些系统是长按音量下,有些系统(比如MIUI)可以在 Recovery 中开启安全模式。进入系统的安全模式后,KernelSU 也会进入安全模式,自动禁用模块。
  2. KernelSU 内置的安全模式;操作方法:开机第一屏后,连续按音量下键超过三次。注意是按下-松开、按下-松开、按下-松开,不是按着不动。

进入安全模式以后,KernelSU 管理器的模块页面所有模块都被禁用,但你可以执行“卸载”操作,卸载可能会有问题的模块。

内置的安全模式是在内核里面实现的,因此不会出现按键事件被拦截导致捕获不到的情况。不过对于非 GKI 内核,可能需要手动集成代码,可以参考官网教程。

格机或其他病毒模块变砖

如果以上方法无法拯救你的设备,那么很有可能你装的模块有恶意操作或者通过其他方式损坏了你的设备,这种情况下,只有两个建议:

  1. 清除数据后刷入完整刷入官方系统。
  2. 咨询售后服务。
',23),i=[l];function n(d,s,c,h,p,u){return e(),a("div",null,i)}const m=o(r,[["render",n]]);export{_ as __pageData,m as default}; +import{_ as o,o as e,c as a,Q as t}from"./chunks/framework.ec8f7e8e.js";const _=JSON.parse('{"title":"救砖","description":"","frontmatter":{},"headers":[],"relativePath":"zh_CN/guide/rescue-from-bootloop.md","filePath":"zh_CN/guide/rescue-from-bootloop.md"}'),r={name:"zh_CN/guide/rescue-from-bootloop.md"},l=t('

救砖

在刷机的时候我们可能会遇到设备“变砖”的情况,理论上讲,如果你只是使用 fastboot 刷入 boot 分区或者安装不合适的模块导致设备无法启动,那么这都可以通过合适的操作恢复手机;本文档旨在提供一些急救方法让你可以在“变砖”中恢复。

刷入 boot 变砖

在 KernelSU 中,刷入 boot 变砖有如下可能:

  1. 你刷入了错误格式的 boot 镜像。比如你的手机 boot 格式是 gz 的,但你刷入了 lz4 格式的镜像,那么此时手机无法启动。
  2. 你的手机需要关闭 avb 验证才能正常启动(注意这通常意味着需要清除手机所有数据)。
  3. 你的 kernel 有某些 bug 或者你的 kernel 不适合你这个手机刷入。

无论哪种情况,你都可以通过刷入原厂 boot恢复;因此,在安装教程最开始,我们已经强烈建议大家,在刷机之前备份自己的原厂 boot!如果你没有备份,那么你可以通过其他跟你相同设备的童鞋或者官方固件包获取原厂 boot。

刷入模块变砖

刷入模块变砖可能是大家遇到更常见的情况,但是这里必须郑重告诉大家:请勿刷入来路不明的模块!!。因为模块其实是有 root 权限的,它完全可能导致你的设备发生不可逆的损坏!

普通模块变砖

如果大家刷入某些开源的或者被证明是安全的模块使得手机无法启动,那么这种情况在 KernelSU 中非常容易恢复,完全无需担心。KernelSU 内置了如下两种机制来救砖:

  1. AB 更新
  2. 音量键救砖

AB 更新

KernelSU 的模块更新借鉴了 Android 系统 OTA 更新时的 AB 更新机制,如果你安装了新模块或者对已有模块有更新操作,不会直接操作当前使用的模块文件,而是会把所有模块构建成另外一个 update 镜像;系统重启之后,会使用这个 update 镜像尝试启动一次,如果 Android 系统成功启动,才会真正更新模块。

因此,最简单最常用的救砖方法就是:强制重启一次。如果你在刷某个模块之后系统无法启动,你可以长按电源键超过 10 秒,系统会自动重启;重启之后会回滚到更新模块之前的状态,之前更新的模块会被自动禁用。

音量键救砖

如果 AB 更新依然无法解决,你可以尝试使用安全模式。进入安全模式之后,所有的模块都会被禁用。

进入安全模式的方法有两种:

  1. 某些系统自带的安全模式;有些系统是长按音量下,有些系统(比如MIUI)可以在 Recovery 中开启安全模式。进入系统的安全模式后,KernelSU 也会进入安全模式,自动禁用模块。
  2. KernelSU 内置的安全模式;操作方法:开机第一屏后,连续按音量下键超过三次。注意是按下-松开、按下-松开、按下-松开,不是按着不动。

进入安全模式以后,KernelSU 管理器的模块页面所有模块都被禁用,但你可以执行“卸载”操作,卸载可能会有问题的模块。

内置的安全模式是在内核里面实现的,因此不会出现按键事件被拦截导致捕获不到的情况。不过对于非 GKI 内核,可能需要手动集成代码,可以参考官网教程。

格机或其他病毒模块变砖

如果以上方法无法拯救你的设备,那么很有可能你装的模块有恶意操作或者通过其他方式损坏了你的设备,这种情况下,只有两个建议:

  1. 清除数据后刷入完整刷入官方系统。
  2. 咨询售后服务。
',23),i=[l];function n(d,s,c,h,p,u){return e(),a("div",null,i)}const m=o(r,[["render",n]]);export{_ as __pageData,m as default}; diff --git a/assets/zh_CN_guide_rescue-from-bootloop.md.3d907e03.lean.js b/assets/zh_CN_guide_rescue-from-bootloop.md.fa76b90c.lean.js similarity index 71% rename from assets/zh_CN_guide_rescue-from-bootloop.md.3d907e03.lean.js rename to assets/zh_CN_guide_rescue-from-bootloop.md.fa76b90c.lean.js index 29496450a28a..7578b5131322 100644 --- a/assets/zh_CN_guide_rescue-from-bootloop.md.3d907e03.lean.js +++ b/assets/zh_CN_guide_rescue-from-bootloop.md.fa76b90c.lean.js @@ -1 +1 @@ -import{_ as o,o as e,c as a,O as t}from"./chunks/framework.43781440.js";const _=JSON.parse('{"title":"救砖","description":"","frontmatter":{},"headers":[],"relativePath":"zh_CN/guide/rescue-from-bootloop.md","filePath":"zh_CN/guide/rescue-from-bootloop.md"}'),r={name:"zh_CN/guide/rescue-from-bootloop.md"},l=t("",23),i=[l];function n(d,s,c,h,p,u){return e(),a("div",null,i)}const m=o(r,[["render",n]]);export{_ as __pageData,m as default}; +import{_ as o,o as e,c as a,Q as t}from"./chunks/framework.ec8f7e8e.js";const _=JSON.parse('{"title":"救砖","description":"","frontmatter":{},"headers":[],"relativePath":"zh_CN/guide/rescue-from-bootloop.md","filePath":"zh_CN/guide/rescue-from-bootloop.md"}'),r={name:"zh_CN/guide/rescue-from-bootloop.md"},l=t("",23),i=[l];function n(d,s,c,h,p,u){return e(),a("div",null,i)}const m=o(r,[["render",n]]);export{_ as __pageData,m as default}; diff --git a/assets/zh_CN_guide_unofficially-support-devices.md.e2fd6c11.js b/assets/zh_CN_guide_unofficially-support-devices.md.4c68d324.js similarity index 92% rename from assets/zh_CN_guide_unofficially-support-devices.md.e2fd6c11.js rename to assets/zh_CN_guide_unofficially-support-devices.md.4c68d324.js index d5966ea5887a..918c465c4428 100644 --- a/assets/zh_CN_guide_unofficially-support-devices.md.e2fd6c11.js +++ b/assets/zh_CN_guide_unofficially-support-devices.md.4c68d324.js @@ -1 +1 @@ -import{d as r}from"./chunks/repos.9a031e32.js";import{o as a,c as l,z as e,F as s,L as i,b as o,O as c,t as n}from"./chunks/framework.43781440.js";const _=c('

非官方支持设备

WARNING

本文档列出由其他开发者维护的支持 KernelSU 的非 GKI 设备内核

WARNING

本文档仅方便查找设备对应源码,这并不意味该源码 KernelSU 开发者审查,你应自行承担使用风险。

',3),d=e("thead",null,[e("tr",null,[e("th",null,"维护者"),e("th",null,"仓库地址"),e("th",null,"支持设备")])],-1),u=["href"],h=["href"],v=JSON.parse('{"title":"非官方支持设备","description":"","frontmatter":{},"headers":[],"relativePath":"zh_CN/guide/unofficially-support-devices.md","filePath":"zh_CN/guide/unofficially-support-devices.md"}'),f={name:"zh_CN/guide/unofficially-support-devices.md"},N=Object.assign(f,{setup(m){return(p,g)=>(a(),l("div",null,[_,e("table",null,[d,e("tbody",null,[(a(!0),l(s,null,i(o(r),t=>(a(),l("tr",{key:t.devices},[e("td",null,[e("a",{href:t.maintainer_link,target:"_blank",rel:"noreferrer"},n(t.maintainer),9,u)]),e("td",null,[e("a",{href:t.kernel_link,target:"_blank",rel:"noreferrer"},n(t.kernel_name),9,h)]),e("td",null,n(t.devices),1)]))),128))])])]))}});export{v as __pageData,N as default}; +import{d as r}from"./chunks/repos.9a031e32.js";import{o as a,c as l,k as e,F as s,D as i,l as o,Q as c,t as n}from"./chunks/framework.ec8f7e8e.js";const _=c('

非官方支持设备

WARNING

本文档列出由其他开发者维护的支持 KernelSU 的非 GKI 设备内核

WARNING

本文档仅方便查找设备对应源码,这并不意味该源码 KernelSU 开发者审查,你应自行承担使用风险。

',3),d=e("thead",null,[e("tr",null,[e("th",null,"维护者"),e("th",null,"仓库地址"),e("th",null,"支持设备")])],-1),u=["href"],h=["href"],v=JSON.parse('{"title":"非官方支持设备","description":"","frontmatter":{},"headers":[],"relativePath":"zh_CN/guide/unofficially-support-devices.md","filePath":"zh_CN/guide/unofficially-support-devices.md"}'),f={name:"zh_CN/guide/unofficially-support-devices.md"},N=Object.assign(f,{setup(m){return(p,g)=>(a(),l("div",null,[_,e("table",null,[d,e("tbody",null,[(a(!0),l(s,null,i(o(r),t=>(a(),l("tr",{key:t.devices},[e("td",null,[e("a",{href:t.maintainer_link,target:"_blank",rel:"noreferrer"},n(t.maintainer),9,u)]),e("td",null,[e("a",{href:t.kernel_link,target:"_blank",rel:"noreferrer"},n(t.kernel_name),9,h)]),e("td",null,n(t.devices),1)]))),128))])])]))}});export{v as __pageData,N as default}; diff --git a/assets/zh_CN_guide_unofficially-support-devices.md.e2fd6c11.lean.js b/assets/zh_CN_guide_unofficially-support-devices.md.4c68d324.lean.js similarity index 87% rename from assets/zh_CN_guide_unofficially-support-devices.md.e2fd6c11.lean.js rename to assets/zh_CN_guide_unofficially-support-devices.md.4c68d324.lean.js index 94e9e0789df8..7ec070039d2a 100644 --- a/assets/zh_CN_guide_unofficially-support-devices.md.e2fd6c11.lean.js +++ b/assets/zh_CN_guide_unofficially-support-devices.md.4c68d324.lean.js @@ -1 +1 @@ -import{d as r}from"./chunks/repos.9a031e32.js";import{o as a,c as l,z as e,F as s,L as i,b as o,O as c,t as n}from"./chunks/framework.43781440.js";const _=c("",3),d=e("thead",null,[e("tr",null,[e("th",null,"维护者"),e("th",null,"仓库地址"),e("th",null,"支持设备")])],-1),u=["href"],h=["href"],v=JSON.parse('{"title":"非官方支持设备","description":"","frontmatter":{},"headers":[],"relativePath":"zh_CN/guide/unofficially-support-devices.md","filePath":"zh_CN/guide/unofficially-support-devices.md"}'),f={name:"zh_CN/guide/unofficially-support-devices.md"},N=Object.assign(f,{setup(m){return(p,g)=>(a(),l("div",null,[_,e("table",null,[d,e("tbody",null,[(a(!0),l(s,null,i(o(r),t=>(a(),l("tr",{key:t.devices},[e("td",null,[e("a",{href:t.maintainer_link,target:"_blank",rel:"noreferrer"},n(t.maintainer),9,u)]),e("td",null,[e("a",{href:t.kernel_link,target:"_blank",rel:"noreferrer"},n(t.kernel_name),9,h)]),e("td",null,n(t.devices),1)]))),128))])])]))}});export{v as __pageData,N as default}; +import{d as r}from"./chunks/repos.9a031e32.js";import{o as a,c as l,k as e,F as s,D as i,l as o,Q as c,t as n}from"./chunks/framework.ec8f7e8e.js";const _=c("",3),d=e("thead",null,[e("tr",null,[e("th",null,"维护者"),e("th",null,"仓库地址"),e("th",null,"支持设备")])],-1),u=["href"],h=["href"],v=JSON.parse('{"title":"非官方支持设备","description":"","frontmatter":{},"headers":[],"relativePath":"zh_CN/guide/unofficially-support-devices.md","filePath":"zh_CN/guide/unofficially-support-devices.md"}'),f={name:"zh_CN/guide/unofficially-support-devices.md"},N=Object.assign(f,{setup(m){return(p,g)=>(a(),l("div",null,[_,e("table",null,[d,e("tbody",null,[(a(!0),l(s,null,i(o(r),t=>(a(),l("tr",{key:t.devices},[e("td",null,[e("a",{href:t.maintainer_link,target:"_blank",rel:"noreferrer"},n(t.maintainer),9,u)]),e("td",null,[e("a",{href:t.kernel_link,target:"_blank",rel:"noreferrer"},n(t.kernel_name),9,h)]),e("td",null,n(t.devices),1)]))),128))])])]))}});export{v as __pageData,N as default}; diff --git a/assets/zh_CN_guide_what-is-kernelsu.md.cdc98b69.js b/assets/zh_CN_guide_what-is-kernelsu.md.296b4e28.js similarity index 94% rename from assets/zh_CN_guide_what-is-kernelsu.md.cdc98b69.js rename to assets/zh_CN_guide_what-is-kernelsu.md.296b4e28.js index d06afc7df475..beddbc44047b 100644 --- a/assets/zh_CN_guide_what-is-kernelsu.md.cdc98b69.js +++ b/assets/zh_CN_guide_what-is-kernelsu.md.296b4e28.js @@ -1 +1 @@ -import{_ as e,o as a,c as t,O as r}from"./chunks/framework.43781440.js";const f=JSON.parse('{"title":"什么是 KernelSU?","description":"","frontmatter":{},"headers":[],"relativePath":"zh_CN/guide/what-is-kernelsu.md","filePath":"zh_CN/guide/what-is-kernelsu.md"}'),o={name:"zh_CN/guide/what-is-kernelsu.md"},n=r('

什么是 KernelSU?

KernelSU 是 Android GKI 设备的 root 解决方案,它工作在内核模式,并直接在内核空间中为用户空间应用程序授予 root 权限。

功能

KernelSU 的主要特点是它是基于内核的。 KernelSU 运行在内核空间, 所以它可以提供我们以前从未有过的内核接口。 例如,我们可以在内核模式下为任何进程添加硬件断点;我们可以在任何进程的物理内存中访问,而无人知晓;我们可以在内核空间拦截任何系统调用; 等等。

KernelSU 还提供了一个基于 overlayfs 的模块系统,允许您加载自定义插件到系统中。它还提供了一种修改 /system 分区中文件的机制。

如何使用

请参考: 安装

如何构建

请参考: 如何构建

讨论

',11),s=[n];function i(l,h,d,u,c,_){return a(),t("div",null,s)}const m=e(o,[["render",i]]);export{f as __pageData,m as default}; +import{_ as e,o as a,c as t,Q as r}from"./chunks/framework.ec8f7e8e.js";const f=JSON.parse('{"title":"什么是 KernelSU?","description":"","frontmatter":{},"headers":[],"relativePath":"zh_CN/guide/what-is-kernelsu.md","filePath":"zh_CN/guide/what-is-kernelsu.md"}'),o={name:"zh_CN/guide/what-is-kernelsu.md"},n=r('

什么是 KernelSU?

KernelSU 是 Android GKI 设备的 root 解决方案,它工作在内核模式,并直接在内核空间中为用户空间应用程序授予 root 权限。

功能

KernelSU 的主要特点是它是基于内核的。 KernelSU 运行在内核空间, 所以它可以提供我们以前从未有过的内核接口。 例如,我们可以在内核模式下为任何进程添加硬件断点;我们可以在任何进程的物理内存中访问,而无人知晓;我们可以在内核空间拦截任何系统调用; 等等。

KernelSU 还提供了一个基于 overlayfs 的模块系统,允许您加载自定义插件到系统中。它还提供了一种修改 /system 分区中文件的机制。

如何使用

请参考: 安装

如何构建

请参考: 如何构建

讨论

',11),s=[n];function i(l,h,d,u,c,_){return a(),t("div",null,s)}const m=e(o,[["render",i]]);export{f as __pageData,m as default}; diff --git a/assets/zh_CN_guide_what-is-kernelsu.md.cdc98b69.lean.js b/assets/zh_CN_guide_what-is-kernelsu.md.296b4e28.lean.js similarity index 71% rename from assets/zh_CN_guide_what-is-kernelsu.md.cdc98b69.lean.js rename to assets/zh_CN_guide_what-is-kernelsu.md.296b4e28.lean.js index a5b6d433752f..19ffda25e69e 100644 --- a/assets/zh_CN_guide_what-is-kernelsu.md.cdc98b69.lean.js +++ b/assets/zh_CN_guide_what-is-kernelsu.md.296b4e28.lean.js @@ -1 +1 @@ -import{_ as e,o as a,c as t,O as r}from"./chunks/framework.43781440.js";const f=JSON.parse('{"title":"什么是 KernelSU?","description":"","frontmatter":{},"headers":[],"relativePath":"zh_CN/guide/what-is-kernelsu.md","filePath":"zh_CN/guide/what-is-kernelsu.md"}'),o={name:"zh_CN/guide/what-is-kernelsu.md"},n=r("",11),s=[n];function i(l,h,d,u,c,_){return a(),t("div",null,s)}const m=e(o,[["render",i]]);export{f as __pageData,m as default}; +import{_ as e,o as a,c as t,Q as r}from"./chunks/framework.ec8f7e8e.js";const f=JSON.parse('{"title":"什么是 KernelSU?","description":"","frontmatter":{},"headers":[],"relativePath":"zh_CN/guide/what-is-kernelsu.md","filePath":"zh_CN/guide/what-is-kernelsu.md"}'),o={name:"zh_CN/guide/what-is-kernelsu.md"},n=r("",11),s=[n];function i(l,h,d,u,c,_){return a(),t("div",null,s)}const m=e(o,[["render",i]]);export{f as __pageData,m as default}; diff --git a/assets/zh_CN_index.md.7dc4034d.js b/assets/zh_CN_index.md.c6e87037.js similarity index 95% rename from assets/zh_CN_index.md.7dc4034d.js rename to assets/zh_CN_index.md.c6e87037.js index b5bf47b28eb8..48e34cfdd7a4 100644 --- a/assets/zh_CN_index.md.7dc4034d.js +++ b/assets/zh_CN_index.md.c6e87037.js @@ -1 +1 @@ -import{_ as e,o as t,c as i}from"./chunks/framework.43781440.js";const m=JSON.parse('{"title":"Android 上的内核级的 root 方案","description":"","frontmatter":{"layout":"home","title":"Android 上的内核级的 root 方案","hero":{"name":"KernelSU","text":"Android 上的内核级的 root 方案","tagline":"","image":{"src":"/logo.png","alt":"KernelSU"},"actions":[{"theme":"brand","text":"开始了解","link":"/zh_CN/guide/what-is-kernelsu"},{"theme":"alt","text":"在 GitHub 中查看","link":"https://github.com/tiann/KernelSU"}]},"features":[{"title":"基于内核","details":"KernelSU 运行在内核空间,对用户空间应用有更强的掌控。"},{"title":"白名单访问控制","details":"只有被授权的 App 才可以访问 `su`,而其他 App 无法感知其存在。"},{"title":"受限制的 root 权限","details":"KernelSU 可以自定义 `su` 的 uid, gid, groups, capabilities 和 SELinux 规则:把 root 权限关进笼子里。"},{"title":"模块系统 & 开源","details":"KernelSU 支持通过 overlayfs 修改 /system,并且是 GPL-3 许可下的开源项目。"}]},"headers":[],"relativePath":"zh_CN/index.md","filePath":"zh_CN/index.md"}'),n={name:"zh_CN/index.md"};function o(r,a,s,l,d,c){return t(),i("div")}const h=e(n,[["render",o]]);export{m as __pageData,h as default}; +import{_ as e,o as t,c as i}from"./chunks/framework.ec8f7e8e.js";const m=JSON.parse('{"title":"Android 上的内核级的 root 方案","description":"","frontmatter":{"layout":"home","title":"Android 上的内核级的 root 方案","hero":{"name":"KernelSU","text":"Android 上的内核级的 root 方案","tagline":"","image":{"src":"/logo.png","alt":"KernelSU"},"actions":[{"theme":"brand","text":"开始了解","link":"/zh_CN/guide/what-is-kernelsu"},{"theme":"alt","text":"在 GitHub 中查看","link":"https://github.com/tiann/KernelSU"}]},"features":[{"title":"基于内核","details":"KernelSU 运行在内核空间,对用户空间应用有更强的掌控。"},{"title":"白名单访问控制","details":"只有被授权的 App 才可以访问 `su`,而其他 App 无法感知其存在。"},{"title":"受限制的 root 权限","details":"KernelSU 可以自定义 `su` 的 uid, gid, groups, capabilities 和 SELinux 规则:把 root 权限关进笼子里。"},{"title":"模块系统 & 开源","details":"KernelSU 支持通过 overlayfs 修改 /system,并且是 GPL-3 许可下的开源项目。"}]},"headers":[],"relativePath":"zh_CN/index.md","filePath":"zh_CN/index.md"}'),n={name:"zh_CN/index.md"};function o(r,a,s,l,d,c){return t(),i("div")}const h=e(n,[["render",o]]);export{m as __pageData,h as default}; diff --git a/assets/zh_CN_index.md.7dc4034d.lean.js b/assets/zh_CN_index.md.c6e87037.lean.js similarity index 95% rename from assets/zh_CN_index.md.7dc4034d.lean.js rename to assets/zh_CN_index.md.c6e87037.lean.js index b5bf47b28eb8..48e34cfdd7a4 100644 --- a/assets/zh_CN_index.md.7dc4034d.lean.js +++ b/assets/zh_CN_index.md.c6e87037.lean.js @@ -1 +1 @@ -import{_ as e,o as t,c as i}from"./chunks/framework.43781440.js";const m=JSON.parse('{"title":"Android 上的内核级的 root 方案","description":"","frontmatter":{"layout":"home","title":"Android 上的内核级的 root 方案","hero":{"name":"KernelSU","text":"Android 上的内核级的 root 方案","tagline":"","image":{"src":"/logo.png","alt":"KernelSU"},"actions":[{"theme":"brand","text":"开始了解","link":"/zh_CN/guide/what-is-kernelsu"},{"theme":"alt","text":"在 GitHub 中查看","link":"https://github.com/tiann/KernelSU"}]},"features":[{"title":"基于内核","details":"KernelSU 运行在内核空间,对用户空间应用有更强的掌控。"},{"title":"白名单访问控制","details":"只有被授权的 App 才可以访问 `su`,而其他 App 无法感知其存在。"},{"title":"受限制的 root 权限","details":"KernelSU 可以自定义 `su` 的 uid, gid, groups, capabilities 和 SELinux 规则:把 root 权限关进笼子里。"},{"title":"模块系统 & 开源","details":"KernelSU 支持通过 overlayfs 修改 /system,并且是 GPL-3 许可下的开源项目。"}]},"headers":[],"relativePath":"zh_CN/index.md","filePath":"zh_CN/index.md"}'),n={name:"zh_CN/index.md"};function o(r,a,s,l,d,c){return t(),i("div")}const h=e(n,[["render",o]]);export{m as __pageData,h as default}; +import{_ as e,o as t,c as i}from"./chunks/framework.ec8f7e8e.js";const m=JSON.parse('{"title":"Android 上的内核级的 root 方案","description":"","frontmatter":{"layout":"home","title":"Android 上的内核级的 root 方案","hero":{"name":"KernelSU","text":"Android 上的内核级的 root 方案","tagline":"","image":{"src":"/logo.png","alt":"KernelSU"},"actions":[{"theme":"brand","text":"开始了解","link":"/zh_CN/guide/what-is-kernelsu"},{"theme":"alt","text":"在 GitHub 中查看","link":"https://github.com/tiann/KernelSU"}]},"features":[{"title":"基于内核","details":"KernelSU 运行在内核空间,对用户空间应用有更强的掌控。"},{"title":"白名单访问控制","details":"只有被授权的 App 才可以访问 `su`,而其他 App 无法感知其存在。"},{"title":"受限制的 root 权限","details":"KernelSU 可以自定义 `su` 的 uid, gid, groups, capabilities 和 SELinux 规则:把 root 权限关进笼子里。"},{"title":"模块系统 & 开源","details":"KernelSU 支持通过 overlayfs 修改 /system,并且是 GPL-3 许可下的开源项目。"}]},"headers":[],"relativePath":"zh_CN/index.md","filePath":"zh_CN/index.md"}'),n={name:"zh_CN/index.md"};function o(r,a,s,l,d,c){return t(),i("div")}const h=e(n,[["render",o]]);export{m as __pageData,h as default}; diff --git a/assets/zh_TW_guide_difference-with-magisk.md.161de8ef.js b/assets/zh_TW_guide_difference-with-magisk.md.831b592b.js similarity index 95% rename from assets/zh_TW_guide_difference-with-magisk.md.161de8ef.js rename to assets/zh_TW_guide_difference-with-magisk.md.831b592b.js index 720a3b4483da..7e404487d457 100644 --- a/assets/zh_TW_guide_difference-with-magisk.md.161de8ef.js +++ b/assets/zh_TW_guide_difference-with-magisk.md.831b592b.js @@ -1 +1 @@ -import{_ as e,o as i,c as a,O as o}from"./chunks/framework.43781440.js";const u=JSON.parse('{"title":"KernelSU 模組與 Magisk 的差異","description":"","frontmatter":{},"headers":[],"relativePath":"zh_TW/guide/difference-with-magisk.md","filePath":"zh_TW/guide/difference-with-magisk.md"}'),s={name:"zh_TW/guide/difference-with-magisk.md"},l=o('

KernelSU 模組與 Magisk 的差異

儘管 KernelSU 模組和 Magisk 模組之間有許多相似之處,但由於它們完全不同的實作機制,不可避免地存在一些差異;如果您想讓您的模組同時在 Magisk 和 KernelSU 上運作,那麼您必須瞭解這些差異。

相同之處

  • 模組檔案格式:都以 Zip 的格式組織模組,並且模組的格式幾乎相同
  • 模組安裝目錄:都位於 /data/adb/modules
  • Systemless:都支援透過模組的形式以 systemless 修改 /system
  • post-fs-data.sh:執行時間和語義完全相同
  • service.sh:執行時間和語義完全相同
  • system.prop:完全相同
  • sepolicy.rule:完全相同
  • BusyBox:指令碼在 BusyBox 中以「獨立模式」執行

不同之處

在瞭解不同之處之前,您需要知道如何區分您的模組是在 KernelSU 還是 Magisk 中執行;在所有可以執行模組指令碼的位置 (customize.sh, post-fs-data.sh, service.sh),您都可以使用環境變數 KSU 來區分,在 KernelSU 中,這個環境變數將被設定為 true

以下是一些不同之處:

  1. KernelSU 的模組不支援在 Recovery 中安裝。
  2. KernelSU 的模組沒有內建的 Zygisk 支援 (但您可以透過 ZygiskNext 來使用 Zygisk 模組)。
  3. KernelSU 模組取代或刪除檔案與 Magisk 完全不同。KernelSU 不支援 .replace 方法,相反,您需要透過 mknod filename c 0 0 建立相同名稱的資料夾以刪除對應檔案。
  4. BusyBox 的目錄不同;KernelSU 內建的 BusyBox 在 /data/adb/ksu/bin/busybox 而 Magisk 在 /data/adb/magisk/busybox注意此為 KernelSU 內部行為,未來可能會變更!
  5. KernelSU 不支援 .replace 檔案;但 KernelSU 支援 REPLACEREMOVE 變數以移除或取代檔案 (資料夾)。
',8),t=[l];function r(d,c,n,h,_,f){return i(),a("div",null,t)}const p=e(s,[["render",r]]);export{u as __pageData,p as default}; +import{_ as e,o as i,c as a,Q as o}from"./chunks/framework.ec8f7e8e.js";const u=JSON.parse('{"title":"KernelSU 模組與 Magisk 的差異","description":"","frontmatter":{},"headers":[],"relativePath":"zh_TW/guide/difference-with-magisk.md","filePath":"zh_TW/guide/difference-with-magisk.md"}'),s={name:"zh_TW/guide/difference-with-magisk.md"},l=o('

KernelSU 模組與 Magisk 的差異

儘管 KernelSU 模組和 Magisk 模組之間有許多相似之處,但由於它們完全不同的實作機制,不可避免地存在一些差異;如果您想讓您的模組同時在 Magisk 和 KernelSU 上運作,那麼您必須瞭解這些差異。

相同之處

  • 模組檔案格式:都以 Zip 的格式組織模組,並且模組的格式幾乎相同
  • 模組安裝目錄:都位於 /data/adb/modules
  • Systemless:都支援透過模組的形式以 systemless 修改 /system
  • post-fs-data.sh:執行時間和語義完全相同
  • service.sh:執行時間和語義完全相同
  • system.prop:完全相同
  • sepolicy.rule:完全相同
  • BusyBox:指令碼在 BusyBox 中以「獨立模式」執行

不同之處

在瞭解不同之處之前,您需要知道如何區分您的模組是在 KernelSU 還是 Magisk 中執行;在所有可以執行模組指令碼的位置 (customize.sh, post-fs-data.sh, service.sh),您都可以使用環境變數 KSU 來區分,在 KernelSU 中,這個環境變數將被設定為 true

以下是一些不同之處:

  1. KernelSU 的模組不支援在 Recovery 中安裝。
  2. KernelSU 的模組沒有內建的 Zygisk 支援 (但您可以透過 ZygiskNext 來使用 Zygisk 模組)。
  3. KernelSU 模組取代或刪除檔案與 Magisk 完全不同。KernelSU 不支援 .replace 方法,相反,您需要透過 mknod filename c 0 0 建立相同名稱的資料夾以刪除對應檔案。
  4. BusyBox 的目錄不同;KernelSU 內建的 BusyBox 在 /data/adb/ksu/bin/busybox 而 Magisk 在 /data/adb/magisk/busybox注意此為 KernelSU 內部行為,未來可能會變更!
  5. KernelSU 不支援 .replace 檔案;但 KernelSU 支援 REPLACEREMOVE 變數以移除或取代檔案 (資料夾)。
',8),t=[l];function r(d,c,n,h,_,f){return i(),a("div",null,t)}const p=e(s,[["render",r]]);export{u as __pageData,p as default}; diff --git a/assets/zh_TW_guide_difference-with-magisk.md.161de8ef.lean.js b/assets/zh_TW_guide_difference-with-magisk.md.831b592b.lean.js similarity index 73% rename from assets/zh_TW_guide_difference-with-magisk.md.161de8ef.lean.js rename to assets/zh_TW_guide_difference-with-magisk.md.831b592b.lean.js index 405b76f02416..1f0431483b58 100644 --- a/assets/zh_TW_guide_difference-with-magisk.md.161de8ef.lean.js +++ b/assets/zh_TW_guide_difference-with-magisk.md.831b592b.lean.js @@ -1 +1 @@ -import{_ as e,o as i,c as a,O as o}from"./chunks/framework.43781440.js";const u=JSON.parse('{"title":"KernelSU 模組與 Magisk 的差異","description":"","frontmatter":{},"headers":[],"relativePath":"zh_TW/guide/difference-with-magisk.md","filePath":"zh_TW/guide/difference-with-magisk.md"}'),s={name:"zh_TW/guide/difference-with-magisk.md"},l=o("",8),t=[l];function r(d,c,n,h,_,f){return i(),a("div",null,t)}const p=e(s,[["render",r]]);export{u as __pageData,p as default}; +import{_ as e,o as i,c as a,Q as o}from"./chunks/framework.ec8f7e8e.js";const u=JSON.parse('{"title":"KernelSU 模組與 Magisk 的差異","description":"","frontmatter":{},"headers":[],"relativePath":"zh_TW/guide/difference-with-magisk.md","filePath":"zh_TW/guide/difference-with-magisk.md"}'),s={name:"zh_TW/guide/difference-with-magisk.md"},l=o("",8),t=[l];function r(d,c,n,h,_,f){return i(),a("div",null,t)}const p=e(s,[["render",r]]);export{u as __pageData,p as default}; diff --git a/assets/zh_TW_guide_faq.md.bea0a7af.js b/assets/zh_TW_guide_faq.md.e91224a7.js similarity index 98% rename from assets/zh_TW_guide_faq.md.bea0a7af.js rename to assets/zh_TW_guide_faq.md.e91224a7.js index cf4c11627cf1..42b642f5c6a1 100644 --- a/assets/zh_TW_guide_faq.md.bea0a7af.js +++ b/assets/zh_TW_guide_faq.md.e91224a7.js @@ -1 +1 @@ -import{_ as e,o as a,c as r,O as n}from"./chunks/framework.43781440.js";const m=JSON.parse('{"title":"常見問題","description":"","frontmatter":{},"headers":[],"relativePath":"zh_TW/guide/faq.md","filePath":"zh_TW/guide/faq.md"}'),o={name:"zh_TW/guide/faq.md"},l=n('

常見問題

KernelSU 是否支援我的裝置?

首先,您的裝置應該能解鎖 Bootloader。如果不能,則不支援。

然後在您的裝置上安裝 KernelSU 管理員並開啟它,如果它顯示 不支援,那麼您的裝置沒有官方支援的開箱即用的 Boot 映像;但您可以自行建置核心來源並整合 KernelSU 以繼續使用。

KernelSU 是否需要解鎖 Bootloader?

當然需要。

KernelSU 是否支援模組?

支援,但它是早期版本,可能存在問題。請等候它逐漸穩定 😃

KernelSU 是否支援 Xposed ?

支援。DreamlandTaiChi 可以正常運作。LSPosed 可以在 ZygiskNext 的支援下正常運作。

KernelSU 支援 Zygisk 嗎?

KernelSU 沒有內建 Zygisk 支援,但是您可以用 ZygiskNext 來使用 Zygisk 模組。

KernelSU 與 Magisk 相容嗎?

KernelSU 的模組系統與 Magisk 的 magic mount 存在衝突,如果在 KernelSU 中啟用了任何模組,那麼整個 Magisk 將無法正常運作。

但是如果您只使用 KernelSU 的 su,那么它會和 Magisk 一同運作:KernelSU 修改 kernel 、 Magisk 修改 ramdisk,它們可以搭配使用。

KernelSU 会取代 Magisk 嗎?

我們不這樣認為,這也不是我們的目標。Magisk 對於使用者空間 Root 解決方案來說已經足夠優秀了,它會存在很長一段時間。KernelSU 的目標是為使用者提供核心介面,而非取代 Magisk。

KernelSU 可以支援非 GKI 裝置嗎?

可以。但是您應該下載核心來源並整合 KernelSU 至來源樹狀結構並自行編譯核心。

KernelSU 支援 Android 12 以下的裝置嗎?

影響 KernelSU 相容性的是裝置的核心版本,它與 Android 版本並無直接關係。唯一有關聯的是:原廠 Android 12 的裝置,一定是 5.10 或更高的核心 (GKI 裝置);因此結論如下:

  1. 原廠 Android 12 的裝置必定支援 (GKI 裝置)
  2. 舊版核心的裝置 (即使是 Android 12,也可能是舊版核心) 是相容的 (您需要自行建置核心)

KernelSU 可以支援舊版核心嗎?

可以,目前最低支援到 4.14;更低的版本您需要手動移植它,歡迎 PR!

如何為舊版核心整合 KernelSU?

請參閱指南

為何我的 Android 版本為 13,但核心版本卻是 "android12-5.10"?

核心版本與 Android 版本無關,如果您要刷新 KernelSU,請一律使用核心版本而非 Android 版本,如果你為 "android12-5.10" 的裝置刷新 Android 13 的核心,等候您的將會是開機迴圈。

KernelSU 支援 --mount-master/全域掛接命名空間嗎?

目前沒有 (未來可能會支援),但實際上有很多種方法手動進入全域命名空間,無需 Su 內建支援,比如:

  1. nsenter -t 1 -m sh 可以取得一個全域 mount namespace 的 shell.
  2. 在您要執行的命令前新增 nsenter --mount=/proc/1/ns/mnt 即可使此命令在全域 mount namespace 下執行。KernelSU 本身也使用了 這種方法

我是 GKI1.0,能用 KernelSU 嗎?

GKI1 與 GKI2 完全不同,所以您需要自行編譯核心。

',33),t=[l];function i(s,d,h,u,k,c){return a(),r("div",null,t)}const g=e(o,[["render",i]]);export{m as __pageData,g as default}; +import{_ as e,o as a,c as r,Q as n}from"./chunks/framework.ec8f7e8e.js";const m=JSON.parse('{"title":"常見問題","description":"","frontmatter":{},"headers":[],"relativePath":"zh_TW/guide/faq.md","filePath":"zh_TW/guide/faq.md"}'),o={name:"zh_TW/guide/faq.md"},l=n('

常見問題

KernelSU 是否支援我的裝置?

首先,您的裝置應該能解鎖 Bootloader。如果不能,則不支援。

然後在您的裝置上安裝 KernelSU 管理員並開啟它,如果它顯示 不支援,那麼您的裝置沒有官方支援的開箱即用的 Boot 映像;但您可以自行建置核心來源並整合 KernelSU 以繼續使用。

KernelSU 是否需要解鎖 Bootloader?

當然需要。

KernelSU 是否支援模組?

支援,但它是早期版本,可能存在問題。請等候它逐漸穩定 😃

KernelSU 是否支援 Xposed ?

支援。DreamlandTaiChi 可以正常運作。LSPosed 可以在 ZygiskNext 的支援下正常運作。

KernelSU 支援 Zygisk 嗎?

KernelSU 沒有內建 Zygisk 支援,但是您可以用 ZygiskNext 來使用 Zygisk 模組。

KernelSU 與 Magisk 相容嗎?

KernelSU 的模組系統與 Magisk 的 magic mount 存在衝突,如果在 KernelSU 中啟用了任何模組,那麼整個 Magisk 將無法正常運作。

但是如果您只使用 KernelSU 的 su,那么它會和 Magisk 一同運作:KernelSU 修改 kernel 、 Magisk 修改 ramdisk,它們可以搭配使用。

KernelSU 会取代 Magisk 嗎?

我們不這樣認為,這也不是我們的目標。Magisk 對於使用者空間 Root 解決方案來說已經足夠優秀了,它會存在很長一段時間。KernelSU 的目標是為使用者提供核心介面,而非取代 Magisk。

KernelSU 可以支援非 GKI 裝置嗎?

可以。但是您應該下載核心來源並整合 KernelSU 至來源樹狀結構並自行編譯核心。

KernelSU 支援 Android 12 以下的裝置嗎?

影響 KernelSU 相容性的是裝置的核心版本,它與 Android 版本並無直接關係。唯一有關聯的是:原廠 Android 12 的裝置,一定是 5.10 或更高的核心 (GKI 裝置);因此結論如下:

  1. 原廠 Android 12 的裝置必定支援 (GKI 裝置)
  2. 舊版核心的裝置 (即使是 Android 12,也可能是舊版核心) 是相容的 (您需要自行建置核心)

KernelSU 可以支援舊版核心嗎?

可以,目前最低支援到 4.14;更低的版本您需要手動移植它,歡迎 PR!

如何為舊版核心整合 KernelSU?

請參閱指南

為何我的 Android 版本為 13,但核心版本卻是 "android12-5.10"?

核心版本與 Android 版本無關,如果您要刷新 KernelSU,請一律使用核心版本而非 Android 版本,如果你為 "android12-5.10" 的裝置刷新 Android 13 的核心,等候您的將會是開機迴圈。

KernelSU 支援 --mount-master/全域掛接命名空間嗎?

目前沒有 (未來可能會支援),但實際上有很多種方法手動進入全域命名空間,無需 Su 內建支援,比如:

  1. nsenter -t 1 -m sh 可以取得一個全域 mount namespace 的 shell.
  2. 在您要執行的命令前新增 nsenter --mount=/proc/1/ns/mnt 即可使此命令在全域 mount namespace 下執行。KernelSU 本身也使用了 這種方法

我是 GKI1.0,能用 KernelSU 嗎?

GKI1 與 GKI2 完全不同,所以您需要自行編譯核心。

',33),t=[l];function i(s,d,h,u,k,c){return a(),r("div",null,t)}const g=e(o,[["render",i]]);export{m as __pageData,g as default}; diff --git a/assets/zh_TW_guide_faq.md.bea0a7af.lean.js b/assets/zh_TW_guide_faq.md.e91224a7.lean.js similarity index 67% rename from assets/zh_TW_guide_faq.md.bea0a7af.lean.js rename to assets/zh_TW_guide_faq.md.e91224a7.lean.js index cc31c93371e9..6f2ac0856aab 100644 --- a/assets/zh_TW_guide_faq.md.bea0a7af.lean.js +++ b/assets/zh_TW_guide_faq.md.e91224a7.lean.js @@ -1 +1 @@ -import{_ as e,o as a,c as r,O as n}from"./chunks/framework.43781440.js";const m=JSON.parse('{"title":"常見問題","description":"","frontmatter":{},"headers":[],"relativePath":"zh_TW/guide/faq.md","filePath":"zh_TW/guide/faq.md"}'),o={name:"zh_TW/guide/faq.md"},l=n("",33),t=[l];function i(s,d,h,u,k,c){return a(),r("div",null,t)}const g=e(o,[["render",i]]);export{m as __pageData,g as default}; +import{_ as e,o as a,c as r,Q as n}from"./chunks/framework.ec8f7e8e.js";const m=JSON.parse('{"title":"常見問題","description":"","frontmatter":{},"headers":[],"relativePath":"zh_TW/guide/faq.md","filePath":"zh_TW/guide/faq.md"}'),o={name:"zh_TW/guide/faq.md"},l=n("",33),t=[l];function i(s,d,h,u,k,c){return a(),r("div",null,t)}const g=e(o,[["render",i]]);export{m as __pageData,g as default}; diff --git a/assets/zh_TW_guide_hidden-features.md.f0bea9ae.js b/assets/zh_TW_guide_hidden-features.md.728dbc1c.js similarity index 86% rename from assets/zh_TW_guide_hidden-features.md.f0bea9ae.js rename to assets/zh_TW_guide_hidden-features.md.728dbc1c.js index 2139180fe4b6..14e91d670b9b 100644 --- a/assets/zh_TW_guide_hidden-features.md.f0bea9ae.js +++ b/assets/zh_TW_guide_hidden-features.md.728dbc1c.js @@ -1 +1 @@ -import{_ as e,o as a,c as t,O as s}from"./chunks/framework.43781440.js";const p=JSON.parse('{"title":"隱藏功能","description":"","frontmatter":{},"headers":[],"relativePath":"zh_TW/guide/hidden-features.md","filePath":"zh_TW/guide/hidden-features.md"}'),c={name:"zh_TW/guide/hidden-features.md"},d=s('

隱藏功能

ksurc

預設狀況下,/system/bin/sh 會載入 /system/etc/mkshrc

可以透過建立 /data/adb/ksu/.ksurc 檔案來讓 Su 載入此檔案而非 /system/etc/mkshrc

',4),r=[d];function o(_,i,n,h,u,l){return a(),t("div",null,r)}const f=e(c,[["render",o]]);export{p as __pageData,f as default}; +import{_ as e,o as a,c as t,Q as s}from"./chunks/framework.ec8f7e8e.js";const p=JSON.parse('{"title":"隱藏功能","description":"","frontmatter":{},"headers":[],"relativePath":"zh_TW/guide/hidden-features.md","filePath":"zh_TW/guide/hidden-features.md"}'),c={name:"zh_TW/guide/hidden-features.md"},d=s('

隱藏功能

ksurc

預設狀況下,/system/bin/sh 會載入 /system/etc/mkshrc

可以透過建立 /data/adb/ksu/.ksurc 檔案來讓 Su 載入此檔案而非 /system/etc/mkshrc

',4),r=[d];function o(_,i,n,h,u,l){return a(),t("div",null,r)}const f=e(c,[["render",o]]);export{p as __pageData,f as default}; diff --git a/assets/zh_TW_guide_hidden-features.md.f0bea9ae.lean.js b/assets/zh_TW_guide_hidden-features.md.728dbc1c.lean.js similarity index 70% rename from assets/zh_TW_guide_hidden-features.md.f0bea9ae.lean.js rename to assets/zh_TW_guide_hidden-features.md.728dbc1c.lean.js index 312c1f4c83bd..56bac43339ca 100644 --- a/assets/zh_TW_guide_hidden-features.md.f0bea9ae.lean.js +++ b/assets/zh_TW_guide_hidden-features.md.728dbc1c.lean.js @@ -1 +1 @@ -import{_ as e,o as a,c as t,O as s}from"./chunks/framework.43781440.js";const p=JSON.parse('{"title":"隱藏功能","description":"","frontmatter":{},"headers":[],"relativePath":"zh_TW/guide/hidden-features.md","filePath":"zh_TW/guide/hidden-features.md"}'),c={name:"zh_TW/guide/hidden-features.md"},d=s("",4),r=[d];function o(_,i,n,h,u,l){return a(),t("div",null,r)}const f=e(c,[["render",o]]);export{p as __pageData,f as default}; +import{_ as e,o as a,c as t,Q as s}from"./chunks/framework.ec8f7e8e.js";const p=JSON.parse('{"title":"隱藏功能","description":"","frontmatter":{},"headers":[],"relativePath":"zh_TW/guide/hidden-features.md","filePath":"zh_TW/guide/hidden-features.md"}'),c={name:"zh_TW/guide/hidden-features.md"},d=s("",4),r=[d];function o(_,i,n,h,u,l){return a(),t("div",null,r)}const f=e(c,[["render",o]]);export{p as __pageData,f as default}; diff --git a/assets/zh_TW_guide_how-to-build.md.32de6944.js b/assets/zh_TW_guide_how-to-build.md.32de6944.js new file mode 100644 index 000000000000..b8252d91fa5b --- /dev/null +++ b/assets/zh_TW_guide_how-to-build.md.32de6944.js @@ -0,0 +1,7 @@ +import{_ as s,o as a,c as n,Q as l}from"./chunks/framework.ec8f7e8e.js";const u=JSON.parse('{"title":"如何建置 KernelSU?","description":"","frontmatter":{},"headers":[],"relativePath":"zh_TW/guide/how-to-build.md","filePath":"zh_TW/guide/how-to-build.md"}'),o={name:"zh_TW/guide/how-to-build.md"},e=l(`

如何建置 KernelSU?

首先,您需要閱讀核心建置的 Android 官方文件:

  1. 建置核心
  2. 標準核心映像 (GKI) 發行組建

WARNING

此文件適用於 GKI 裝置,如果您是舊版核心,請參閱如何為非 GKI 裝置整合 KernelSU

建置核心

同步核心原始碼

sh
repo init -u https://android.googlesource.com/kernel/manifest
+mv <kernel_manifest.xml> .repo/manifests
+repo init -m manifest.xml
+repo sync
repo init -u https://android.googlesource.com/kernel/manifest
+mv <kernel_manifest.xml> .repo/manifests
+repo init -m manifest.xml
+repo sync

<kernel_manifest.xml> 是一個可以唯一確定組建的資訊清單檔案,您可以使用這個資訊清單進行可重新預測的組建。您需要從標準核心映像 (GKI) 發行組建 下載資訊清單檔案

建置

請先查看官方文件

例如,我們需要建置 aarch64 核心映像:

sh
LTO=thin BUILD_CONFIG=common/build.config.gki.aarch64 build/build.sh
LTO=thin BUILD_CONFIG=common/build.config.gki.aarch64 build/build.sh

不要忘記新增 LTO=thin,否則,如果您的電腦記憶體小於 24GB,建置可能會失敗。

從 Android 13 開始,核心由 bazel 建置:

sh
tools/bazel build --config=fast //common:kernel_aarch64_dist
tools/bazel build --config=fast //common:kernel_aarch64_dist

使用 KernelSU 建置核心

如果您可以成功建置核心,那麼建置 KernelSU 就會非常輕鬆,依自己的需求在核心原始碼根目錄中執行以下任一命令:

  • 最新 tag (穩定版本)
sh
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -
  • main 分支 (開發版本)
sh
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -s main
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -s main
  • 選取 tag (例如 v0.5.2)
sh
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -s v0.5.2
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -s v0.5.2

然後重新建置核心,您將會得到一個帶有 KernelSU 的核心映像!

`,24),p=[e];function t(r,c,i,y,d,E){return a(),n("div",null,p)}const F=s(o,[["render",t]]);export{u as __pageData,F as default}; diff --git a/assets/zh_TW_guide_how-to-build.md.32de6944.lean.js b/assets/zh_TW_guide_how-to-build.md.32de6944.lean.js new file mode 100644 index 000000000000..10d78e42a9da --- /dev/null +++ b/assets/zh_TW_guide_how-to-build.md.32de6944.lean.js @@ -0,0 +1 @@ +import{_ as s,o as a,c as n,Q as l}from"./chunks/framework.ec8f7e8e.js";const u=JSON.parse('{"title":"如何建置 KernelSU?","description":"","frontmatter":{},"headers":[],"relativePath":"zh_TW/guide/how-to-build.md","filePath":"zh_TW/guide/how-to-build.md"}'),o={name:"zh_TW/guide/how-to-build.md"},e=l("",24),p=[e];function t(r,c,i,y,d,E){return a(),n("div",null,p)}const F=s(o,[["render",t]]);export{u as __pageData,F as default}; diff --git a/assets/zh_TW_guide_how-to-build.md.ef330d07.js b/assets/zh_TW_guide_how-to-build.md.ef330d07.js deleted file mode 100644 index 56dfee1b54c3..000000000000 --- a/assets/zh_TW_guide_how-to-build.md.ef330d07.js +++ /dev/null @@ -1,4 +0,0 @@ -import{_ as s,o as a,c as l,O as n}from"./chunks/framework.43781440.js";const u=JSON.parse('{"title":"如何建置 KernelSU?","description":"","frontmatter":{},"headers":[],"relativePath":"zh_TW/guide/how-to-build.md","filePath":"zh_TW/guide/how-to-build.md"}'),e={name:"zh_TW/guide/how-to-build.md"},o=n(`

如何建置 KernelSU?

首先,您需要閱讀核心建置的 Android 官方文件:

  1. 建置核心
  2. 標準核心映像 (GKI) 發行組建

WARNING

此文件適用於 GKI 裝置,如果您是舊版核心,請參閱如何為非 GKI 裝置整合 KernelSU

建置核心

同步核心原始碼

sh
repo init -u https://android.googlesource.com/kernel/manifest
-mv <kernel_manifest.xml> .repo/manifests
-repo init -m manifest.xml
-repo sync

<kernel_manifest.xml> 是一個可以唯一確定組建的資訊清單檔案,您可以使用這個資訊清單進行可重新預測的組建。您需要從標準核心映像 (GKI) 發行組建 下載資訊清單檔案

建置

請先查看官方文件

例如,我們需要建置 aarch64 核心映像:

sh
LTO=thin BUILD_CONFIG=common/build.config.gki.aarch64 build/build.sh

不要忘記新增 LTO=thin,否則,如果您的電腦記憶體小於 24GB,建置可能會失敗。

從 Android 13 開始,核心由 bazel 建置:

sh
tools/bazel build --config=fast //common:kernel_aarch64_dist

使用 KernelSU 建置核心

如果您可以成功建置核心,那麼建置 KernelSU 就會非常輕鬆,依自己的需求在核心原始碼根目錄中執行以下任一命令:

  • 最新 tag (穩定版本)
sh
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -
  • main 分支 (開發版本)
sh
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -s main
  • 選取 tag (例如 v0.5.2)
sh
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -s v0.5.2

然後重新建置核心,您將會得到一個帶有 KernelSU 的核心映像!

`,24),p=[o];function t(r,c,i,C,d,h){return a(),l("div",null,p)}const D=s(e,[["render",t]]);export{u as __pageData,D as default}; diff --git a/assets/zh_TW_guide_how-to-build.md.ef330d07.lean.js b/assets/zh_TW_guide_how-to-build.md.ef330d07.lean.js deleted file mode 100644 index 6656bfbbe07e..000000000000 --- a/assets/zh_TW_guide_how-to-build.md.ef330d07.lean.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as s,o as a,c as l,O as n}from"./chunks/framework.43781440.js";const u=JSON.parse('{"title":"如何建置 KernelSU?","description":"","frontmatter":{},"headers":[],"relativePath":"zh_TW/guide/how-to-build.md","filePath":"zh_TW/guide/how-to-build.md"}'),e={name:"zh_TW/guide/how-to-build.md"},o=n("",24),p=[o];function t(r,c,i,C,d,h){return a(),l("div",null,p)}const D=s(e,[["render",t]]);export{u as __pageData,D as default}; diff --git a/assets/zh_TW_guide_how-to-integrate-for-non-gki.md.035c5fb8.js b/assets/zh_TW_guide_how-to-integrate-for-non-gki.md.035c5fb8.js new file mode 100644 index 000000000000..46b5baa68c92 --- /dev/null +++ b/assets/zh_TW_guide_how-to-integrate-for-non-gki.md.035c5fb8.js @@ -0,0 +1,303 @@ +import{_ as s,o as n,c as a,Q as l}from"./chunks/framework.ec8f7e8e.js";const _=JSON.parse('{"title":"如何為非 GKI 核心整合 KernelSU","description":"","frontmatter":{},"headers":[],"relativePath":"zh_TW/guide/how-to-integrate-for-non-gki.md","filePath":"zh_TW/guide/how-to-integrate-for-non-gki.md"}'),p={name:"zh_TW/guide/how-to-integrate-for-non-gki.md"},e=l(`

如何為非 GKI 核心整合 KernelSU

KernelSU 可以被整合到非 GKI 核心中,現在它最低支援到核心 4.14 版本;理論上也可以支援更低的版本。

由於非 GKI 核心的片段化極其嚴重,因此通常沒有統一的方法來建置它,所以我們也無法為非 GKI 裝置提供 Boot 映像。但您完全可以自行整合 KernelSU 並建置核心以繼續使用。

首先,您必須有能力從您裝置的核心原始碼建置出一個可以開機並且能夠正常使用的核心,如果核心並非開放原始碼,這通常難以做到。

如果您已經做好了上述準備,那有兩個方法來將 KernelSU 整合至您的核心之中。

  1. 藉助 kprobe 自動整合
  2. 手動修改核心原始碼

使用 kprobe 整合

KernelSU 使用 kprobe 機制來處理核心的相關 hook,如果 kprobe 可以在您建置的核心中正常運作,那麼建議使用這個方法進行整合。

首先,把 KernelSU 新增至您的核心來源樹狀結構,再核心的根目錄執行以下命令:

  • 最新 tag (稳定版本)
sh
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -
  • main 分支(開發版本)
sh
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -s main
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -s main
  • 選取 tag (例如 v0.5.2)
sh
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -s v0.5.2
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -s v0.5.2

然後,您需要檢查您的核心是否啟用 kprobe 相關組態,如果未啟用,則需要新增以下組態:

CONFIG_KPROBES=y
+CONFIG_HAVE_KPROBES=y
+CONFIG_KPROBE_EVENTS=y
CONFIG_KPROBES=y
+CONFIG_HAVE_KPROBES=y
+CONFIG_KPROBE_EVENTS=y

最後,重新建置您的核心即可。

如果您發現 KPROBES 仍未生效,很有可能是因為它的相依性 CONFIG_MODULES 並未被啟用 (如果還是未生效請輸入 make menuconfig 搜尋 KPROBES 的其他相依性並啟用)

如果您在整合 KernelSU 之後手機無法啟動,那麼很可能您的核心中 kprobe 無法正常運作,您需要修正這個錯誤,或者使用第二種方法。

如何檢查 kprobe 是否損毀?

KernelSU/kernel/ksu.c 中的 ksu_enable_sucompat()ksu_enable_ksud() 取消註解,如果正常開機,即 kprobe 已損毀;或者您可以手動嘗試使用 kprobe 功能,如果不正常,手機會直接重新啟動。

手動修改核心原始碼

如果 kprobe 無法正常運作 (可能是上游的錯誤或核心版本過低),那您可以嘗試這種方法:

首先,將 KernelSU 新增至您的原始碼樹狀結構,再核心的根目錄執行以下命令:

sh
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -

然後,手動修改核心原始碼,您可以參閱下方的 patch:

diff
diff --git a/fs/exec.c b/fs/exec.c
+index ac59664eaecf..bdd585e1d2cc 100644
+--- a/fs/exec.c
++++ b/fs/exec.c
+@@ -1890,11 +1890,14 @@ static int __do_execve_file(int fd, struct filename *filename,
+ 	return retval;
+ }
+ 
++extern bool ksu_execveat_hook __read_mostly;
++extern int ksu_handle_execveat(int *fd, struct filename **filename_ptr, void *argv,
++			void *envp, int *flags);
++extern int ksu_handle_execveat_sucompat(int *fd, struct filename **filename_ptr,
++				 void *argv, void *envp, int *flags);
+ static int do_execveat_common(int fd, struct filename *filename,
+ 			      struct user_arg_ptr argv,
+ 			      struct user_arg_ptr envp,
+ 			      int flags)
+ {
++	if (unlikely(ksu_execveat_hook))
++		ksu_handle_execveat(&fd, &filename, &argv, &envp, &flags);
++	else
++		ksu_handle_execveat_sucompat(&fd, &filename, &argv, &envp, &flags);
+ 	return __do_execve_file(fd, filename, argv, envp, flags, NULL);
+ }
+ 
+diff --git a/fs/open.c b/fs/open.c
+index 05036d819197..965b84d486b8 100644
+--- a/fs/open.c
++++ b/fs/open.c
+@@ -348,6 +348,8 @@ SYSCALL_DEFINE4(fallocate, int, fd, int, mode, loff_t, offset, loff_t, len)
+ 	return ksys_fallocate(fd, mode, offset, len);
+ }
+ 
++extern int ksu_handle_faccessat(int *dfd, const char __user **filename_user, int *mode,
++			 int *flags);
+ /*
+  * access() needs to use the real uid/gid, not the effective uid/gid.
+  * We do this by temporarily clearing all FS-related capabilities and
+@@ -355,6 +357,7 @@ SYSCALL_DEFINE4(fallocate, int, fd, int, mode, loff_t, offset, loff_t, len)
+  */
+ long do_faccessat(int dfd, const char __user *filename, int mode)
+ {
++	ksu_handle_faccessat(&dfd, &filename, &mode, NULL);
+ 	const struct cred *old_cred;
+ 	struct cred *override_cred;
+ 	struct path path;
+diff --git a/fs/read_write.c b/fs/read_write.c
+index 650fc7e0f3a6..55be193913b6 100644
+--- a/fs/read_write.c
++++ b/fs/read_write.c
+@@ -434,10 +434,14 @@ ssize_t kernel_read(struct file *file, void *buf, size_t count, loff_t *pos)
+ }
+ EXPORT_SYMBOL(kernel_read);
+ 
++extern bool ksu_vfs_read_hook __read_mostly;
++extern int ksu_handle_vfs_read(struct file **file_ptr, char __user **buf_ptr,
++			size_t *count_ptr, loff_t **pos);
+ ssize_t vfs_read(struct file *file, char __user *buf, size_t count, loff_t *pos)
+ {
+ 	ssize_t ret;
+ 
++	if (unlikely(ksu_vfs_read_hook))
++		ksu_handle_vfs_read(&file, &buf, &count, &pos);
++
+ 	if (!(file->f_mode & FMODE_READ))
+ 		return -EBADF;
+ 	if (!(file->f_mode & FMODE_CAN_READ))
+diff --git a/fs/stat.c b/fs/stat.c
+index 376543199b5a..82adcef03ecc 100644
+--- a/fs/stat.c
++++ b/fs/stat.c
+@@ -148,6 +148,8 @@ int vfs_statx_fd(unsigned int fd, struct kstat *stat,
+ }
+ EXPORT_SYMBOL(vfs_statx_fd);
+ 
++extern int ksu_handle_stat(int *dfd, const char __user **filename_user, int *flags);
++
+ /**
+  * vfs_statx - Get basic and extra attributes by filename
+  * @dfd: A file descriptor representing the base dir for a relative filename
+@@ -170,6 +172,7 @@ int vfs_statx(int dfd, const char __user *filename, int flags,
+ 	int error = -EINVAL;
+ 	unsigned int lookup_flags = LOOKUP_FOLLOW | LOOKUP_AUTOMOUNT;
+ 
++	ksu_handle_stat(&dfd, &filename, &flags);
+ 	if ((flags & ~(AT_SYMLINK_NOFOLLOW | AT_NO_AUTOMOUNT |
+ 		       AT_EMPTY_PATH | KSTAT_QUERY_FLAGS)) != 0)
+ 		return -EINVAL;
diff --git a/fs/exec.c b/fs/exec.c
+index ac59664eaecf..bdd585e1d2cc 100644
+--- a/fs/exec.c
++++ b/fs/exec.c
+@@ -1890,11 +1890,14 @@ static int __do_execve_file(int fd, struct filename *filename,
+ 	return retval;
+ }
+ 
++extern bool ksu_execveat_hook __read_mostly;
++extern int ksu_handle_execveat(int *fd, struct filename **filename_ptr, void *argv,
++			void *envp, int *flags);
++extern int ksu_handle_execveat_sucompat(int *fd, struct filename **filename_ptr,
++				 void *argv, void *envp, int *flags);
+ static int do_execveat_common(int fd, struct filename *filename,
+ 			      struct user_arg_ptr argv,
+ 			      struct user_arg_ptr envp,
+ 			      int flags)
+ {
++	if (unlikely(ksu_execveat_hook))
++		ksu_handle_execveat(&fd, &filename, &argv, &envp, &flags);
++	else
++		ksu_handle_execveat_sucompat(&fd, &filename, &argv, &envp, &flags);
+ 	return __do_execve_file(fd, filename, argv, envp, flags, NULL);
+ }
+ 
+diff --git a/fs/open.c b/fs/open.c
+index 05036d819197..965b84d486b8 100644
+--- a/fs/open.c
++++ b/fs/open.c
+@@ -348,6 +348,8 @@ SYSCALL_DEFINE4(fallocate, int, fd, int, mode, loff_t, offset, loff_t, len)
+ 	return ksys_fallocate(fd, mode, offset, len);
+ }
+ 
++extern int ksu_handle_faccessat(int *dfd, const char __user **filename_user, int *mode,
++			 int *flags);
+ /*
+  * access() needs to use the real uid/gid, not the effective uid/gid.
+  * We do this by temporarily clearing all FS-related capabilities and
+@@ -355,6 +357,7 @@ SYSCALL_DEFINE4(fallocate, int, fd, int, mode, loff_t, offset, loff_t, len)
+  */
+ long do_faccessat(int dfd, const char __user *filename, int mode)
+ {
++	ksu_handle_faccessat(&dfd, &filename, &mode, NULL);
+ 	const struct cred *old_cred;
+ 	struct cred *override_cred;
+ 	struct path path;
+diff --git a/fs/read_write.c b/fs/read_write.c
+index 650fc7e0f3a6..55be193913b6 100644
+--- a/fs/read_write.c
++++ b/fs/read_write.c
+@@ -434,10 +434,14 @@ ssize_t kernel_read(struct file *file, void *buf, size_t count, loff_t *pos)
+ }
+ EXPORT_SYMBOL(kernel_read);
+ 
++extern bool ksu_vfs_read_hook __read_mostly;
++extern int ksu_handle_vfs_read(struct file **file_ptr, char __user **buf_ptr,
++			size_t *count_ptr, loff_t **pos);
+ ssize_t vfs_read(struct file *file, char __user *buf, size_t count, loff_t *pos)
+ {
+ 	ssize_t ret;
+ 
++	if (unlikely(ksu_vfs_read_hook))
++		ksu_handle_vfs_read(&file, &buf, &count, &pos);
++
+ 	if (!(file->f_mode & FMODE_READ))
+ 		return -EBADF;
+ 	if (!(file->f_mode & FMODE_CAN_READ))
+diff --git a/fs/stat.c b/fs/stat.c
+index 376543199b5a..82adcef03ecc 100644
+--- a/fs/stat.c
++++ b/fs/stat.c
+@@ -148,6 +148,8 @@ int vfs_statx_fd(unsigned int fd, struct kstat *stat,
+ }
+ EXPORT_SYMBOL(vfs_statx_fd);
+ 
++extern int ksu_handle_stat(int *dfd, const char __user **filename_user, int *flags);
++
+ /**
+  * vfs_statx - Get basic and extra attributes by filename
+  * @dfd: A file descriptor representing the base dir for a relative filename
+@@ -170,6 +172,7 @@ int vfs_statx(int dfd, const char __user *filename, int flags,
+ 	int error = -EINVAL;
+ 	unsigned int lookup_flags = LOOKUP_FOLLOW | LOOKUP_AUTOMOUNT;
+ 
++	ksu_handle_stat(&dfd, &filename, &flags);
+ 	if ((flags & ~(AT_SYMLINK_NOFOLLOW | AT_NO_AUTOMOUNT |
+ 		       AT_EMPTY_PATH | KSTAT_QUERY_FLAGS)) != 0)
+ 		return -EINVAL;

主要修改四個項目:

  1. do_faccessat,通常位於 fs/open.c
  2. do_execveat_common,通常位於 fs/exec.c
  3. vfs_read,通常位於 fs/read_write.c
  4. vfs_statx,通常位於 fs/stat.c

如果您的核心沒有 vfs_statx,使用 vfs_fstatat 將其取代:

diff
diff --git a/fs/stat.c b/fs/stat.c
+index 068fdbcc9e26..5348b7bb9db2 100644
+--- a/fs/stat.c
++++ b/fs/stat.c
+@@ -87,6 +87,8 @@ int vfs_fstat(unsigned int fd, struct kstat *stat)
+ }
+ EXPORT_SYMBOL(vfs_fstat);
+ 
++extern int ksu_handle_stat(int *dfd, const char __user **filename_user, int *flags);
++
+ int vfs_fstatat(int dfd, const char __user *filename, struct kstat *stat,
+ 		int flag)
+ {
+@@ -94,6 +96,8 @@ int vfs_fstatat(int dfd, const char __user *filename, struct kstat *stat,
+ 	int error = -EINVAL;
+ 	unsigned int lookup_flags = 0;
+ 
++	ksu_handle_stat(&dfd, &filename, &flag);
++
+ 	if ((flag & ~(AT_SYMLINK_NOFOLLOW | AT_NO_AUTOMOUNT |
+ 		      AT_EMPTY_PATH)) != 0)
+ 		goto out;
diff --git a/fs/stat.c b/fs/stat.c
+index 068fdbcc9e26..5348b7bb9db2 100644
+--- a/fs/stat.c
++++ b/fs/stat.c
+@@ -87,6 +87,8 @@ int vfs_fstat(unsigned int fd, struct kstat *stat)
+ }
+ EXPORT_SYMBOL(vfs_fstat);
+ 
++extern int ksu_handle_stat(int *dfd, const char __user **filename_user, int *flags);
++
+ int vfs_fstatat(int dfd, const char __user *filename, struct kstat *stat,
+ 		int flag)
+ {
+@@ -94,6 +96,8 @@ int vfs_fstatat(int dfd, const char __user *filename, struct kstat *stat,
+ 	int error = -EINVAL;
+ 	unsigned int lookup_flags = 0;
+ 
++	ksu_handle_stat(&dfd, &filename, &flag);
++
+ 	if ((flag & ~(AT_SYMLINK_NOFOLLOW | AT_NO_AUTOMOUNT |
+ 		      AT_EMPTY_PATH)) != 0)
+ 		goto out;

對於早於 4.17 的核心,如果沒有 do_faccessat,可以直接找到 faccessat 系統呼叫的定義並進行修改:

diff
diff --git a/fs/open.c b/fs/open.c
+index 2ff887661237..e758d7db7663 100644
+--- a/fs/open.c
++++ b/fs/open.c
+@@ -355,6 +355,9 @@ SYSCALL_DEFINE4(fallocate, int, fd, int, mode, loff_t, offset, loff_t, len)
+ 	return error;
+ }
+ 
++extern int ksu_handle_faccessat(int *dfd, const char __user **filename_user, int *mode,
++			        int *flags);
++
+ /*
+  * access() needs to use the real uid/gid, not the effective uid/gid.
+  * We do this by temporarily clearing all FS-related capabilities and
+@@ -370,6 +373,8 @@ SYSCALL_DEFINE3(faccessat, int, dfd, const char __user *, filename, int, mode)
+ 	int res;
+ 	unsigned int lookup_flags = LOOKUP_FOLLOW;
+ 
++	ksu_handle_faccessat(&dfd, &filename, &mode, NULL);
++
+ 	if (mode & ~S_IRWXO)	/* where's F_OK, X_OK, W_OK, R_OK? */
+ 		return -EINVAL;
diff --git a/fs/open.c b/fs/open.c
+index 2ff887661237..e758d7db7663 100644
+--- a/fs/open.c
++++ b/fs/open.c
+@@ -355,6 +355,9 @@ SYSCALL_DEFINE4(fallocate, int, fd, int, mode, loff_t, offset, loff_t, len)
+ 	return error;
+ }
+ 
++extern int ksu_handle_faccessat(int *dfd, const char __user **filename_user, int *mode,
++			        int *flags);
++
+ /*
+  * access() needs to use the real uid/gid, not the effective uid/gid.
+  * We do this by temporarily clearing all FS-related capabilities and
+@@ -370,6 +373,8 @@ SYSCALL_DEFINE3(faccessat, int, dfd, const char __user *, filename, int, mode)
+ 	int res;
+ 	unsigned int lookup_flags = LOOKUP_FOLLOW;
+ 
++	ksu_handle_faccessat(&dfd, &filename, &mode, NULL);
++
+ 	if (mode & ~S_IRWXO)	/* where's F_OK, X_OK, W_OK, R_OK? */
+ 		return -EINVAL;

若要啟用 KernelSU 內建的安全模式,您還需要修改 drivers/input/input.c 中的 input_handle_event 方法:

TIP

強烈建議啟用此功能,如果遇到開機迴圈,這將會非常有用!

diff
diff --git a/drivers/input/input.c b/drivers/input/input.c
+index 45306f9ef247..815091ebfca4 100755
+--- a/drivers/input/input.c
++++ b/drivers/input/input.c
+@@ -367,10 +367,13 @@ static int input_get_disposition(struct input_dev *dev,
+ 	return disposition;
+ }
+ 
++extern bool ksu_input_hook __read_mostly;
++extern int ksu_handle_input_handle_event(unsigned int *type, unsigned int *code, int *value);
++
+ static void input_handle_event(struct input_dev *dev,
+ 			       unsigned int type, unsigned int code, int value)
+ {
+	int disposition = input_get_disposition(dev, type, code, &value);
++
++	if (unlikely(ksu_input_hook))
++		ksu_handle_input_handle_event(&type, &code, &value);
+ 
+ 	if (disposition != INPUT_IGNORE_EVENT && type != EV_SYN)
+ 		add_input_randomness(type, code, value);
diff --git a/drivers/input/input.c b/drivers/input/input.c
+index 45306f9ef247..815091ebfca4 100755
+--- a/drivers/input/input.c
++++ b/drivers/input/input.c
+@@ -367,10 +367,13 @@ static int input_get_disposition(struct input_dev *dev,
+ 	return disposition;
+ }
+ 
++extern bool ksu_input_hook __read_mostly;
++extern int ksu_handle_input_handle_event(unsigned int *type, unsigned int *code, int *value);
++
+ static void input_handle_event(struct input_dev *dev,
+ 			       unsigned int type, unsigned int code, int value)
+ {
+	int disposition = input_get_disposition(dev, type, code, &value);
++
++	if (unlikely(ksu_input_hook))
++		ksu_handle_input_handle_event(&type, &code, &value);
+ 
+ 	if (disposition != INPUT_IGNORE_EVENT && type != EV_SYN)
+ 		add_input_randomness(type, code, value);

最後,再次建置您的核心,KernelSU 將會如期運作。

`,37),t=[e];function o(c,i,r,E,d,f){return n(),a("div",null,t)}const u=s(p,[["render",o]]);export{_ as __pageData,u as default}; diff --git a/assets/zh_TW_guide_how-to-integrate-for-non-gki.md.035c5fb8.lean.js b/assets/zh_TW_guide_how-to-integrate-for-non-gki.md.035c5fb8.lean.js new file mode 100644 index 000000000000..6597d61da31a --- /dev/null +++ b/assets/zh_TW_guide_how-to-integrate-for-non-gki.md.035c5fb8.lean.js @@ -0,0 +1 @@ +import{_ as s,o as n,c as a,Q as l}from"./chunks/framework.ec8f7e8e.js";const _=JSON.parse('{"title":"如何為非 GKI 核心整合 KernelSU","description":"","frontmatter":{},"headers":[],"relativePath":"zh_TW/guide/how-to-integrate-for-non-gki.md","filePath":"zh_TW/guide/how-to-integrate-for-non-gki.md"}'),p={name:"zh_TW/guide/how-to-integrate-for-non-gki.md"},e=l("",37),t=[e];function o(c,i,r,E,d,f){return n(),a("div",null,t)}const u=s(p,[["render",o]]);export{_ as __pageData,u as default}; diff --git a/assets/zh_TW_guide_how-to-integrate-for-non-gki.md.e3487c4d.js b/assets/zh_TW_guide_how-to-integrate-for-non-gki.md.e3487c4d.js deleted file mode 100644 index 6200e7bc5980..000000000000 --- a/assets/zh_TW_guide_how-to-integrate-for-non-gki.md.e3487c4d.js +++ /dev/null @@ -1,152 +0,0 @@ -import{_ as s,o as n,c as a,O as l}from"./chunks/framework.43781440.js";const d=JSON.parse('{"title":"如何為非 GKI 核心整合 KernelSU","description":"","frontmatter":{},"headers":[],"relativePath":"zh_TW/guide/how-to-integrate-for-non-gki.md","filePath":"zh_TW/guide/how-to-integrate-for-non-gki.md"}'),p={name:"zh_TW/guide/how-to-integrate-for-non-gki.md"},e=l(`

如何為非 GKI 核心整合 KernelSU

KernelSU 可以被整合到非 GKI 核心中,現在它最低支援到核心 4.14 版本;理論上也可以支援更低的版本。

由於非 GKI 核心的片段化極其嚴重,因此通常沒有統一的方法來建置它,所以我們也無法為非 GKI 裝置提供 Boot 映像。但您完全可以自行整合 KernelSU 並建置核心以繼續使用。

首先,您必須有能力從您裝置的核心原始碼建置出一個可以開機並且能夠正常使用的核心,如果核心並非開放原始碼,這通常難以做到。

如果您已經做好了上述準備,那有兩個方法來將 KernelSU 整合至您的核心之中。

  1. 藉助 kprobe 自動整合
  2. 手動修改核心原始碼

使用 kprobe 整合

KernelSU 使用 kprobe 機制來處理核心的相關 hook,如果 kprobe 可以在您建置的核心中正常運作,那麼建議使用這個方法進行整合。

首先,把 KernelSU 新增至您的核心來源樹狀結構,再核心的根目錄執行以下命令:

  • 最新 tag (稳定版本)
sh
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -
  • main 分支(開發版本)
sh
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -s main
  • 選取 tag (例如 v0.5.2)
sh
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -s v0.5.2

然後,您需要檢查您的核心是否啟用 kprobe 相關組態,如果未啟用,則需要新增以下組態:

CONFIG_KPROBES=y
-CONFIG_HAVE_KPROBES=y
-CONFIG_KPROBE_EVENTS=y

最後,重新建置您的核心即可。

如果您發現 KPROBES 仍未生效,很有可能是因為它的相依性 CONFIG_MODULES 並未被啟用 (如果還是未生效請輸入 make menuconfig 搜尋 KPROBES 的其他相依性並啟用)

如果您在整合 KernelSU 之後手機無法啟動,那麼很可能您的核心中 kprobe 無法正常運作,您需要修正這個錯誤,或者使用第二種方法。

如何檢查 kprobe 是否損毀?

KernelSU/kernel/ksu.c 中的 ksu_enable_sucompat()ksu_enable_ksud() 取消註解,如果正常開機,即 kprobe 已損毀;或者您可以手動嘗試使用 kprobe 功能,如果不正常,手機會直接重新啟動。

手動修改核心原始碼

如果 kprobe 無法正常運作 (可能是上游的錯誤或核心版本過低),那您可以嘗試這種方法:

首先,將 KernelSU 新增至您的原始碼樹狀結構,再核心的根目錄執行以下命令:

sh
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -

然後,手動修改核心原始碼,您可以參閱下方的 patch:

diff
diff --git a/fs/exec.c b/fs/exec.c
-index ac59664eaecf..bdd585e1d2cc 100644
---- a/fs/exec.c
-+++ b/fs/exec.c
-@@ -1890,11 +1890,14 @@ static int __do_execve_file(int fd, struct filename *filename,
- 	return retval;
- }
- 
-+extern bool ksu_execveat_hook __read_mostly;
-+extern int ksu_handle_execveat(int *fd, struct filename **filename_ptr, void *argv,
-+			void *envp, int *flags);
-+extern int ksu_handle_execveat_sucompat(int *fd, struct filename **filename_ptr,
-+				 void *argv, void *envp, int *flags);
- static int do_execveat_common(int fd, struct filename *filename,
- 			      struct user_arg_ptr argv,
- 			      struct user_arg_ptr envp,
- 			      int flags)
- {
-+	if (unlikely(ksu_execveat_hook))
-+		ksu_handle_execveat(&fd, &filename, &argv, &envp, &flags);
-+	else
-+		ksu_handle_execveat_sucompat(&fd, &filename, &argv, &envp, &flags);
- 	return __do_execve_file(fd, filename, argv, envp, flags, NULL);
- }
- 
-diff --git a/fs/open.c b/fs/open.c
-index 05036d819197..965b84d486b8 100644
---- a/fs/open.c
-+++ b/fs/open.c
-@@ -348,6 +348,8 @@ SYSCALL_DEFINE4(fallocate, int, fd, int, mode, loff_t, offset, loff_t, len)
- 	return ksys_fallocate(fd, mode, offset, len);
- }
- 
-+extern int ksu_handle_faccessat(int *dfd, const char __user **filename_user, int *mode,
-+			 int *flags);
- /*
-  * access() needs to use the real uid/gid, not the effective uid/gid.
-  * We do this by temporarily clearing all FS-related capabilities and
-@@ -355,6 +357,7 @@ SYSCALL_DEFINE4(fallocate, int, fd, int, mode, loff_t, offset, loff_t, len)
-  */
- long do_faccessat(int dfd, const char __user *filename, int mode)
- {
-+	ksu_handle_faccessat(&dfd, &filename, &mode, NULL);
- 	const struct cred *old_cred;
- 	struct cred *override_cred;
- 	struct path path;
-diff --git a/fs/read_write.c b/fs/read_write.c
-index 650fc7e0f3a6..55be193913b6 100644
---- a/fs/read_write.c
-+++ b/fs/read_write.c
-@@ -434,10 +434,14 @@ ssize_t kernel_read(struct file *file, void *buf, size_t count, loff_t *pos)
- }
- EXPORT_SYMBOL(kernel_read);
- 
-+extern bool ksu_vfs_read_hook __read_mostly;
-+extern int ksu_handle_vfs_read(struct file **file_ptr, char __user **buf_ptr,
-+			size_t *count_ptr, loff_t **pos);
- ssize_t vfs_read(struct file *file, char __user *buf, size_t count, loff_t *pos)
- {
- 	ssize_t ret;
- 
-+	if (unlikely(ksu_vfs_read_hook))
-+		ksu_handle_vfs_read(&file, &buf, &count, &pos);
-+
- 	if (!(file->f_mode & FMODE_READ))
- 		return -EBADF;
- 	if (!(file->f_mode & FMODE_CAN_READ))
-diff --git a/fs/stat.c b/fs/stat.c
-index 376543199b5a..82adcef03ecc 100644
---- a/fs/stat.c
-+++ b/fs/stat.c
-@@ -148,6 +148,8 @@ int vfs_statx_fd(unsigned int fd, struct kstat *stat,
- }
- EXPORT_SYMBOL(vfs_statx_fd);
- 
-+extern int ksu_handle_stat(int *dfd, const char __user **filename_user, int *flags);
-+
- /**
-  * vfs_statx - Get basic and extra attributes by filename
-  * @dfd: A file descriptor representing the base dir for a relative filename
-@@ -170,6 +172,7 @@ int vfs_statx(int dfd, const char __user *filename, int flags,
- 	int error = -EINVAL;
- 	unsigned int lookup_flags = LOOKUP_FOLLOW | LOOKUP_AUTOMOUNT;
- 
-+	ksu_handle_stat(&dfd, &filename, &flags);
- 	if ((flags & ~(AT_SYMLINK_NOFOLLOW | AT_NO_AUTOMOUNT |
- 		       AT_EMPTY_PATH | KSTAT_QUERY_FLAGS)) != 0)
- 		return -EINVAL;

主要修改四個項目:

  1. do_faccessat,通常位於 fs/open.c
  2. do_execveat_common,通常位於 fs/exec.c
  3. vfs_read,通常位於 fs/read_write.c
  4. vfs_statx,通常位於 fs/stat.c

如果您的核心沒有 vfs_statx,使用 vfs_fstatat 將其取代:

diff
diff --git a/fs/stat.c b/fs/stat.c
-index 068fdbcc9e26..5348b7bb9db2 100644
---- a/fs/stat.c
-+++ b/fs/stat.c
-@@ -87,6 +87,8 @@ int vfs_fstat(unsigned int fd, struct kstat *stat)
- }
- EXPORT_SYMBOL(vfs_fstat);
- 
-+extern int ksu_handle_stat(int *dfd, const char __user **filename_user, int *flags);
-+
- int vfs_fstatat(int dfd, const char __user *filename, struct kstat *stat,
- 		int flag)
- {
-@@ -94,6 +96,8 @@ int vfs_fstatat(int dfd, const char __user *filename, struct kstat *stat,
- 	int error = -EINVAL;
- 	unsigned int lookup_flags = 0;
- 
-+	ksu_handle_stat(&dfd, &filename, &flag);
-+
- 	if ((flag & ~(AT_SYMLINK_NOFOLLOW | AT_NO_AUTOMOUNT |
- 		      AT_EMPTY_PATH)) != 0)
- 		goto out;

對於早於 4.17 的核心,如果沒有 do_faccessat,可以直接找到 faccessat 系統呼叫的定義並進行修改:

diff
diff --git a/fs/open.c b/fs/open.c
-index 2ff887661237..e758d7db7663 100644
---- a/fs/open.c
-+++ b/fs/open.c
-@@ -355,6 +355,9 @@ SYSCALL_DEFINE4(fallocate, int, fd, int, mode, loff_t, offset, loff_t, len)
- 	return error;
- }
- 
-+extern int ksu_handle_faccessat(int *dfd, const char __user **filename_user, int *mode,
-+			        int *flags);
-+
- /*
-  * access() needs to use the real uid/gid, not the effective uid/gid.
-  * We do this by temporarily clearing all FS-related capabilities and
-@@ -370,6 +373,8 @@ SYSCALL_DEFINE3(faccessat, int, dfd, const char __user *, filename, int, mode)
- 	int res;
- 	unsigned int lookup_flags = LOOKUP_FOLLOW;
- 
-+	ksu_handle_faccessat(&dfd, &filename, &mode, NULL);
-+
- 	if (mode & ~S_IRWXO)	/* where's F_OK, X_OK, W_OK, R_OK? */
- 		return -EINVAL;

若要啟用 KernelSU 內建的安全模式,您還需要修改 drivers/input/input.c 中的 input_handle_event 方法:

TIP

強烈建議啟用此功能,如果遇到開機迴圈,這將會非常有用!

diff
diff --git a/drivers/input/input.c b/drivers/input/input.c
-index 45306f9ef247..815091ebfca4 100755
---- a/drivers/input/input.c
-+++ b/drivers/input/input.c
-@@ -367,10 +367,13 @@ static int input_get_disposition(struct input_dev *dev,
- 	return disposition;
- }
- 
-+extern bool ksu_input_hook __read_mostly;
-+extern int ksu_handle_input_handle_event(unsigned int *type, unsigned int *code, int *value);
-+
- static void input_handle_event(struct input_dev *dev,
- 			       unsigned int type, unsigned int code, int value)
- {
-	int disposition = input_get_disposition(dev, type, code, &value);
-+
-+	if (unlikely(ksu_input_hook))
-+		ksu_handle_input_handle_event(&type, &code, &value);
- 
- 	if (disposition != INPUT_IGNORE_EVENT && type != EV_SYN)
- 		add_input_randomness(type, code, value);

最後,再次建置您的核心,KernelSU 將會如期運作。

`,37),o=[e];function t(c,r,i,C,D,A){return n(),a("div",null,o)}const f=s(p,[["render",t]]);export{d as __pageData,f as default}; diff --git a/assets/zh_TW_guide_how-to-integrate-for-non-gki.md.e3487c4d.lean.js b/assets/zh_TW_guide_how-to-integrate-for-non-gki.md.e3487c4d.lean.js deleted file mode 100644 index b852b580956c..000000000000 --- a/assets/zh_TW_guide_how-to-integrate-for-non-gki.md.e3487c4d.lean.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as s,o as n,c as a,O as l}from"./chunks/framework.43781440.js";const d=JSON.parse('{"title":"如何為非 GKI 核心整合 KernelSU","description":"","frontmatter":{},"headers":[],"relativePath":"zh_TW/guide/how-to-integrate-for-non-gki.md","filePath":"zh_TW/guide/how-to-integrate-for-non-gki.md"}'),p={name:"zh_TW/guide/how-to-integrate-for-non-gki.md"},e=l("",37),o=[e];function t(c,r,i,C,D,A){return n(),a("div",null,o)}const f=s(p,[["render",t]]);export{d as __pageData,f as default}; diff --git a/assets/zh_TW_guide_installation.md.0f23d3bf.js b/assets/zh_TW_guide_installation.md.48e9e870.js similarity index 56% rename from assets/zh_TW_guide_installation.md.0f23d3bf.js rename to assets/zh_TW_guide_installation.md.48e9e870.js index a323662efd34..8d50184373e9 100644 --- a/assets/zh_TW_guide_installation.md.0f23d3bf.js +++ b/assets/zh_TW_guide_installation.md.48e9e870.js @@ -1,3 +1,5 @@ -import{_ as a,o as l,c as t,O as o,z as e,a as i}from"./chunks/framework.43781440.js";const y=JSON.parse('{"title":"安裝","description":"","frontmatter":{},"headers":[],"relativePath":"zh_TW/guide/installation.md","filePath":"zh_TW/guide/installation.md"}'),r={name:"zh_TW/guide/installation.md"},n=o(`

安裝

檢查您的裝置是否受支援

GitHub Releases酷安 下載 KernelSU 管理員應用程式,然後將應用程式安裝至裝置並開啟:

  • 如果應用程式顯示「不支援」,則表示您的裝置不支援 KernelSU,您需要自行編譯核心才能繼續使用,,KernelSU 官方也永遠不會為您提供一個可以刷新的 Boot 映像。
  • 如果應用程式顯示「未安裝」,那麼 KernelSU 支援您的裝置;可以進行下一步作業。

INFO

對於顯示「不支援」的裝置,這裡有一個非官方支援裝置清單,您可以使用這個清單裡的核心自行編譯。

備份您的原廠 boot.img

在進行刷新作業前,您必須預先備份您的原廠 boot.img。如果您在後續刷新作業中出現了任何問題,您都可以透過使用 Fastboot 刷新回到原廠 Boot 以還原系統。

WARNING

刷新作業可能會造成資料遺失,請確保做好這一步再繼續進行下一步作業!!必要時您還可以備份您手機的所有資料。

必要知識

ADB 和 Fastboot

預設狀況下,您將會使用 ADB 和 Fastboot 工具,如果您不知道它們,建議使用搜尋引擎先瞭解相關內容。

KMI

KMI 全稱 Kernel Module Interface,相同 KMI 的核心版本是相容的 這也是 GKI 中「標準」的涵義所在;反之,如果 KMI 不同,那麼這些核心之間無法彼此相容,刷新與您裝置 KMI 不同的核心映像可能會導致開機迴圈。

具體來講,對 GKI 的裝置,其核心版本格式應該如下:

txt
KernelRelease :=
-Version.PatchLevel.SubLevel-AndroidRelease-KmiGeneration-suffix
-w      .x         .y       -zzz           -k            -something

其中,w.x-zzz-k 為 KMI 版本。例如,一部裝置核心版本為 5.10.101-android12-9-g30979850fc20,那麼它的 KMI 為 5.10-android12-9;理論上刷新其他這個 KMI 的核心也能正常開機。

TIP

請注意,核心版本中的 SubLevel 並非 KMI 的一部分!也就是說 5.10.101-android12-9-g30979850fc205.10.137-android12-9-g30979850fc20 的 KMI 相同!

核心版本與 Android 版本

請注意:核心版本與 Android 版本並不一定相同!

如果您發現您的核心版本是 android12-5.10.101,然而您 Android 系統的版本為 Android 13 或者其他;請不要覺得奇怪,因為 Android 系統的版本與 Linux 核心的版本號碼並非一致;Linux 核心的版本號碼一般與裝置出廠時隨附的 Android 系統的版本一致,如果後續 Android 系統更新,核心版本一般不會發生變化。如果您需要刷新,請以核心版本為準!!

安裝簡介

KernelSU 的安裝方法有以下幾種,各自適用於不同的場景,請視需要選擇:

  1. 使用自訂 Recovery (如 TWRP) 安裝
  2. 使用核心刷新應用程式 (例如 Franco Kernel Manager) 安裝
  3. 使用 KernelSU 提供的 boot.img 透過 Fastboot 安裝
  4. 手動修補 boot.img 並安裝

使用自訂 Recovery 安裝

先決條件:您的裝置必須有自訂的 Recovery,例如 TWRP;如果沒有或者只有官方 Recovery,請使用其他方法。

步驟:

  1. 在 KernelSU 的 Release 頁面 下載與您手機版本相符的以 AnyKernel3 開頭的 Zip 套件;例如,手機核心版本為 android12-5.10.66,那麼您應該下載 AnyKernel3-android12-5.10.66_yyyy-MM.zip 這個檔案 (其中 yyyy 為年份,MM 為月份)。
  2. 重新開機手機至 TWRP。
  3. 使用 Adb 將 AnyKernel3-*.zip 放置到手機 /sdcard 然後在 TWRP 圖形使用者介面選擇並安裝;或者您也可以直接 adb sideload AnyKernel-*.zip 安裝。

PS. 這種方法適用於任何狀況下的安裝 (不限於初次安裝或後續更新),只要您用 TWRP 就可以進行作業。

使用核心刷新應用程式安裝

先決條件:您的裝置必須已經 Root。例如您已經安裝了 Magisk 並取得 Root 存取權,或者您已經安裝了舊版本的 KernelSU 需升級到其他版本的 KernelSU;如果您的裝置並未 Root,請嘗試其他方法。

步驟:

  1. 下載 AnyKernel3 的 Zip 檔案;請參閱 使用自訂 Recovery 安裝 章節的内容。
  2. 開啟核心刷新應用程式提供的 AnyKernel3 Zip 檔案進行刷新。

如果您先前並未使用過核心刷新應用程式,可以嘗試下面幾個方法:

  1. Kernel Flasher
  2. Franco Kernel Manager
  3. Ex Kernel Manager

PS. 這種方法在更新 KernelSU 時比較方便,無需電腦即可完成 (注意備份!)。

使用 KernelSU 提供的 boot.img 安裝

這種方法無需您有 TWRP,也無需您的手機有 Root 權限;適用於您初次安裝 KernelSU。

找到合適的 boot.img

KernelSU 為 GKI 裝置提供了標準 boot.img,您需要將 boot.img 刷新至裝置的 Boot 分割區。

您可以從 GitHub Release 下載 boot.img,請注意,您應該使用正確版本的 boot.img。例如,如果您的裝置顯示核心是 android12-5.10.101,需要下載 android-5.10.101_yyyy-MM.boot-<format>.img.

其中 <format> 指的是您的官方 boot.img 的核心壓縮格式,請檢查您原有 boot.img 的核心壓縮格式,您應該使用正確的格式,例如 lz4gz;如果使用不正確的壓縮格式,刷新 Boot 後可能無法開機。

INFO

  1. 您可以透過 magiskboot 以取得您的原始 Boot 的壓縮格式;當然,您也可以詢問與您相同型號的其他更有經驗的使用者。另外,核心的壓縮格式通常部會出現變更,如果您使用的某個壓縮格式成功開機,後續可以優先嘗試這個格式。
  2. 小米裝置通常 gz 或者 不壓縮
  3. Pixel 裝置有些特殊,請遵循下方的指示。

將 boot.img 刷新至裝置

使用 adb 連接您的裝置,然後執行 adb reboot bootloader 進入 fastboot 模式,然後使用此命令刷新 KernelSU:

sh
fastboot flash boot boot.img

INFO

如果您的裝置支援 fastboot boot,可以先使用 fastboot boot boot.img 來先嘗試使用 boot.img 開機進入系統,如果出現意外,重新啟動即可開機。

重新開機

刷新完成後,您應該重新啟動您的裝置:

sh
fastboot reboot

手動修補 boot.img

對於某些裝置來說,其 boot.img 格式並不是很常見,比如 lz4gz 和未壓縮;最典型的就是 Pixel,它的 boot.img 格式是 lz4_legacy 壓縮,ramdisk 可能是 gz 也可能是 lz4_legacy 壓縮;此時如果您直接刷新 KernelSU 提供的 boot.img,手機可能無法開機;這時,您可以透過手動修補 boot.img 來完成。

一般有兩種修補方法:

  1. Android-Image-Kitchen
  2. magiskboot

其中,Android-Image-Kitchen 適用於在電腦上作業,magiskboot 需要手機協作。

準備

  1. 取得您手機的原廠 boot.img;您可以聯絡您的裝置製造商,您也可能需要payload-dumper-go
  2. 下載 KernelSU 提供的與您的裝置 KMI 一致地 AnyKernel3 Zip 檔案 (可參閱 使用自訂 Recovery 安裝)。
  3. 解壓縮 AnyKernel3 Zip 檔案,取得其中的 Image 檔案,此檔案為 KernelSU 的核心檔案。

使用 Android-Image-Kitchen

  1. 下載 Android-Image-Kitchen 至您的電腦。
  2. 將手機原廠 boot.img 放置於 Android-Image-Kitchen 根目錄。
  3. 在 Android-Image-Kitchen 根目錄執行 ./unpackimg.sh boot.img;此命令會將 boot.img 解除封裝,您會得到一些檔案。
  4. split_img 目錄中的 boot.img-kernel 取代為您從 AnyKernel3 解壓縮出來的 Image (注意名稱變更為 boot.img-kernel)。
  5. 在 Android-Image-Kitchecn 根目錄執行 ./repackimg.sh;此時您會得到一個 image-new.img 檔案;使用此 boot.img 透過 fastboot 刷新即可 (刷新方法請參閱上一章節)。
`,58),s=e("h3",{id:"using",magiskboot:"",tabindex:"-1"},[i("使用 magiskboot "),e("a",{class:"header-anchor",href:"#using","aria-label":'Permalink to "使用 magiskboot {#using magiskboot}"'},"​")],-1),d=o('
  1. 在 Magisk 的 Release 頁面 下載最新的 Magisk 安裝套件。
  2. 將 Magisk-*.apk 重新命名為 Magisk-vesion.zip 然後解壓縮。
  3. 將解壓縮後的 Magisk-v25.2/lib/arm64-v8a/libmagiskboot.so 檔案,使用 Adb 推入至手機:adb push Magisk-v25.2/lib/arm64-v8a/libmagiskboot.so /data/local/tmp/magiskboot
  4. 使用 Adb 將原廠 boot.img 和 AnyKernel3 中的 Image 推入至手機。
  5. adb shell 進入 /data/local/tmp/ 目錄,然後賦予先前推入檔案的可執行權限 chmod +x magiskboot
  6. adb shell 進入 /data/local/tmp/ 目錄,執行 ./magiskboot unpack boot.img 此時會將 boot.img 解除封裝,得到一個名為 kernel 的檔案,這個檔案是您的原廠核心。
  7. 使用 Image 取代 kernel: mv -f Image kernel
  8. 執行 ./magiskboot repack boot.img 重新封裝 img,此時您會得到一個 new-boot.img 檔案,透過 Fastboot 將這個檔案刷新至裝置即可。

其他替代方法

其實所有這些安裝方法的主旨只有一個,那就是將原廠核心取代為 KernelSU 提供的核心;只要能實現這個目的,就可以安裝;比如以下是其他可行的方法:

  1. 首先安裝 Magisk,透過 Magisk 取得 Root 權限後使用核心刷新程式刷新 KernelSU 的 AnyKernel Zip。
  2. 使用某些 PC 上的刷新工具組刷新 KernelSU 提供的核心。
',4),c=[n,s,d];function p(h,b,g,m,u,k){return l(),t("div",null,c)}const K=a(r,[["render",p]]);export{y as __pageData,K as default}; +import{_ as a,o as l,c as t,Q as o,k as e,a as i}from"./chunks/framework.ec8f7e8e.js";const y=JSON.parse('{"title":"安裝","description":"","frontmatter":{},"headers":[],"relativePath":"zh_TW/guide/installation.md","filePath":"zh_TW/guide/installation.md"}'),r={name:"zh_TW/guide/installation.md"},n=o(`

安裝

檢查您的裝置是否受支援

GitHub Releases酷安 下載 KernelSU 管理員應用程式,然後將應用程式安裝至裝置並開啟:

  • 如果應用程式顯示「不支援」,則表示您的裝置不支援 KernelSU,您需要自行編譯核心才能繼續使用,,KernelSU 官方也永遠不會為您提供一個可以刷新的 Boot 映像。
  • 如果應用程式顯示「未安裝」,那麼 KernelSU 支援您的裝置;可以進行下一步作業。

INFO

對於顯示「不支援」的裝置,這裡有一個非官方支援裝置清單,您可以使用這個清單裡的核心自行編譯。

備份您的原廠 boot.img

在進行刷新作業前,您必須預先備份您的原廠 boot.img。如果您在後續刷新作業中出現了任何問題,您都可以透過使用 Fastboot 刷新回到原廠 Boot 以還原系統。

WARNING

刷新作業可能會造成資料遺失,請確保做好這一步再繼續進行下一步作業!!必要時您還可以備份您手機的所有資料。

必要知識

ADB 和 Fastboot

預設狀況下,您將會使用 ADB 和 Fastboot 工具,如果您不知道它們,建議使用搜尋引擎先瞭解相關內容。

KMI

KMI 全稱 Kernel Module Interface,相同 KMI 的核心版本是相容的 這也是 GKI 中「標準」的涵義所在;反之,如果 KMI 不同,那麼這些核心之間無法彼此相容,刷新與您裝置 KMI 不同的核心映像可能會導致開機迴圈。

具體來講,對 GKI 的裝置,其核心版本格式應該如下:

txt
KernelRelease :=
+Version.PatchLevel.SubLevel-AndroidRelease-KmiGeneration-suffix
+w      .x         .y       -zzz           -k            -something
KernelRelease :=
+Version.PatchLevel.SubLevel-AndroidRelease-KmiGeneration-suffix
+w      .x         .y       -zzz           -k            -something

其中,w.x-zzz-k 為 KMI 版本。例如,一部裝置核心版本為 5.10.101-android12-9-g30979850fc20,那麼它的 KMI 為 5.10-android12-9;理論上刷新其他這個 KMI 的核心也能正常開機。

TIP

請注意,核心版本中的 SubLevel 並非 KMI 的一部分!也就是說 5.10.101-android12-9-g30979850fc205.10.137-android12-9-g30979850fc20 的 KMI 相同!

核心版本與 Android 版本

請注意:核心版本與 Android 版本並不一定相同!

如果您發現您的核心版本是 android12-5.10.101,然而您 Android 系統的版本為 Android 13 或者其他;請不要覺得奇怪,因為 Android 系統的版本與 Linux 核心的版本號碼並非一致;Linux 核心的版本號碼一般與裝置出廠時隨附的 Android 系統的版本一致,如果後續 Android 系統更新,核心版本一般不會發生變化。如果您需要刷新,請以核心版本為準!!

安裝簡介

KernelSU 的安裝方法有以下幾種,各自適用於不同的場景,請視需要選擇:

  1. 使用自訂 Recovery (如 TWRP) 安裝
  2. 使用核心刷新應用程式 (例如 Franco Kernel Manager) 安裝
  3. 使用 KernelSU 提供的 boot.img 透過 Fastboot 安裝
  4. 手動修補 boot.img 並安裝

使用自訂 Recovery 安裝

先決條件:您的裝置必須有自訂的 Recovery,例如 TWRP;如果沒有或者只有官方 Recovery,請使用其他方法。

步驟:

  1. 在 KernelSU 的 Release 頁面 下載與您手機版本相符的以 AnyKernel3 開頭的 Zip 套件;例如,手機核心版本為 android12-5.10.66,那麼您應該下載 AnyKernel3-android12-5.10.66_yyyy-MM.zip 這個檔案 (其中 yyyy 為年份,MM 為月份)。
  2. 重新開機手機至 TWRP。
  3. 使用 Adb 將 AnyKernel3-*.zip 放置到手機 /sdcard 然後在 TWRP 圖形使用者介面選擇並安裝;或者您也可以直接 adb sideload AnyKernel-*.zip 安裝。

PS. 這種方法適用於任何狀況下的安裝 (不限於初次安裝或後續更新),只要您用 TWRP 就可以進行作業。

使用核心刷新應用程式安裝

先決條件:您的裝置必須已經 Root。例如您已經安裝了 Magisk 並取得 Root 存取權,或者您已經安裝了舊版本的 KernelSU 需升級到其他版本的 KernelSU;如果您的裝置並未 Root,請嘗試其他方法。

步驟:

  1. 下載 AnyKernel3 的 Zip 檔案;請參閱 使用自訂 Recovery 安裝 章節的内容。
  2. 開啟核心刷新應用程式提供的 AnyKernel3 Zip 檔案進行刷新。

如果您先前並未使用過核心刷新應用程式,可以嘗試下面幾個方法:

  1. Kernel Flasher
  2. Franco Kernel Manager
  3. Ex Kernel Manager

PS. 這種方法在更新 KernelSU 時比較方便,無需電腦即可完成 (注意備份!)。

使用 KernelSU 提供的 boot.img 安裝

這種方法無需您有 TWRP,也無需您的手機有 Root 權限;適用於您初次安裝 KernelSU。

找到合適的 boot.img

KernelSU 為 GKI 裝置提供了標準 boot.img,您需要將 boot.img 刷新至裝置的 Boot 分割區。

您可以從 GitHub Release 下載 boot.img,請注意,您應該使用正確版本的 boot.img。例如,如果您的裝置顯示核心是 android12-5.10.101,需要下載 android-5.10.101_yyyy-MM.boot-<format>.img.

其中 <format> 指的是您的官方 boot.img 的核心壓縮格式,請檢查您原有 boot.img 的核心壓縮格式,您應該使用正確的格式,例如 lz4gz;如果使用不正確的壓縮格式,刷新 Boot 後可能無法開機。

INFO

  1. 您可以透過 magiskboot 以取得您的原始 Boot 的壓縮格式;當然,您也可以詢問與您相同型號的其他更有經驗的使用者。另外,核心的壓縮格式通常部會出現變更,如果您使用的某個壓縮格式成功開機,後續可以優先嘗試這個格式。
  2. 小米裝置通常 gz 或者 不壓縮
  3. Pixel 裝置有些特殊,請遵循下方的指示。

將 boot.img 刷新至裝置

使用 adb 連接您的裝置,然後執行 adb reboot bootloader 進入 fastboot 模式,然後使用此命令刷新 KernelSU:

sh
fastboot flash boot boot.img
fastboot flash boot boot.img

INFO

如果您的裝置支援 fastboot boot,可以先使用 fastboot boot boot.img 來先嘗試使用 boot.img 開機進入系統,如果出現意外,重新啟動即可開機。

重新開機

刷新完成後,您應該重新啟動您的裝置:

sh
fastboot reboot
fastboot reboot

手動修補 boot.img

對於某些裝置來說,其 boot.img 格式並不是很常見,比如 lz4gz 和未壓縮;最典型的就是 Pixel,它的 boot.img 格式是 lz4_legacy 壓縮,ramdisk 可能是 gz 也可能是 lz4_legacy 壓縮;此時如果您直接刷新 KernelSU 提供的 boot.img,手機可能無法開機;這時,您可以透過手動修補 boot.img 來完成。

一般有兩種修補方法:

  1. Android-Image-Kitchen
  2. magiskboot

其中,Android-Image-Kitchen 適用於在電腦上作業,magiskboot 需要手機協作。

準備

  1. 取得您手機的原廠 boot.img;您可以聯絡您的裝置製造商,您也可能需要payload-dumper-go
  2. 下載 KernelSU 提供的與您的裝置 KMI 一致地 AnyKernel3 Zip 檔案 (可參閱 使用自訂 Recovery 安裝)。
  3. 解壓縮 AnyKernel3 Zip 檔案,取得其中的 Image 檔案,此檔案為 KernelSU 的核心檔案。

使用 Android-Image-Kitchen

  1. 下載 Android-Image-Kitchen 至您的電腦。
  2. 將手機原廠 boot.img 放置於 Android-Image-Kitchen 根目錄。
  3. 在 Android-Image-Kitchen 根目錄執行 ./unpackimg.sh boot.img;此命令會將 boot.img 解除封裝,您會得到一些檔案。
  4. split_img 目錄中的 boot.img-kernel 取代為您從 AnyKernel3 解壓縮出來的 Image (注意名稱變更為 boot.img-kernel)。
  5. 在 Android-Image-Kitchecn 根目錄執行 ./repackimg.sh;此時您會得到一個 image-new.img 檔案;使用此 boot.img 透過 fastboot 刷新即可 (刷新方法請參閱上一章節)。
`,58),s=e("h3",{id:"using",magiskboot:"",tabindex:"-1"},[i("使用 magiskboot "),e("a",{class:"header-anchor",href:"#using","aria-label":'Permalink to "使用 magiskboot {#using magiskboot}"'},"​")],-1),c=o('
  1. 在 Magisk 的 Release 頁面 下載最新的 Magisk 安裝套件。
  2. 將 Magisk-*.apk 重新命名為 Magisk-vesion.zip 然後解壓縮。
  3. 將解壓縮後的 Magisk-v25.2/lib/arm64-v8a/libmagiskboot.so 檔案,使用 Adb 推入至手機:adb push Magisk-v25.2/lib/arm64-v8a/libmagiskboot.so /data/local/tmp/magiskboot
  4. 使用 Adb 將原廠 boot.img 和 AnyKernel3 中的 Image 推入至手機。
  5. adb shell 進入 /data/local/tmp/ 目錄,然後賦予先前推入檔案的可執行權限 chmod +x magiskboot
  6. adb shell 進入 /data/local/tmp/ 目錄,執行 ./magiskboot unpack boot.img 此時會將 boot.img 解除封裝,得到一個名為 kernel 的檔案,這個檔案是您的原廠核心。
  7. 使用 Image 取代 kernel: mv -f Image kernel
  8. 執行 ./magiskboot repack boot.img 重新封裝 img,此時您會得到一個 new-boot.img 檔案,透過 Fastboot 將這個檔案刷新至裝置即可。

其他替代方法

其實所有這些安裝方法的主旨只有一個,那就是將原廠核心取代為 KernelSU 提供的核心;只要能實現這個目的,就可以安裝;比如以下是其他可行的方法:

  1. 首先安裝 Magisk,透過 Magisk 取得 Root 權限後使用核心刷新程式刷新 KernelSU 的 AnyKernel Zip。
  2. 使用某些 PC 上的刷新工具組刷新 KernelSU 提供的核心。
',4),d=[n,s,c];function p(h,b,g,m,k,u){return l(),t("div",null,d)}const K=a(r,[["render",p]]);export{y as __pageData,K as default}; diff --git a/assets/zh_TW_guide_installation.md.0f23d3bf.lean.js b/assets/zh_TW_guide_installation.md.48e9e870.lean.js similarity index 50% rename from assets/zh_TW_guide_installation.md.0f23d3bf.lean.js rename to assets/zh_TW_guide_installation.md.48e9e870.lean.js index 42db27a1be38..6fd39a1dc544 100644 --- a/assets/zh_TW_guide_installation.md.0f23d3bf.lean.js +++ b/assets/zh_TW_guide_installation.md.48e9e870.lean.js @@ -1 +1 @@ -import{_ as a,o as l,c as t,O as o,z as e,a as i}from"./chunks/framework.43781440.js";const y=JSON.parse('{"title":"安裝","description":"","frontmatter":{},"headers":[],"relativePath":"zh_TW/guide/installation.md","filePath":"zh_TW/guide/installation.md"}'),r={name:"zh_TW/guide/installation.md"},n=o("",58),s=e("h3",{id:"using",magiskboot:"",tabindex:"-1"},[i("使用 magiskboot "),e("a",{class:"header-anchor",href:"#using","aria-label":'Permalink to "使用 magiskboot {#using magiskboot}"'},"​")],-1),d=o("",4),c=[n,s,d];function p(h,b,g,m,u,k){return l(),t("div",null,c)}const K=a(r,[["render",p]]);export{y as __pageData,K as default}; +import{_ as a,o as l,c as t,Q as o,k as e,a as i}from"./chunks/framework.ec8f7e8e.js";const y=JSON.parse('{"title":"安裝","description":"","frontmatter":{},"headers":[],"relativePath":"zh_TW/guide/installation.md","filePath":"zh_TW/guide/installation.md"}'),r={name:"zh_TW/guide/installation.md"},n=o("",58),s=e("h3",{id:"using",magiskboot:"",tabindex:"-1"},[i("使用 magiskboot "),e("a",{class:"header-anchor",href:"#using","aria-label":'Permalink to "使用 magiskboot {#using magiskboot}"'},"​")],-1),c=o("",4),d=[n,s,c];function p(h,b,g,m,k,u){return l(),t("div",null,d)}const K=a(r,[["render",p]]);export{y as __pageData,K as default}; diff --git a/assets/zh_TW_guide_module.md.76671e26.js b/assets/zh_TW_guide_module.md.57647aec.js similarity index 51% rename from assets/zh_TW_guide_module.md.76671e26.js rename to assets/zh_TW_guide_module.md.57647aec.js index 28ba42fcc36e..cee9d5360096 100644 --- a/assets/zh_TW_guide_module.md.76671e26.js +++ b/assets/zh_TW_guide_module.md.57647aec.js @@ -1,84 +1,167 @@ -import{_ as s,o as e,c as a,O as l}from"./chunks/framework.43781440.js";const C=JSON.parse('{"title":"模組指南","description":"","frontmatter":{},"headers":[],"relativePath":"zh_TW/guide/module.md","filePath":"zh_TW/guide/module.md"}'),n={name:"zh_TW/guide/module.md"},o=l(`

模組指南

KernelSU 提供了一個模組機制,它可以在保持系統分割區完整性的同時達到修改系統分割區的效果;這種機制一般被稱為 systemless。

KernelSU 的模組運作機制與 Magisk 幾乎相同,如果您熟悉 Magisk 模組的開發,那麼開發 KernelSU 的模組大同小異,您可以跳過下列有關模組的介紹,只需要瞭解 KernelSU 模組與 Magisk 模組的異同

Busybox

KernelSU 提供了一個完備的 BusyBox 二進位檔案 (包括完整的 SELinux 支援)。可執行檔位於 /data/adb/ksu/bin/busybox。 KernelSU 的 BusyBox 支援同時執行時可切換的 "ASH Standalone Shell Mode"。 這種讀了模式意味著在執行 BusyBox 的 ash shell 時,每個命令都會直接使用 BusyBox 中內建的應用程式,而不論 PATH 的設定為何。 例如,lsrmchmod 等命令將不會使用 PATH 中設定的命令 (在 Android 的狀況下,預設狀況下分別為 /system/bin/ls/system/bin/rm/system/bin/chmod),而是直接呼叫 BusyBox 內建的應用程式。 這確保了指令碼始終在可預測的環境中執行,並始終具有完整的命令套件,不論它執行在哪個 Android 版本上。 要強制下一個命令不使用 BusyBox,您必須使用完整路徑呼叫可執行檔。

在 KernelSU 上下文中執行的每個 shell 指令碼都將在 BusyBox 的 ash shell 中以獨立模式執行。對於第三方開發人員相關的內容,包括所有開機指令碼和模組安裝指令碼。

對於想要在 KernelSU 之外使用這個「獨立模式」功能的使用者,有兩種啟用方法:

  1. 將環境變數 ASH_STANDALONE 設為 1。例如:ASH_STANDALONE=1 /data/adb/ksu/bin/busybox sh <script>
  2. 使用命令列選項切換:/data/adb/ksu/bin/busybox sh -o standalone <script>

為了確保所有後續的 sh shell 都在獨立模式下執行,第一種是首選方法 (這也是 KernelSU 和 KernelSU 管理員內部使用的方法),因為環境變數會被繼承到子處理程序中。

與 Magisk 的差異

KernelSU 的 BusyBox 現在是直接使用 Magisk 專案編譯的二進位檔案,感謝 Magisk! 因此,您完全不必擔心 BusyBox 指令碼與在 Magisk 和 KernelSU 之間的相容性問題,因為它們完全相同!

KernelSU 模組

KernelSU 模組是一個放置於 /data/adb/modules 且滿足下列結構的資料夾:

txt
/data/adb/modules
-├── .
-├── .
-|
-├── $MODID                  <--- 模組的資料夾名稱與模組 ID 相同
-│   │
-│   │      *** 模組識別 ***
-│   │
-│   ├── module.prop         <--- 這個檔案儲存與模組相關的中繼資料,例如模組 ID、版本等
-│   │
-│   │      *** 主要內容 ***
-│   │
-│   ├── system              <--- 這個資料夾會在 skip_mount 不存在時被掛接至系統
-│   │   ├── ...
-│   │   ├── ...
-│   │   └── ...
-│   │
-│   │      *** 狀態旗標 ***
-│   │
-│   ├── skip_mount          <--- 如果這個檔案存在,那麼 KernelSU 將不會掛接您的系統資料夾
-│   ├── disable             <--- 如果這個檔案存在,那麼模組將會被停用
-│   ├── remove              <--- 如果這個檔案存在,那麼模組將會在下次重新開機時被移除
-│   │
-│   │      *** 選用檔案 ***
-│   │
-│   ├── post-fs-data.sh     <--- 這個指令碼將會在 post-fs-data 中執行
-│   ├── service.sh          <--- 這個指令碼將會在 late_start 服務中執行
-|   ├── uninstall.sh        <--- 這個指令碼將會在 KernelSU 移除模組時執行
-│   ├── system.prop         <--- 這個檔案中指定的屬性將會在系統啟動時透過 resetprop 變更
-│   ├── sepolicy.rule       <--- 這個檔案中的 SELinux 原則將會在系統開機時載入
-│   │
-│   │      *** 自動產生的目錄,不要手動建立或修改! ***
-│   │
-│   ├── vendor              <--- A symlink to $MODID/system/vendor
-│   ├── product             <--- A symlink to $MODID/system/product
-│   ├── system_ext          <--- A symlink to $MODID/system/system_ext
-│   │
-│   │      *** 允許的其他額外檔案/資料夾 ***
-│   │
-│   ├── ...
-│   └── ...
-|
-├── another_module
-│   ├── .
-│   └── .
-├── .
-├── .

與 Magisk 的差異

KernelSU 沒有內建的針對 Zygisk 的支援,因此模組中沒有與 Zygisk 相關的內容,但您可以透過 ZygiskNext 以支援 Zygisk 模組,此時 Zygisk 模組的內容與 Magisk 所支援的 Zygisk 完全相同。

module.prop

module.prop 是一個模組的組態檔案,在 KernelSU 中如果模組中不包含這個檔案,那麼它將不被認為是一個模組;這個檔案的格式如下:

txt
id=<string>
-name=<string>
-version=<string>
-versionCode=<int>
-author=<string>
-description=<string>
  • id 必須與這個規則運算式相符:^[a-zA-Z][a-zA-Z0-9._-]+$ 例如:✓ a_module,✓ a.module,✓ module-101,✗ a module,✗ 1_module,✗ -a-module。這是您的模組的唯一識別碼,發表後將無法變更。
  • versionCode 必須是一個整數,用於比較版本。
  • 其他未在上方提到的內容可以是任何單行字串。
  • 請確保使用 UNIX (LF) 分行符號類型,而非 Windows (CR + LF)Macintosh (CR)

Shell 指令碼

請閱讀 開機指令碼 章節,以瞭解 post-fs-data.shservice.sh 之間的差別。對於大多數模組開發人員來說,如果您只需要執行一個開機指令碼,service.sh 應該已經足夠了。

在您的模組中的所有指令碼中,請使用 MODDIR=\${0%/*} 以取得您的模組基本目錄路徑;請不要在指令碼中以硬式編碼的方式加入您的模組路徑。

與 Magisk 的差異

您可以透過環境變數 KSU 來判斷指令碼是執行在 KernelSU 還是 Magisk 中,如果執行在 KernelSU,這個值會被設為 true

system 目錄

這個目錄的內容會在系統啟動後,以 overlayfs 的方式覆疊在系統的 /system 分割區之上,這表示:

  1. 系統中對應目錄的相同名稱的檔案會被此目錄中的檔案覆寫。
  2. 系統中對應目錄的相同名稱的檔案會與此目錄的檔案合併。

如果您想要刪除系統先前的目錄中的某個檔案或資料夾,您需要在模組目錄中透過 mknod filename c 0 0 以建立一個 filename 的相同名稱的檔案;這樣 overlayfs 系統會自動「whiteout」等效刪除這個檔案 (/system 分割區並未被變更)。

您也可以在 customize.sh 中宣告一個名為 REMOVE 並且包含一系列目錄的變數以執行移除作業,KernelSU 會自動為您在模組對應目錄執行 mknod <TARGET> c 0 0。例如:

sh
REMOVE="
-/system/app/YouTube
-/system/app/Bloatware
-"

上方的清單將會執行:mknod $MODPATH/system/app/YouTuBe c 0 0mknod $MODPATH/system/app/Bloatware c 0 0;並且 /system/app/YouTube/system/app/Bloatware 將會在模組生效前移除。

如果您想要取代系統的某個目錄,您需要在模組目錄中建立一個相同路徑的目錄,然後為此目錄設定此屬性:setfattr -n trusted.overlay.opaque -v y <TARGET>;這樣 overlayfs 系統會自動將對應目錄取代 (/system 分割區並未被變更)。

您可以在 customize.sh 中宣告一個名為 REMOVE 並且包含一系列目錄的變數以執行移除作業,KernelSU 會自動為您在模組對應目錄執行相關作業。例如:

sh
REPLACE="
-/system/app/YouTube
-/system/app/Bloatware
-"

上方的清單將會執行:自動建立目錄 $MODPATH/system/app/YouTube$MODPATH//system/app/Bloatware,然後執行 setfattr -n trusted.overlay.opaque -v y $$MODPATH/system/app/YouTubesetfattr -n trusted.overlay.opaque -v y $$MODPATH/system/app/Bloatware;並且 /system/app/YouTube/system/app/Bloatware 將會在模組生效後被取代為空白目錄。

與 Magisk 的差異

KernelSU 的 systemless 機制透過核心的 overlayfs 實作,而 Magisk 目前則是透過 magic mount (bind mount),兩者的實作方式有很大的差別,但最終的目標是一致的:不修改實際的 /system 分割區但修改 /system 檔案。

如果您對 overlayfs 感興趣,建議閱讀 Linux Kernel 關於 overlayfs 的文件

system.prop

這個檔案的格式與 build.prop 完全相同:每一行都是由 [key]=[value] 組成。

sepolicy.rule

如果您的模組需要一些額外 SELinux 原則修補程式,請將這些原則新增至這個檔案中。這個檔案的每一行都將被視為一個原則陳述。

模組安裝程式

KernelSU 的模組安裝程式就是一個可以透過 KernelSU 管理員應用程式刷新的 Zip 檔案,這個 Zip 檔案的格式如下:

txt
module.zip
-
-├── customize.sh                       <--- (Optional, more details later)
-│                                           This script will be sourced by update-binary
-├── ...
-├── ...  /* 其他模块文件 */
-

WARNING

KernelSU 模組不支援在 Recovery 中安裝!!

自訂安裝程序

如果您想要控制模組的安裝程序,可以在模組的目錄下建立一個名為 customize.sh 的檔案,這個檔案將會在模組被解壓縮後匯入至目前的 shell 中,如果您的模組需要依據裝置的 API 版本或裝置架構執行一些額外的作業,這個指令碼將非常有用。

如果您想完全控制指令碼的安裝程序,您可以在 customize.sh 中宣告 SKIPUNZIP=1 以跳過所有的預設安裝步驟;此時,您需要自行處理所有的安裝程序 (例如解壓縮模組、設定權限等)

customize.sh 指令碼以「獨立模式」執行在 KernelSU 的 BusyBox ash shell 中。您可以使用下列變數和函式:

變數

  • KSU (bool): 標示此指令碼執行於 KernelSU 環境中,此變數的值將永遠為 true,您可以透過它與 Magisk 進行區分。
  • KSU_VER (string): KernelSU 目前的版本名稱 (例如 v0.4.0)
  • KSU_VER_CODE (int): KernelSU 使用者空間目前的版本代碼 (例如 10672)
  • KSU_KERNEL_VER_CODE (int): KernelSU 核心空間目前的版本代碼 (例如 10672)
  • BOOTMODE (bool): 此變數在 KernelSU 中永遠為 true
  • MODPATH (path): 目前模組的安裝目錄
  • TMPDIR (path): 可以存放暫存檔的位置
  • ZIPFILE (path): 目前模組的安裝程式 Zip
  • ARCH (string): 裝置的 CPU 架構,有這幾種:arm, arm64, x86, or x64
  • IS64BIT (bool): 是否為 64 位元裝置
  • API (int): 目前裝置的 Android API 版本 (例如 Android 6.0 上為 23)

WARNING

MAGISK_VER_CODE 在 KernelSU 永遠為 25200MAGISK_VER 則為 v25.2,請不要透過這兩個變數來判斷是否為 KernelSU!

函式

txt
ui_print <msg>
-    print <msg> to console
-    Avoid using 'echo' as it will not display in custom recovery's console
-
-abort <msg>
-    print error message <msg> to console and terminate the installation
-    Avoid using 'exit' as it will skip the termination cleanup steps
-
-set_perm <target> <owner> <group> <permission> [context]
-    if [context] is not set, the default is "u:object_r:system_file:s0"
-    this function is a shorthand for the following commands:
-       chown owner.group target
-       chmod permission target
-       chcon context target
-
-set_perm_recursive <directory> <owner> <group> <dirpermission> <filepermission> [context]
-    if [context] is not set, the default is "u:object_r:system_file:s0"
-    for all files in <directory>, it will call:
-       set_perm file owner group filepermission context
-    for all directories in <directory> (including itself), it will call:
-       set_perm dir owner group dirpermission context

開機指令碼

在 KernelSU 中,依據指令碼執行模式的不同分為兩種:post-fs-data 模式和 late_start 服務模式。

  • post-fs-data 模式

    • 這個階段是「封鎖」的。在執行完成之前或 10 秒鐘之後,開機程序會被暫停。
    • 指令碼在任何模組被掛接之前執行。這使模組開發人員可以在模組被掛接之前動態調整他們的模組。
    • 這個階段發生在 Zygote 啟動之前,這意味著 Android 中的一切。
    • 使用 setprop 會導致開機程序死鎖!請使用 resetprop -n <prop_name> <prop_value> 替代。
    • 僅在必要時在此模式中執行指令碼
  • late_start 服務模式

    • 這個階段是「非封鎖」的。您的指令碼會與其餘的啟動程序平行執行。
    • 大多数脚本都建议在这种模式下运行

在 KernelSU 中,開機指令碼依據存放位置的不同還分為兩種:一般指令碼和模組指令碼。

  • 一般指令碼

    • 放置於 /data/adb/post-fs-data.d/data/adb/service.d 中。
    • 僅有指令碼被設為可執行 (chmod +x script.sh) 時才會被執行。
    • post-fs-data.d 中的指令碼以 post-fs-data 模式執行,在 service.d 中的指令碼以 late_start 服務模式執行。
    • 模組不應在安裝程序中新增一般指令碼。
  • 模組指令碼

    • 放置於模組自己的資料夾中。
    • 僅有在模組啟用時才會執行。
    • post-fs-data.sh 以 post-fs-data 模式執行,而 service.sh 則以 late_start 服務模式執行。

所有启动脚本都将在 KernelSU 的 BusyBox ash shell 中运行,并启用“独立模式”。

`,58),t=[o];function p(c,i,r,d,u,A){return e(),a("div",null,t)}const m=s(n,[["render",p]]);export{C as __pageData,m as default}; +import{_ as s,o as e,c as n,Q as a}from"./chunks/framework.ec8f7e8e.js";const h=JSON.parse('{"title":"模組指南","description":"","frontmatter":{},"headers":[],"relativePath":"zh_TW/guide/module.md","filePath":"zh_TW/guide/module.md"}'),l={name:"zh_TW/guide/module.md"},o=a(`

模組指南

KernelSU 提供了一個模組機制,它可以在保持系統分割區完整性的同時達到修改系統分割區的效果;這種機制一般被稱為 systemless。

KernelSU 的模組運作機制與 Magisk 幾乎相同,如果您熟悉 Magisk 模組的開發,那麼開發 KernelSU 的模組大同小異,您可以跳過下列有關模組的介紹,只需要瞭解 KernelSU 模組與 Magisk 模組的異同

Busybox

KernelSU 提供了一個完備的 BusyBox 二進位檔案 (包括完整的 SELinux 支援)。可執行檔位於 /data/adb/ksu/bin/busybox。 KernelSU 的 BusyBox 支援同時執行時可切換的 "ASH Standalone Shell Mode"。 這種讀了模式意味著在執行 BusyBox 的 ash shell 時,每個命令都會直接使用 BusyBox 中內建的應用程式,而不論 PATH 的設定為何。 例如,lsrmchmod 等命令將不會使用 PATH 中設定的命令 (在 Android 的狀況下,預設狀況下分別為 /system/bin/ls/system/bin/rm/system/bin/chmod),而是直接呼叫 BusyBox 內建的應用程式。 這確保了指令碼始終在可預測的環境中執行,並始終具有完整的命令套件,不論它執行在哪個 Android 版本上。 要強制下一個命令不使用 BusyBox,您必須使用完整路徑呼叫可執行檔。

在 KernelSU 上下文中執行的每個 shell 指令碼都將在 BusyBox 的 ash shell 中以獨立模式執行。對於第三方開發人員相關的內容,包括所有開機指令碼和模組安裝指令碼。

對於想要在 KernelSU 之外使用這個「獨立模式」功能的使用者,有兩種啟用方法:

  1. 將環境變數 ASH_STANDALONE 設為 1。例如:ASH_STANDALONE=1 /data/adb/ksu/bin/busybox sh <script>
  2. 使用命令列選項切換:/data/adb/ksu/bin/busybox sh -o standalone <script>

為了確保所有後續的 sh shell 都在獨立模式下執行,第一種是首選方法 (這也是 KernelSU 和 KernelSU 管理員內部使用的方法),因為環境變數會被繼承到子處理程序中。

與 Magisk 的差異

KernelSU 的 BusyBox 現在是直接使用 Magisk 專案編譯的二進位檔案,感謝 Magisk! 因此,您完全不必擔心 BusyBox 指令碼與在 Magisk 和 KernelSU 之間的相容性問題,因為它們完全相同!

KernelSU 模組

KernelSU 模組是一個放置於 /data/adb/modules 且滿足下列結構的資料夾:

txt
/data/adb/modules
+├── .
+├── .
+|
+├── $MODID                  <--- 模組的資料夾名稱與模組 ID 相同
+│   │
+│   │      *** 模組識別 ***
+│   │
+│   ├── module.prop         <--- 這個檔案儲存與模組相關的中繼資料,例如模組 ID、版本等
+│   │
+│   │      *** 主要內容 ***
+│   │
+│   ├── system              <--- 這個資料夾會在 skip_mount 不存在時被掛接至系統
+│   │   ├── ...
+│   │   ├── ...
+│   │   └── ...
+│   │
+│   │      *** 狀態旗標 ***
+│   │
+│   ├── skip_mount          <--- 如果這個檔案存在,那麼 KernelSU 將不會掛接您的系統資料夾
+│   ├── disable             <--- 如果這個檔案存在,那麼模組將會被停用
+│   ├── remove              <--- 如果這個檔案存在,那麼模組將會在下次重新開機時被移除
+│   │
+│   │      *** 選用檔案 ***
+│   │
+│   ├── post-fs-data.sh     <--- 這個指令碼將會在 post-fs-data 中執行
+│   ├── service.sh          <--- 這個指令碼將會在 late_start 服務中執行
+|   ├── uninstall.sh        <--- 這個指令碼將會在 KernelSU 移除模組時執行
+│   ├── system.prop         <--- 這個檔案中指定的屬性將會在系統啟動時透過 resetprop 變更
+│   ├── sepolicy.rule       <--- 這個檔案中的 SELinux 原則將會在系統開機時載入
+│   │
+│   │      *** 自動產生的目錄,不要手動建立或修改! ***
+│   │
+│   ├── vendor              <--- A symlink to $MODID/system/vendor
+│   ├── product             <--- A symlink to $MODID/system/product
+│   ├── system_ext          <--- A symlink to $MODID/system/system_ext
+│   │
+│   │      *** 允許的其他額外檔案/資料夾 ***
+│   │
+│   ├── ...
+│   └── ...
+|
+├── another_module
+│   ├── .
+│   └── .
+├── .
+├── .
/data/adb/modules
+├── .
+├── .
+|
+├── $MODID                  <--- 模組的資料夾名稱與模組 ID 相同
+│   │
+│   │      *** 模組識別 ***
+│   │
+│   ├── module.prop         <--- 這個檔案儲存與模組相關的中繼資料,例如模組 ID、版本等
+│   │
+│   │      *** 主要內容 ***
+│   │
+│   ├── system              <--- 這個資料夾會在 skip_mount 不存在時被掛接至系統
+│   │   ├── ...
+│   │   ├── ...
+│   │   └── ...
+│   │
+│   │      *** 狀態旗標 ***
+│   │
+│   ├── skip_mount          <--- 如果這個檔案存在,那麼 KernelSU 將不會掛接您的系統資料夾
+│   ├── disable             <--- 如果這個檔案存在,那麼模組將會被停用
+│   ├── remove              <--- 如果這個檔案存在,那麼模組將會在下次重新開機時被移除
+│   │
+│   │      *** 選用檔案 ***
+│   │
+│   ├── post-fs-data.sh     <--- 這個指令碼將會在 post-fs-data 中執行
+│   ├── service.sh          <--- 這個指令碼將會在 late_start 服務中執行
+|   ├── uninstall.sh        <--- 這個指令碼將會在 KernelSU 移除模組時執行
+│   ├── system.prop         <--- 這個檔案中指定的屬性將會在系統啟動時透過 resetprop 變更
+│   ├── sepolicy.rule       <--- 這個檔案中的 SELinux 原則將會在系統開機時載入
+│   │
+│   │      *** 自動產生的目錄,不要手動建立或修改! ***
+│   │
+│   ├── vendor              <--- A symlink to $MODID/system/vendor
+│   ├── product             <--- A symlink to $MODID/system/product
+│   ├── system_ext          <--- A symlink to $MODID/system/system_ext
+│   │
+│   │      *** 允許的其他額外檔案/資料夾 ***
+│   │
+│   ├── ...
+│   └── ...
+|
+├── another_module
+│   ├── .
+│   └── .
+├── .
+├── .

與 Magisk 的差異

KernelSU 沒有內建的針對 Zygisk 的支援,因此模組中沒有與 Zygisk 相關的內容,但您可以透過 ZygiskNext 以支援 Zygisk 模組,此時 Zygisk 模組的內容與 Magisk 所支援的 Zygisk 完全相同。

module.prop

module.prop 是一個模組的組態檔案,在 KernelSU 中如果模組中不包含這個檔案,那麼它將不被認為是一個模組;這個檔案的格式如下:

txt
id=<string>
+name=<string>
+version=<string>
+versionCode=<int>
+author=<string>
+description=<string>
id=<string>
+name=<string>
+version=<string>
+versionCode=<int>
+author=<string>
+description=<string>
  • id 必須與這個規則運算式相符:^[a-zA-Z][a-zA-Z0-9._-]+$ 例如:✓ a_module,✓ a.module,✓ module-101,✗ a module,✗ 1_module,✗ -a-module。這是您的模組的唯一識別碼,發表後將無法變更。
  • versionCode 必須是一個整數,用於比較版本。
  • 其他未在上方提到的內容可以是任何單行字串。
  • 請確保使用 UNIX (LF) 分行符號類型,而非 Windows (CR + LF)Macintosh (CR)

Shell 指令碼

請閱讀 開機指令碼 章節,以瞭解 post-fs-data.shservice.sh 之間的差別。對於大多數模組開發人員來說,如果您只需要執行一個開機指令碼,service.sh 應該已經足夠了。

在您的模組中的所有指令碼中,請使用 MODDIR=\${0%/*} 以取得您的模組基本目錄路徑;請不要在指令碼中以硬式編碼的方式加入您的模組路徑。

與 Magisk 的差異

您可以透過環境變數 KSU 來判斷指令碼是執行在 KernelSU 還是 Magisk 中,如果執行在 KernelSU,這個值會被設為 true

system 目錄

這個目錄的內容會在系統啟動後,以 overlayfs 的方式覆疊在系統的 /system 分割區之上,這表示:

  1. 系統中對應目錄的相同名稱的檔案會被此目錄中的檔案覆寫。
  2. 系統中對應目錄的相同名稱的檔案會與此目錄的檔案合併。

如果您想要刪除系統先前的目錄中的某個檔案或資料夾,您需要在模組目錄中透過 mknod filename c 0 0 以建立一個 filename 的相同名稱的檔案;這樣 overlayfs 系統會自動「whiteout」等效刪除這個檔案 (/system 分割區並未被變更)。

您也可以在 customize.sh 中宣告一個名為 REMOVE 並且包含一系列目錄的變數以執行移除作業,KernelSU 會自動為您在模組對應目錄執行 mknod <TARGET> c 0 0。例如:

sh
REMOVE="
+/system/app/YouTube
+/system/app/Bloatware
+"
REMOVE="
+/system/app/YouTube
+/system/app/Bloatware
+"

上方的清單將會執行:mknod $MODPATH/system/app/YouTuBe c 0 0mknod $MODPATH/system/app/Bloatware c 0 0;並且 /system/app/YouTube/system/app/Bloatware 將會在模組生效前移除。

如果您想要取代系統的某個目錄,您需要在模組目錄中建立一個相同路徑的目錄,然後為此目錄設定此屬性:setfattr -n trusted.overlay.opaque -v y <TARGET>;這樣 overlayfs 系統會自動將對應目錄取代 (/system 分割區並未被變更)。

您可以在 customize.sh 中宣告一個名為 REMOVE 並且包含一系列目錄的變數以執行移除作業,KernelSU 會自動為您在模組對應目錄執行相關作業。例如:

sh
REPLACE="
+/system/app/YouTube
+/system/app/Bloatware
+"
REPLACE="
+/system/app/YouTube
+/system/app/Bloatware
+"

上方的清單將會執行:自動建立目錄 $MODPATH/system/app/YouTube$MODPATH//system/app/Bloatware,然後執行 setfattr -n trusted.overlay.opaque -v y $$MODPATH/system/app/YouTubesetfattr -n trusted.overlay.opaque -v y $$MODPATH/system/app/Bloatware;並且 /system/app/YouTube/system/app/Bloatware 將會在模組生效後被取代為空白目錄。

與 Magisk 的差異

KernelSU 的 systemless 機制透過核心的 overlayfs 實作,而 Magisk 目前則是透過 magic mount (bind mount),兩者的實作方式有很大的差別,但最終的目標是一致的:不修改實際的 /system 分割區但修改 /system 檔案。

如果您對 overlayfs 感興趣,建議閱讀 Linux Kernel 關於 overlayfs 的文件

system.prop

這個檔案的格式與 build.prop 完全相同:每一行都是由 [key]=[value] 組成。

sepolicy.rule

如果您的模組需要一些額外 SELinux 原則修補程式,請將這些原則新增至這個檔案中。這個檔案的每一行都將被視為一個原則陳述。

模組安裝程式

KernelSU 的模組安裝程式就是一個可以透過 KernelSU 管理員應用程式刷新的 Zip 檔案,這個 Zip 檔案的格式如下:

txt
module.zip
+
+├── customize.sh                       <--- (Optional, more details later)
+│                                           This script will be sourced by update-binary
+├── ...
+├── ...  /* 其他模块文件 */
+
module.zip
+
+├── customize.sh                       <--- (Optional, more details later)
+│                                           This script will be sourced by update-binary
+├── ...
+├── ...  /* 其他模块文件 */
+

WARNING

KernelSU 模組不支援在 Recovery 中安裝!!

自訂安裝程序

如果您想要控制模組的安裝程序,可以在模組的目錄下建立一個名為 customize.sh 的檔案,這個檔案將會在模組被解壓縮後匯入至目前的 shell 中,如果您的模組需要依據裝置的 API 版本或裝置架構執行一些額外的作業,這個指令碼將非常有用。

如果您想完全控制指令碼的安裝程序,您可以在 customize.sh 中宣告 SKIPUNZIP=1 以跳過所有的預設安裝步驟;此時,您需要自行處理所有的安裝程序 (例如解壓縮模組、設定權限等)

customize.sh 指令碼以「獨立模式」執行在 KernelSU 的 BusyBox ash shell 中。您可以使用下列變數和函式:

變數

  • KSU (bool): 標示此指令碼執行於 KernelSU 環境中,此變數的值將永遠為 true,您可以透過它與 Magisk 進行區分。
  • KSU_VER (string): KernelSU 目前的版本名稱 (例如 v0.4.0)
  • KSU_VER_CODE (int): KernelSU 使用者空間目前的版本代碼 (例如 10672)
  • KSU_KERNEL_VER_CODE (int): KernelSU 核心空間目前的版本代碼 (例如 10672)
  • BOOTMODE (bool): 此變數在 KernelSU 中永遠為 true
  • MODPATH (path): 目前模組的安裝目錄
  • TMPDIR (path): 可以存放暫存檔的位置
  • ZIPFILE (path): 目前模組的安裝程式 Zip
  • ARCH (string): 裝置的 CPU 架構,有這幾種:arm, arm64, x86, or x64
  • IS64BIT (bool): 是否為 64 位元裝置
  • API (int): 目前裝置的 Android API 版本 (例如 Android 6.0 上為 23)

WARNING

MAGISK_VER_CODE 在 KernelSU 永遠為 25200MAGISK_VER 則為 v25.2,請不要透過這兩個變數來判斷是否為 KernelSU!

函式

txt
ui_print <msg>
+    print <msg> to console
+    Avoid using 'echo' as it will not display in custom recovery's console
+
+abort <msg>
+    print error message <msg> to console and terminate the installation
+    Avoid using 'exit' as it will skip the termination cleanup steps
+
+set_perm <target> <owner> <group> <permission> [context]
+    if [context] is not set, the default is "u:object_r:system_file:s0"
+    this function is a shorthand for the following commands:
+       chown owner.group target
+       chmod permission target
+       chcon context target
+
+set_perm_recursive <directory> <owner> <group> <dirpermission> <filepermission> [context]
+    if [context] is not set, the default is "u:object_r:system_file:s0"
+    for all files in <directory>, it will call:
+       set_perm file owner group filepermission context
+    for all directories in <directory> (including itself), it will call:
+       set_perm dir owner group dirpermission context
ui_print <msg>
+    print <msg> to console
+    Avoid using 'echo' as it will not display in custom recovery's console
+
+abort <msg>
+    print error message <msg> to console and terminate the installation
+    Avoid using 'exit' as it will skip the termination cleanup steps
+
+set_perm <target> <owner> <group> <permission> [context]
+    if [context] is not set, the default is "u:object_r:system_file:s0"
+    this function is a shorthand for the following commands:
+       chown owner.group target
+       chmod permission target
+       chcon context target
+
+set_perm_recursive <directory> <owner> <group> <dirpermission> <filepermission> [context]
+    if [context] is not set, the default is "u:object_r:system_file:s0"
+    for all files in <directory>, it will call:
+       set_perm file owner group filepermission context
+    for all directories in <directory> (including itself), it will call:
+       set_perm dir owner group dirpermission context

開機指令碼

在 KernelSU 中,依據指令碼執行模式的不同分為兩種:post-fs-data 模式和 late_start 服務模式。

  • post-fs-data 模式

    • 這個階段是「封鎖」的。在執行完成之前或 10 秒鐘之後,開機程序會被暫停。
    • 指令碼在任何模組被掛接之前執行。這使模組開發人員可以在模組被掛接之前動態調整他們的模組。
    • 這個階段發生在 Zygote 啟動之前,這意味著 Android 中的一切。
    • 使用 setprop 會導致開機程序死鎖!請使用 resetprop -n <prop_name> <prop_value> 替代。
    • 僅在必要時在此模式中執行指令碼
  • late_start 服務模式

    • 這個階段是「非封鎖」的。您的指令碼會與其餘的啟動程序平行執行。
    • 大多数脚本都建议在这种模式下运行

在 KernelSU 中,開機指令碼依據存放位置的不同還分為兩種:一般指令碼和模組指令碼。

  • 一般指令碼

    • 放置於 /data/adb/post-fs-data.d/data/adb/service.d 中。
    • 僅有指令碼被設為可執行 (chmod +x script.sh) 時才會被執行。
    • post-fs-data.d 中的指令碼以 post-fs-data 模式執行,在 service.d 中的指令碼以 late_start 服務模式執行。
    • 模組不應在安裝程序中新增一般指令碼。
  • 模組指令碼

    • 放置於模組自己的資料夾中。
    • 僅有在模組啟用時才會執行。
    • post-fs-data.sh 以 post-fs-data 模式執行,而 service.sh 則以 late_start 服務模式執行。

所有启动脚本都将在 KernelSU 的 BusyBox ash shell 中运行,并启用“独立模式”。

`,58),p=[o];function t(c,i,r,d,y,u){return e(),n("div",null,p)}const g=s(l,[["render",t]]);export{h as __pageData,g as default}; diff --git a/assets/zh_TW_guide_module.md.57647aec.lean.js b/assets/zh_TW_guide_module.md.57647aec.lean.js new file mode 100644 index 000000000000..30a7a44efeef --- /dev/null +++ b/assets/zh_TW_guide_module.md.57647aec.lean.js @@ -0,0 +1 @@ +import{_ as s,o as e,c as n,Q as a}from"./chunks/framework.ec8f7e8e.js";const h=JSON.parse('{"title":"模組指南","description":"","frontmatter":{},"headers":[],"relativePath":"zh_TW/guide/module.md","filePath":"zh_TW/guide/module.md"}'),l={name:"zh_TW/guide/module.md"},o=a("",58),p=[o];function t(c,i,r,d,y,u){return e(),n("div",null,p)}const g=s(l,[["render",t]]);export{h as __pageData,g as default}; diff --git a/assets/zh_TW_guide_module.md.76671e26.lean.js b/assets/zh_TW_guide_module.md.76671e26.lean.js deleted file mode 100644 index 5470df7075f1..000000000000 --- a/assets/zh_TW_guide_module.md.76671e26.lean.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as s,o as e,c as a,O as l}from"./chunks/framework.43781440.js";const C=JSON.parse('{"title":"模組指南","description":"","frontmatter":{},"headers":[],"relativePath":"zh_TW/guide/module.md","filePath":"zh_TW/guide/module.md"}'),n={name:"zh_TW/guide/module.md"},o=l("",58),t=[o];function p(c,i,r,d,u,A){return e(),a("div",null,t)}const m=s(n,[["render",p]]);export{C as __pageData,m as default}; diff --git a/assets/zh_TW_guide_rescue-from-bootloop.md.db20cf2e.js b/assets/zh_TW_guide_rescue-from-bootloop.md.499539d8.js similarity index 97% rename from assets/zh_TW_guide_rescue-from-bootloop.md.db20cf2e.js rename to assets/zh_TW_guide_rescue-from-bootloop.md.499539d8.js index 82e5f99893b0..3e48758f52ce 100644 --- a/assets/zh_TW_guide_rescue-from-bootloop.md.db20cf2e.js +++ b/assets/zh_TW_guide_rescue-from-bootloop.md.499539d8.js @@ -1 +1 @@ -import{_ as o,o as e,c as a,O as t}from"./chunks/framework.43781440.js";const b=JSON.parse('{"title":"搶救開機迴圈","description":"","frontmatter":{},"headers":[],"relativePath":"zh_TW/guide/rescue-from-bootloop.md","filePath":"zh_TW/guide/rescue-from-bootloop.md"}'),r={name:"zh_TW/guide/rescue-from-bootloop.md"},l=t('

搶救開機迴圈

在刷新裝置時,我們很可能會遇到裝置「變磚」的狀況,從理論上講,如果您只是使用 Fastboot 刷新 Boot 分割區或者安裝不合適的模組導致裝置無法開機,那麼這都可以透過合適的作業還原您的手機;這個文件提供一些緊急方法可以讓您在「變磚」中還原。

刷新 Boot 時變磚

在 KernelSU 中,刷新 boot 時變磚有下列原因:

  1. 你刷新了錯誤格式的 Boot 映像。比如您的手機 Boot 格式為 gz,但您刷新 lz4 格式的映像,那麼此時手機將無法開機。
  2. 您的手機需要關閉 AVB 驗證才可正常開機 (這通常需要抹除手機上的所有資料)。
  3. 您的核心存在某些錯誤或您的核心並不適合這部手機刷新。

無論哪種狀況,您都可以透過刷新原廠 Boot還原;因此,在安裝教學最開始,我們已經強烈建議大家,在刷新之前備份自己的原廠 Boot!如果您沒有備份,那麼您可以透過其他與您相同裝置的使用者或官方韌體擷取 Boot。

刷新模組變磚

刷新模組變磚可能是大家遇到的更常見的狀況,但是這裡要嚴正警示大家:不要刷新未知來源的模組!!。因為模組擁有 Root 權限,它能完全對您的裝置造成無法復原的損壞!

一般模組

如果大家刷新了某些開放原始碼的或者被證明是安全的模組使手機無法開機,那麼這種狀況在 KernelSU 中非常容易還原,也無需擔心。KernelSU 內建了下列兩種機制以搶救您的裝置:

  1. AB 更新
  2. 透過按下「音量 -」搶救

AB 更新

KernelSU 的模組借鑒了 Android 系統 OTA 更新時的 AB 更新機制,如果您安裝了新模組或者對現存模組進行了更新作業,不會直接修改目前使用的模組檔案,而是會把所有模組建置為另外一個更新映像;系統重新啟動後,會使用這個更新映像嘗試重新啟動一次,如果 Android 系統成功開機,模組才會真正更新。

因此,最簡單最常用的搶救方法就是:強制重新開機一次。如果您在刷新某個模組之後系統無法開機,您可以長按電源按鈕超過 10 秒,系統會自動重新開機;模組會回復為更新前的狀態,先前更新的模組也將會被自動停用。

透過按下「音量 -」搶救

如果 AB 更新仍然無法解決,您可以嘗試使用安全模式。進入安全模式之後,所有的模組將會被停用。

進入安全模式的方法有兩種:

  1. 某些系統內建的安全模式;有些系統是長按「音量 -」,有些系統 (例如 MIUI) 可以在 Recovery 中啟用安全模式。進入系統的安全模式後,KernelSU 也會進入安全模式,並自動停用模組。
  2. KernelSU 內建的安全模式;作業方法:開機第一個畫面後,連續按下「音量 -」按鈕超過三次。注意是按下-抬起、按下-抬起、按下-抬起,並非一直按下。

進入安全模式後,KernelSU 管理員的模組頁面的所有模組將會被停用,但您可以執行「解除安裝」作業,將可能存在問題的模組解除安裝。

內建的安全模式在核心中實作,因此不會出現按鍵活動無法攔截的狀況。不過對於非 GKI 核心,可能需要手動整合程式碼,可以參閱官方文件指南。

惡意模組

如果以上方法無法搶救您的裝置,那麼很可能您安裝的模組存在惡意作業或透過其他方式損壞了您的裝置,在這種狀況下,只有兩個建議:

  1. 抹除資料並刷新官方系統。
  2. 諮詢售後服務。
',23),i=[l];function n(d,s,c,h,p,u){return e(),a("div",null,i)}const m=o(r,[["render",n]]);export{b as __pageData,m as default}; +import{_ as o,o as e,c as a,Q as t}from"./chunks/framework.ec8f7e8e.js";const b=JSON.parse('{"title":"搶救開機迴圈","description":"","frontmatter":{},"headers":[],"relativePath":"zh_TW/guide/rescue-from-bootloop.md","filePath":"zh_TW/guide/rescue-from-bootloop.md"}'),r={name:"zh_TW/guide/rescue-from-bootloop.md"},l=t('

搶救開機迴圈

在刷新裝置時,我們很可能會遇到裝置「變磚」的狀況,從理論上講,如果您只是使用 Fastboot 刷新 Boot 分割區或者安裝不合適的模組導致裝置無法開機,那麼這都可以透過合適的作業還原您的手機;這個文件提供一些緊急方法可以讓您在「變磚」中還原。

刷新 Boot 時變磚

在 KernelSU 中,刷新 boot 時變磚有下列原因:

  1. 你刷新了錯誤格式的 Boot 映像。比如您的手機 Boot 格式為 gz,但您刷新 lz4 格式的映像,那麼此時手機將無法開機。
  2. 您的手機需要關閉 AVB 驗證才可正常開機 (這通常需要抹除手機上的所有資料)。
  3. 您的核心存在某些錯誤或您的核心並不適合這部手機刷新。

無論哪種狀況,您都可以透過刷新原廠 Boot還原;因此,在安裝教學最開始,我們已經強烈建議大家,在刷新之前備份自己的原廠 Boot!如果您沒有備份,那麼您可以透過其他與您相同裝置的使用者或官方韌體擷取 Boot。

刷新模組變磚

刷新模組變磚可能是大家遇到的更常見的狀況,但是這裡要嚴正警示大家:不要刷新未知來源的模組!!。因為模組擁有 Root 權限,它能完全對您的裝置造成無法復原的損壞!

一般模組

如果大家刷新了某些開放原始碼的或者被證明是安全的模組使手機無法開機,那麼這種狀況在 KernelSU 中非常容易還原,也無需擔心。KernelSU 內建了下列兩種機制以搶救您的裝置:

  1. AB 更新
  2. 透過按下「音量 -」搶救

AB 更新

KernelSU 的模組借鑒了 Android 系統 OTA 更新時的 AB 更新機制,如果您安裝了新模組或者對現存模組進行了更新作業,不會直接修改目前使用的模組檔案,而是會把所有模組建置為另外一個更新映像;系統重新啟動後,會使用這個更新映像嘗試重新啟動一次,如果 Android 系統成功開機,模組才會真正更新。

因此,最簡單最常用的搶救方法就是:強制重新開機一次。如果您在刷新某個模組之後系統無法開機,您可以長按電源按鈕超過 10 秒,系統會自動重新開機;模組會回復為更新前的狀態,先前更新的模組也將會被自動停用。

透過按下「音量 -」搶救

如果 AB 更新仍然無法解決,您可以嘗試使用安全模式。進入安全模式之後,所有的模組將會被停用。

進入安全模式的方法有兩種:

  1. 某些系統內建的安全模式;有些系統是長按「音量 -」,有些系統 (例如 MIUI) 可以在 Recovery 中啟用安全模式。進入系統的安全模式後,KernelSU 也會進入安全模式,並自動停用模組。
  2. KernelSU 內建的安全模式;作業方法:開機第一個畫面後,連續按下「音量 -」按鈕超過三次。注意是按下-抬起、按下-抬起、按下-抬起,並非一直按下。

進入安全模式後,KernelSU 管理員的模組頁面的所有模組將會被停用,但您可以執行「解除安裝」作業,將可能存在問題的模組解除安裝。

內建的安全模式在核心中實作,因此不會出現按鍵活動無法攔截的狀況。不過對於非 GKI 核心,可能需要手動整合程式碼,可以參閱官方文件指南。

惡意模組

如果以上方法無法搶救您的裝置,那麼很可能您安裝的模組存在惡意作業或透過其他方式損壞了您的裝置,在這種狀況下,只有兩個建議:

  1. 抹除資料並刷新官方系統。
  2. 諮詢售後服務。
',23),i=[l];function n(d,s,c,h,p,u){return e(),a("div",null,i)}const m=o(r,[["render",n]]);export{b as __pageData,m as default}; diff --git a/assets/zh_TW_guide_rescue-from-bootloop.md.db20cf2e.lean.js b/assets/zh_TW_guide_rescue-from-bootloop.md.499539d8.lean.js similarity index 71% rename from assets/zh_TW_guide_rescue-from-bootloop.md.db20cf2e.lean.js rename to assets/zh_TW_guide_rescue-from-bootloop.md.499539d8.lean.js index 0978fcec55c8..10444542997c 100644 --- a/assets/zh_TW_guide_rescue-from-bootloop.md.db20cf2e.lean.js +++ b/assets/zh_TW_guide_rescue-from-bootloop.md.499539d8.lean.js @@ -1 +1 @@ -import{_ as o,o as e,c as a,O as t}from"./chunks/framework.43781440.js";const b=JSON.parse('{"title":"搶救開機迴圈","description":"","frontmatter":{},"headers":[],"relativePath":"zh_TW/guide/rescue-from-bootloop.md","filePath":"zh_TW/guide/rescue-from-bootloop.md"}'),r={name:"zh_TW/guide/rescue-from-bootloop.md"},l=t("",23),i=[l];function n(d,s,c,h,p,u){return e(),a("div",null,i)}const m=o(r,[["render",n]]);export{b as __pageData,m as default}; +import{_ as o,o as e,c as a,Q as t}from"./chunks/framework.ec8f7e8e.js";const b=JSON.parse('{"title":"搶救開機迴圈","description":"","frontmatter":{},"headers":[],"relativePath":"zh_TW/guide/rescue-from-bootloop.md","filePath":"zh_TW/guide/rescue-from-bootloop.md"}'),r={name:"zh_TW/guide/rescue-from-bootloop.md"},l=t("",23),i=[l];function n(d,s,c,h,p,u){return e(),a("div",null,i)}const m=o(r,[["render",n]]);export{b as __pageData,m as default}; diff --git a/assets/zh_TW_guide_unofficially-support-devices.md.e385e7b5.js b/assets/zh_TW_guide_unofficially-support-devices.md.e1ab05b4.js similarity index 92% rename from assets/zh_TW_guide_unofficially-support-devices.md.e385e7b5.js rename to assets/zh_TW_guide_unofficially-support-devices.md.e1ab05b4.js index 83874deb83fb..820538b10f86 100644 --- a/assets/zh_TW_guide_unofficially-support-devices.md.e385e7b5.js +++ b/assets/zh_TW_guide_unofficially-support-devices.md.e1ab05b4.js @@ -1 +1 @@ -import{d as r}from"./chunks/repos.9a031e32.js";import{o as a,c as l,z as e,F as s,L as i,b as o,O as c,t as n}from"./chunks/framework.43781440.js";const _=c('

非官方支援裝置

WARNING

本文件列出由其他開發人員維護的支援 KernelSU 的非 GKI 裝置核心

WARNING

本文件仅便於尋找裝置對應原始碼,這並非意味著這些原始碼 KernelSU 開發人員審查,您應自行承擔風險。

',3),d=e("thead",null,[e("tr",null,[e("th",null,"維護者"),e("th",null,"存放庫"),e("th",null,"支援裝置")])],-1),u=["href"],h=["href"],v=JSON.parse('{"title":"非官方支援裝置","description":"","frontmatter":{},"headers":[],"relativePath":"zh_TW/guide/unofficially-support-devices.md","filePath":"zh_TW/guide/unofficially-support-devices.md"}'),f={name:"zh_TW/guide/unofficially-support-devices.md"},T=Object.assign(f,{setup(m){return(p,g)=>(a(),l("div",null,[_,e("table",null,[d,e("tbody",null,[(a(!0),l(s,null,i(o(r),t=>(a(),l("tr",{key:t.devices},[e("td",null,[e("a",{href:t.maintainer_link,target:"_blank",rel:"noreferrer"},n(t.maintainer),9,u)]),e("td",null,[e("a",{href:t.kernel_link,target:"_blank",rel:"noreferrer"},n(t.kernel_name),9,h)]),e("td",null,n(t.devices),1)]))),128))])])]))}});export{v as __pageData,T as default}; +import{d as r}from"./chunks/repos.9a031e32.js";import{o as a,c as l,k as e,F as s,D as i,l as o,Q as c,t as n}from"./chunks/framework.ec8f7e8e.js";const _=c('

非官方支援裝置

WARNING

本文件列出由其他開發人員維護的支援 KernelSU 的非 GKI 裝置核心

WARNING

本文件仅便於尋找裝置對應原始碼,這並非意味著這些原始碼 KernelSU 開發人員審查,您應自行承擔風險。

',3),d=e("thead",null,[e("tr",null,[e("th",null,"維護者"),e("th",null,"存放庫"),e("th",null,"支援裝置")])],-1),u=["href"],h=["href"],v=JSON.parse('{"title":"非官方支援裝置","description":"","frontmatter":{},"headers":[],"relativePath":"zh_TW/guide/unofficially-support-devices.md","filePath":"zh_TW/guide/unofficially-support-devices.md"}'),f={name:"zh_TW/guide/unofficially-support-devices.md"},T=Object.assign(f,{setup(m){return(p,g)=>(a(),l("div",null,[_,e("table",null,[d,e("tbody",null,[(a(!0),l(s,null,i(o(r),t=>(a(),l("tr",{key:t.devices},[e("td",null,[e("a",{href:t.maintainer_link,target:"_blank",rel:"noreferrer"},n(t.maintainer),9,u)]),e("td",null,[e("a",{href:t.kernel_link,target:"_blank",rel:"noreferrer"},n(t.kernel_name),9,h)]),e("td",null,n(t.devices),1)]))),128))])])]))}});export{v as __pageData,T as default}; diff --git a/assets/zh_TW_guide_unofficially-support-devices.md.e385e7b5.lean.js b/assets/zh_TW_guide_unofficially-support-devices.md.e1ab05b4.lean.js similarity index 87% rename from assets/zh_TW_guide_unofficially-support-devices.md.e385e7b5.lean.js rename to assets/zh_TW_guide_unofficially-support-devices.md.e1ab05b4.lean.js index 0c4aed317f00..57e96ed105e3 100644 --- a/assets/zh_TW_guide_unofficially-support-devices.md.e385e7b5.lean.js +++ b/assets/zh_TW_guide_unofficially-support-devices.md.e1ab05b4.lean.js @@ -1 +1 @@ -import{d as r}from"./chunks/repos.9a031e32.js";import{o as a,c as l,z as e,F as s,L as i,b as o,O as c,t as n}from"./chunks/framework.43781440.js";const _=c("",3),d=e("thead",null,[e("tr",null,[e("th",null,"維護者"),e("th",null,"存放庫"),e("th",null,"支援裝置")])],-1),u=["href"],h=["href"],v=JSON.parse('{"title":"非官方支援裝置","description":"","frontmatter":{},"headers":[],"relativePath":"zh_TW/guide/unofficially-support-devices.md","filePath":"zh_TW/guide/unofficially-support-devices.md"}'),f={name:"zh_TW/guide/unofficially-support-devices.md"},T=Object.assign(f,{setup(m){return(p,g)=>(a(),l("div",null,[_,e("table",null,[d,e("tbody",null,[(a(!0),l(s,null,i(o(r),t=>(a(),l("tr",{key:t.devices},[e("td",null,[e("a",{href:t.maintainer_link,target:"_blank",rel:"noreferrer"},n(t.maintainer),9,u)]),e("td",null,[e("a",{href:t.kernel_link,target:"_blank",rel:"noreferrer"},n(t.kernel_name),9,h)]),e("td",null,n(t.devices),1)]))),128))])])]))}});export{v as __pageData,T as default}; +import{d as r}from"./chunks/repos.9a031e32.js";import{o as a,c as l,k as e,F as s,D as i,l as o,Q as c,t as n}from"./chunks/framework.ec8f7e8e.js";const _=c("",3),d=e("thead",null,[e("tr",null,[e("th",null,"維護者"),e("th",null,"存放庫"),e("th",null,"支援裝置")])],-1),u=["href"],h=["href"],v=JSON.parse('{"title":"非官方支援裝置","description":"","frontmatter":{},"headers":[],"relativePath":"zh_TW/guide/unofficially-support-devices.md","filePath":"zh_TW/guide/unofficially-support-devices.md"}'),f={name:"zh_TW/guide/unofficially-support-devices.md"},T=Object.assign(f,{setup(m){return(p,g)=>(a(),l("div",null,[_,e("table",null,[d,e("tbody",null,[(a(!0),l(s,null,i(o(r),t=>(a(),l("tr",{key:t.devices},[e("td",null,[e("a",{href:t.maintainer_link,target:"_blank",rel:"noreferrer"},n(t.maintainer),9,u)]),e("td",null,[e("a",{href:t.kernel_link,target:"_blank",rel:"noreferrer"},n(t.kernel_name),9,h)]),e("td",null,n(t.devices),1)]))),128))])])]))}});export{v as __pageData,T as default}; diff --git a/assets/zh_TW_guide_what-is-kernelsu.md.a2ba0439.js b/assets/zh_TW_guide_what-is-kernelsu.md.c73a8b16.js similarity index 94% rename from assets/zh_TW_guide_what-is-kernelsu.md.a2ba0439.js rename to assets/zh_TW_guide_what-is-kernelsu.md.c73a8b16.js index 9c908286d2a6..29b68d577db7 100644 --- a/assets/zh_TW_guide_what-is-kernelsu.md.a2ba0439.js +++ b/assets/zh_TW_guide_what-is-kernelsu.md.c73a8b16.js @@ -1 +1 @@ -import{_ as e,o as a,c as t,O as r}from"./chunks/framework.43781440.js";const f=JSON.parse('{"title":"什麼是 KernelSU?","description":"","frontmatter":{},"headers":[],"relativePath":"zh_TW/guide/what-is-kernelsu.md","filePath":"zh_TW/guide/what-is-kernelsu.md"}'),o={name:"zh_TW/guide/what-is-kernelsu.md"},n=r('

什麼是 KernelSU?

KernelSU 是 Android GKI 裝置的 Root 解決方案,它以核心模式運作,並直接在核心空間中為使用者空間應用程式授予 Root 權限。

功能

KernelSU 的主要功能是它是以核心為基礎的。 KernelSU 在核心空間中執行,所以它可以向我們提供從未有過的核心介面。例如,我們可以在核心模式中為任何處理程序新增硬體中斷點;我們可以在任何處理程序的實體記憶體中存取,而無人知曉;我們可以在核心空間攔截任何系統呼叫;等等。

KernelSU 還提供了一個以 overlayfs 為基礎的模組系統,允許您將自訂外掛程式載入到系統中。它還提供了一種修改 /system 分割區中檔案的機制。

如何使用

請參閱:安裝

如何建置

请參閱:如何建置

討論

',11),s=[n];function i(l,h,d,u,c,_){return a(),t("div",null,s)}const m=e(o,[["render",i]]);export{f as __pageData,m as default}; +import{_ as e,o as a,c as t,Q as r}from"./chunks/framework.ec8f7e8e.js";const f=JSON.parse('{"title":"什麼是 KernelSU?","description":"","frontmatter":{},"headers":[],"relativePath":"zh_TW/guide/what-is-kernelsu.md","filePath":"zh_TW/guide/what-is-kernelsu.md"}'),o={name:"zh_TW/guide/what-is-kernelsu.md"},n=r('

什麼是 KernelSU?

KernelSU 是 Android GKI 裝置的 Root 解決方案,它以核心模式運作,並直接在核心空間中為使用者空間應用程式授予 Root 權限。

功能

KernelSU 的主要功能是它是以核心為基礎的。 KernelSU 在核心空間中執行,所以它可以向我們提供從未有過的核心介面。例如,我們可以在核心模式中為任何處理程序新增硬體中斷點;我們可以在任何處理程序的實體記憶體中存取,而無人知曉;我們可以在核心空間攔截任何系統呼叫;等等。

KernelSU 還提供了一個以 overlayfs 為基礎的模組系統,允許您將自訂外掛程式載入到系統中。它還提供了一種修改 /system 分割區中檔案的機制。

如何使用

請參閱:安裝

如何建置

请參閱:如何建置

討論

',11),s=[n];function i(l,h,d,u,c,_){return a(),t("div",null,s)}const m=e(o,[["render",i]]);export{f as __pageData,m as default}; diff --git a/assets/zh_TW_guide_what-is-kernelsu.md.a2ba0439.lean.js b/assets/zh_TW_guide_what-is-kernelsu.md.c73a8b16.lean.js similarity index 71% rename from assets/zh_TW_guide_what-is-kernelsu.md.a2ba0439.lean.js rename to assets/zh_TW_guide_what-is-kernelsu.md.c73a8b16.lean.js index acad894004bc..c9361be2a400 100644 --- a/assets/zh_TW_guide_what-is-kernelsu.md.a2ba0439.lean.js +++ b/assets/zh_TW_guide_what-is-kernelsu.md.c73a8b16.lean.js @@ -1 +1 @@ -import{_ as e,o as a,c as t,O as r}from"./chunks/framework.43781440.js";const f=JSON.parse('{"title":"什麼是 KernelSU?","description":"","frontmatter":{},"headers":[],"relativePath":"zh_TW/guide/what-is-kernelsu.md","filePath":"zh_TW/guide/what-is-kernelsu.md"}'),o={name:"zh_TW/guide/what-is-kernelsu.md"},n=r("",11),s=[n];function i(l,h,d,u,c,_){return a(),t("div",null,s)}const m=e(o,[["render",i]]);export{f as __pageData,m as default}; +import{_ as e,o as a,c as t,Q as r}from"./chunks/framework.ec8f7e8e.js";const f=JSON.parse('{"title":"什麼是 KernelSU?","description":"","frontmatter":{},"headers":[],"relativePath":"zh_TW/guide/what-is-kernelsu.md","filePath":"zh_TW/guide/what-is-kernelsu.md"}'),o={name:"zh_TW/guide/what-is-kernelsu.md"},n=r("",11),s=[n];function i(l,h,d,u,c,_){return a(),t("div",null,s)}const m=e(o,[["render",i]]);export{f as __pageData,m as default}; diff --git a/assets/zh_TW_index.md.896578f0.js b/assets/zh_TW_index.md.419b5e06.js similarity index 95% rename from assets/zh_TW_index.md.896578f0.js rename to assets/zh_TW_index.md.419b5e06.js index 3a3e1b6b6603..448860d8b824 100644 --- a/assets/zh_TW_index.md.896578f0.js +++ b/assets/zh_TW_index.md.419b5e06.js @@ -1 +1 @@ -import{_ as e,o as t,c as n}from"./chunks/framework.43781440.js";const h=JSON.parse('{"title":"Android 上以核心為基礎的 Root 解決方案","description":"","frontmatter":{"layout":"home","title":"Android 上以核心為基礎的 Root 解決方案","hero":{"name":"KernelSU","text":"Android 上以核心為基礎的 root 解決方案","tagline":"","image":{"src":"/logo.png","alt":"KernelSU"},"actions":[{"theme":"brand","text":"開始瞭解","link":"/zh_TW/guide/what-is-kernelsu"},{"theme":"alt","text":"在 GitHub 中檢視","link":"https://github.com/tiann/KernelSU"}]},"features":[{"title":"以核心為基礎","details":"KernelSU 以 Linux 核心模式運作,對使用者空間有更強的掌控。"},{"title":"白名單存取控制","details":"僅有被授予 Root 權限的應用程式才可存取 `su`,而其他應用程式完全無法知悉。"},{"title":"模組支援","details":"KernelSU 支援透過 overlayfs 修改 /system,它甚至可以使 /system 可寫入。"},{"title":"開放原始碼","details":"KernelSU 是 GPL-3 授權下的開放原始碼專案。"}]},"headers":[],"relativePath":"zh_TW/index.md","filePath":"zh_TW/index.md"}'),i={name:"zh_TW/index.md"};function a(o,r,s,l,d,c){return t(),n("div")}const _=e(i,[["render",a]]);export{h as __pageData,_ as default}; +import{_ as e,o as t,c as n}from"./chunks/framework.ec8f7e8e.js";const h=JSON.parse('{"title":"Android 上以核心為基礎的 Root 解決方案","description":"","frontmatter":{"layout":"home","title":"Android 上以核心為基礎的 Root 解決方案","hero":{"name":"KernelSU","text":"Android 上以核心為基礎的 root 解決方案","tagline":"","image":{"src":"/logo.png","alt":"KernelSU"},"actions":[{"theme":"brand","text":"開始瞭解","link":"/zh_TW/guide/what-is-kernelsu"},{"theme":"alt","text":"在 GitHub 中檢視","link":"https://github.com/tiann/KernelSU"}]},"features":[{"title":"以核心為基礎","details":"KernelSU 以 Linux 核心模式運作,對使用者空間有更強的掌控。"},{"title":"白名單存取控制","details":"僅有被授予 Root 權限的應用程式才可存取 `su`,而其他應用程式完全無法知悉。"},{"title":"模組支援","details":"KernelSU 支援透過 overlayfs 修改 /system,它甚至可以使 /system 可寫入。"},{"title":"開放原始碼","details":"KernelSU 是 GPL-3 授權下的開放原始碼專案。"}]},"headers":[],"relativePath":"zh_TW/index.md","filePath":"zh_TW/index.md"}'),i={name:"zh_TW/index.md"};function a(o,r,s,l,d,c){return t(),n("div")}const _=e(i,[["render",a]]);export{h as __pageData,_ as default}; diff --git a/assets/zh_TW_index.md.896578f0.lean.js b/assets/zh_TW_index.md.419b5e06.lean.js similarity index 95% rename from assets/zh_TW_index.md.896578f0.lean.js rename to assets/zh_TW_index.md.419b5e06.lean.js index 3a3e1b6b6603..448860d8b824 100644 --- a/assets/zh_TW_index.md.896578f0.lean.js +++ b/assets/zh_TW_index.md.419b5e06.lean.js @@ -1 +1 @@ -import{_ as e,o as t,c as n}from"./chunks/framework.43781440.js";const h=JSON.parse('{"title":"Android 上以核心為基礎的 Root 解決方案","description":"","frontmatter":{"layout":"home","title":"Android 上以核心為基礎的 Root 解決方案","hero":{"name":"KernelSU","text":"Android 上以核心為基礎的 root 解決方案","tagline":"","image":{"src":"/logo.png","alt":"KernelSU"},"actions":[{"theme":"brand","text":"開始瞭解","link":"/zh_TW/guide/what-is-kernelsu"},{"theme":"alt","text":"在 GitHub 中檢視","link":"https://github.com/tiann/KernelSU"}]},"features":[{"title":"以核心為基礎","details":"KernelSU 以 Linux 核心模式運作,對使用者空間有更強的掌控。"},{"title":"白名單存取控制","details":"僅有被授予 Root 權限的應用程式才可存取 `su`,而其他應用程式完全無法知悉。"},{"title":"模組支援","details":"KernelSU 支援透過 overlayfs 修改 /system,它甚至可以使 /system 可寫入。"},{"title":"開放原始碼","details":"KernelSU 是 GPL-3 授權下的開放原始碼專案。"}]},"headers":[],"relativePath":"zh_TW/index.md","filePath":"zh_TW/index.md"}'),i={name:"zh_TW/index.md"};function a(o,r,s,l,d,c){return t(),n("div")}const _=e(i,[["render",a]]);export{h as __pageData,_ as default}; +import{_ as e,o as t,c as n}from"./chunks/framework.ec8f7e8e.js";const h=JSON.parse('{"title":"Android 上以核心為基礎的 Root 解決方案","description":"","frontmatter":{"layout":"home","title":"Android 上以核心為基礎的 Root 解決方案","hero":{"name":"KernelSU","text":"Android 上以核心為基礎的 root 解決方案","tagline":"","image":{"src":"/logo.png","alt":"KernelSU"},"actions":[{"theme":"brand","text":"開始瞭解","link":"/zh_TW/guide/what-is-kernelsu"},{"theme":"alt","text":"在 GitHub 中檢視","link":"https://github.com/tiann/KernelSU"}]},"features":[{"title":"以核心為基礎","details":"KernelSU 以 Linux 核心模式運作,對使用者空間有更強的掌控。"},{"title":"白名單存取控制","details":"僅有被授予 Root 權限的應用程式才可存取 `su`,而其他應用程式完全無法知悉。"},{"title":"模組支援","details":"KernelSU 支援透過 overlayfs 修改 /system,它甚至可以使 /system 可寫入。"},{"title":"開放原始碼","details":"KernelSU 是 GPL-3 授權下的開放原始碼專案。"}]},"headers":[],"relativePath":"zh_TW/index.md","filePath":"zh_TW/index.md"}'),i={name:"zh_TW/index.md"};function a(o,r,s,l,d,c){return t(),n("div")}const _=e(i,[["render",a]]);export{h as __pageData,_ as default}; diff --git a/guide/app-profile.html b/guide/app-profile.html index 08f2ef4711f3..af36000b5d32 100644 --- a/guide/app-profile.html +++ b/guide/app-profile.html @@ -5,22 +5,27 @@ App Profile | KernelSU - - + + + - - - - + + + + + -
Skip to content
On this page

App Profile

The App Profile is a mechanism provided by KernelSU for customizing the configuration of various applications.

For applications granted root permissions (i.e., able to use su), the App Profile can also be referred to as the Root Profile. It allows customization of the uid, gid, groups, capabilities, and SELinux rules of the su command, thereby restricting the privileges of the root user. For example, it can grant network permissions only to firewall applications while denying file access permissions, or it can grant shell permissions instead of full root access for freeze applications: keeping the power confined with the principle of least privilege.

For ordinary applications without root permissions, the App Profile can control the behavior of the kernel and module system towards these applications. For instance, it can determine whether modifications resulting from modules should be addressed. The kernel and module system can make decisions based on this configuration, such as performing operations akin to "hiding"

Root Profile

UID, GID, and Groups

Linux systems have two concepts: users and groups. Each user has a user ID (UID), and a user can belong to multiple groups, each with its own group ID (GID). These IDs are used to identify users in the system and determine which system resources they can access.

Users with a UID of 0 are known as root users, and groups with a GID of 0 are known as root groups. The root user group typically holds the highest system privileges.

In the case of the Android system, each app is a separate user (excluding shared UID scenarios) with a unique UID. For example, 0 represents the root user, 1000 represents system, 2000 represents the ADB shell, and UIDs ranging from 10000 to 19999 represent ordinary apps.

INFO

Here, the UID mentioned is not the same as the concept of multiple users or work profiles in the Android system. Work profiles are actually implemented by partitioning the UID range. For example, 10000-19999 represents the main user, while 110000-119999 represents a work profile. Each ordinary app among them has its own unique UID.

Each app can have several groups, with the GID representing the primary group, which usually matches the UID. Other groups are known as supplementary groups. Certain permissions are controlled through groups, such as network access permissions or Bluetooth access.

For example, if we execute the id command in ADB shell, the output might look like this:

sh
oriole:/ $ id
-uid=2000(shell) gid=2000(shell) groups=2000(shell),1004(input),1007(log),1011(adb),1015(sdcard_rw),1028(sdcard_r),1078(ext_data_rw),1079(ext_obb_rw),3001(net_bt_admin),3002(net_bt),3003(inet),3006(net_bw_stats),3009(readproc),3011(uhid),3012(readtracefs) context=u:r:shell:s0

Here, the UID is 2000, and the GID (primary group ID) is also 2000. Additionally, it belongs to several supplementary groups, such as inet (indicating the ability to create AF_INET and AF_INET6 sockets) and sdcard_rw (indicating read/write permissions for the SD card).

KernelSU's Root Profile allows customization of the UID, GID, and groups for the root process after executing su. For example, the Root Profile of a root app can set its UID to 2000, which means that when using su, the app's actual permissions are at the ADB shell level. The inet group can be removed, preventing the su command from accessing the network.

Note

The App Profile only controls the permissions of the root process after using su; it does not control the permissions of the app itself. If an app has requested network access permission, it can still access the network even without using su. Removing the inet group from su only prevents su from accessing the network.

Root Profile is enforced in the kernel and does not rely on the voluntary behavior of root applications, unlike switching users or groups through su, the granting of su permission is entirely up to the user rather than the developer.

Capabilities

Capabilities are a mechanism for privilege separation in Linux.

For the purpose of performing permission checks, traditional UNIX implementations distinguish two categories of processes: privileged processes (whose effective user ID is 0, referred to as superuser or root), and unprivileged processes (whose effective UID is nonzero). Privileged processes bypass all kernel permission checks, while unprivileged processes are subject to full permission checking based on the process's credentials (usually: effective UID, effective GID, and supplementary group list).

Starting with Linux 2.2, Linux divides the privileges traditionally associated with superuser into distinct units, known as capabilities, which can be independently enabled and disabled.

Each Capability represents one or more privileges. For example, CAP_DAC_READ_SEARCH represents the ability to bypass permission checks for file reading, as well as directory reading and execution permissions. If a user with an effective UID of 0 (root user) lacks CAP_DAC_READ_SEARCH or higher capabilities, this means that even though they are root, they cannot read files at will.

KernelSU's Root Profile allows customization of the Capabilities of the root process after executing su, thereby achieving partially granting "root permissions." Unlike the aforementioned UID and GID, certain root apps require a UID of 0 after using su. In such cases, limiting the Capabilities of this root user with UID 0 can restrict their allowed operations.

Strong Recommendation

Linux's Capability official documentation provides detailed explanations of the abilities represented by each Capability. If you intend to customize Capabilities, it is strongly recommended that you read this document first.

SELinux

SELinux is a powerful Mandatory Access Control (MAC) mechanism. It operates on the principle of default deny: any action not explicitly allowed is denied.

SELinux can run in two global modes:

  1. Permissive mode: Denial events are logged but not enforced.
  2. Enforcing mode: Denial events are logged and enforced.

Warning

Modern Android systems heavily rely on SELinux to ensure overall system security. It is highly recommended not to use any custom systems running in "permissive mode" since it provides no significant advantages over a completely open system.

Explaining the full concept of SELinux is complex and beyond the scope of this document. It is recommended to first understand its workings through the following resources:

  1. Wikipedia
  2. Red Hat: What Is SELinux?
  3. ArchLinux: SELinux

KernelSU's Root Profile allows customization of the SELinux context of the root process after executing su. Specific access control rules can be set for this context to enable fine-grained control over root permissions.

In typical scenarios, when an app executes su, it switches the process to a SELinux domain with unrestricted access, such as u:r:su:s0. Through the Root Profile, this domain can be switched to a custom domain, such as u:r:app1:s0, and a series of rules can be defined for this domain:

sh
type app1
-enforce app1
-typeattribute app1 mlstrustedsubject
-allow app1 * * *

Note that the allow app1 * * * rule is used for demonstration purposes only. In practice, this rule should not be used extensively since it doesn't differ much from permissive mode.

Escalation

If the configuration of the Root Profile is not set properly, an escalation scenario may occur: the restrictions imposed by the Root Profile can unintentionally fail.

For example, if you grant root permission to an ADB shell user (which is a common case), and then you grant root permission to a regular application but configure its root profile with UID 2000 (which is the UID of the ADB shell user), the application can obtain full root access by executing the su command twice:

  1. The first su execution is subject to the enforcement of the App Profile and will switch to UID 2000 (adb shell) instead of 0 (root).
  2. The second su execution, since the UID is 2000, and you have granted root access to the UID 2000 (adb shell) in the configuration, the application will gain full root privileges.

Note

This behavior is entirely expected and not a bug. Therefore, we recommend the following:

If you genuinely need to grant root permissions to ADB (e.g., as a developer), it is not advisable to change the UID to 2000 when configuring the Root Profile. Using 1000 (system) would be a better choice.

Non-Root Profile

Umount Modules

KernelSU provides a systemless mechanism for modifying system partitions, achieved through overlayfs mounting. However, some apps may be sensitive to such behavior. Thus, we can unload modules mounted on these apps by setting the "umount modules" option.

Additionally, the settings interface of the KernelSU manager provides a switch for "umount modules by default". By default, this switch is enabled, which means that KernelSU or some modules will unload modules for this app unless additional settings are applied. If you do not prefer this setting or if it affects certain apps, you have the following options:

  1. Keep the switch for "umount modules by default" and individually disable the "umount modules" option in the App Profile for apps requiring module loading (acting as a "whitelist").
  2. Disable the switch for "umount modules by default" and individually enable the "umount modules" option in the App Profile for apps requiring module unloading (acting as a "blacklist").

INFO

In devices using kernel version 5.10 and above, the kernel performs the unloading of modules. However, for devices running kernel versions below 5.10, this switch is merely a configuration option, and KernelSU itself does not take any action. Some modules, such as Zygisksu, may use this switch to determine whether module unloading is necessary.

Released under the GPL3 License.

- +
Skip to content

App Profile

The App Profile is a mechanism provided by KernelSU for customizing the configuration of various applications.

For applications granted root permissions (i.e., able to use su), the App Profile can also be referred to as the Root Profile. It allows customization of the uid, gid, groups, capabilities, and SELinux rules of the su command, thereby restricting the privileges of the root user. For example, it can grant network permissions only to firewall applications while denying file access permissions, or it can grant shell permissions instead of full root access for freeze applications: keeping the power confined with the principle of least privilege.

For ordinary applications without root permissions, the App Profile can control the behavior of the kernel and module system towards these applications. For instance, it can determine whether modifications resulting from modules should be addressed. The kernel and module system can make decisions based on this configuration, such as performing operations akin to "hiding"

Root Profile

UID, GID, and Groups

Linux systems have two concepts: users and groups. Each user has a user ID (UID), and a user can belong to multiple groups, each with its own group ID (GID). These IDs are used to identify users in the system and determine which system resources they can access.

Users with a UID of 0 are known as root users, and groups with a GID of 0 are known as root groups. The root user group typically holds the highest system privileges.

In the case of the Android system, each app is a separate user (excluding shared UID scenarios) with a unique UID. For example, 0 represents the root user, 1000 represents system, 2000 represents the ADB shell, and UIDs ranging from 10000 to 19999 represent ordinary apps.

INFO

Here, the UID mentioned is not the same as the concept of multiple users or work profiles in the Android system. Work profiles are actually implemented by partitioning the UID range. For example, 10000-19999 represents the main user, while 110000-119999 represents a work profile. Each ordinary app among them has its own unique UID.

Each app can have several groups, with the GID representing the primary group, which usually matches the UID. Other groups are known as supplementary groups. Certain permissions are controlled through groups, such as network access permissions or Bluetooth access.

For example, if we execute the id command in ADB shell, the output might look like this:

sh
oriole:/ $ id
+uid=2000(shell) gid=2000(shell) groups=2000(shell),1004(input),1007(log),1011(adb),1015(sdcard_rw),1028(sdcard_r),1078(ext_data_rw),1079(ext_obb_rw),3001(net_bt_admin),3002(net_bt),3003(inet),3006(net_bw_stats),3009(readproc),3011(uhid),3012(readtracefs) context=u:r:shell:s0
oriole:/ $ id
+uid=2000(shell) gid=2000(shell) groups=2000(shell),1004(input),1007(log),1011(adb),1015(sdcard_rw),1028(sdcard_r),1078(ext_data_rw),1079(ext_obb_rw),3001(net_bt_admin),3002(net_bt),3003(inet),3006(net_bw_stats),3009(readproc),3011(uhid),3012(readtracefs) context=u:r:shell:s0

Here, the UID is 2000, and the GID (primary group ID) is also 2000. Additionally, it belongs to several supplementary groups, such as inet (indicating the ability to create AF_INET and AF_INET6 sockets) and sdcard_rw (indicating read/write permissions for the SD card).

KernelSU's Root Profile allows customization of the UID, GID, and groups for the root process after executing su. For example, the Root Profile of a root app can set its UID to 2000, which means that when using su, the app's actual permissions are at the ADB shell level. The inet group can be removed, preventing the su command from accessing the network.

Note

The App Profile only controls the permissions of the root process after using su; it does not control the permissions of the app itself. If an app has requested network access permission, it can still access the network even without using su. Removing the inet group from su only prevents su from accessing the network.

Root Profile is enforced in the kernel and does not rely on the voluntary behavior of root applications, unlike switching users or groups through su, the granting of su permission is entirely up to the user rather than the developer.

Capabilities

Capabilities are a mechanism for privilege separation in Linux.

For the purpose of performing permission checks, traditional UNIX implementations distinguish two categories of processes: privileged processes (whose effective user ID is 0, referred to as superuser or root), and unprivileged processes (whose effective UID is nonzero). Privileged processes bypass all kernel permission checks, while unprivileged processes are subject to full permission checking based on the process's credentials (usually: effective UID, effective GID, and supplementary group list).

Starting with Linux 2.2, Linux divides the privileges traditionally associated with superuser into distinct units, known as capabilities, which can be independently enabled and disabled.

Each Capability represents one or more privileges. For example, CAP_DAC_READ_SEARCH represents the ability to bypass permission checks for file reading, as well as directory reading and execution permissions. If a user with an effective UID of 0 (root user) lacks CAP_DAC_READ_SEARCH or higher capabilities, this means that even though they are root, they cannot read files at will.

KernelSU's Root Profile allows customization of the Capabilities of the root process after executing su, thereby achieving partially granting "root permissions." Unlike the aforementioned UID and GID, certain root apps require a UID of 0 after using su. In such cases, limiting the Capabilities of this root user with UID 0 can restrict their allowed operations.

Strong Recommendation

Linux's Capability official documentation provides detailed explanations of the abilities represented by each Capability. If you intend to customize Capabilities, it is strongly recommended that you read this document first.

SELinux

SELinux is a powerful Mandatory Access Control (MAC) mechanism. It operates on the principle of default deny: any action not explicitly allowed is denied.

SELinux can run in two global modes:

  1. Permissive mode: Denial events are logged but not enforced.
  2. Enforcing mode: Denial events are logged and enforced.

Warning

Modern Android systems heavily rely on SELinux to ensure overall system security. It is highly recommended not to use any custom systems running in "permissive mode" since it provides no significant advantages over a completely open system.

Explaining the full concept of SELinux is complex and beyond the scope of this document. It is recommended to first understand its workings through the following resources:

  1. Wikipedia
  2. Red Hat: What Is SELinux?
  3. ArchLinux: SELinux

KernelSU's Root Profile allows customization of the SELinux context of the root process after executing su. Specific access control rules can be set for this context to enable fine-grained control over root permissions.

In typical scenarios, when an app executes su, it switches the process to a SELinux domain with unrestricted access, such as u:r:su:s0. Through the Root Profile, this domain can be switched to a custom domain, such as u:r:app1:s0, and a series of rules can be defined for this domain:

sh
type app1
+enforce app1
+typeattribute app1 mlstrustedsubject
+allow app1 * * *
type app1
+enforce app1
+typeattribute app1 mlstrustedsubject
+allow app1 * * *

Note that the allow app1 * * * rule is used for demonstration purposes only. In practice, this rule should not be used extensively since it doesn't differ much from permissive mode.

Escalation

If the configuration of the Root Profile is not set properly, an escalation scenario may occur: the restrictions imposed by the Root Profile can unintentionally fail.

For example, if you grant root permission to an ADB shell user (which is a common case), and then you grant root permission to a regular application but configure its root profile with UID 2000 (which is the UID of the ADB shell user), the application can obtain full root access by executing the su command twice:

  1. The first su execution is subject to the enforcement of the App Profile and will switch to UID 2000 (adb shell) instead of 0 (root).
  2. The second su execution, since the UID is 2000, and you have granted root access to the UID 2000 (adb shell) in the configuration, the application will gain full root privileges.

Note

This behavior is entirely expected and not a bug. Therefore, we recommend the following:

If you genuinely need to grant root permissions to ADB (e.g., as a developer), it is not advisable to change the UID to 2000 when configuring the Root Profile. Using 1000 (system) would be a better choice.

Non-Root Profile

Umount Modules

KernelSU provides a systemless mechanism for modifying system partitions, achieved through overlayfs mounting. However, some apps may be sensitive to such behavior. Thus, we can unload modules mounted on these apps by setting the "umount modules" option.

Additionally, the settings interface of the KernelSU manager provides a switch for "umount modules by default". By default, this switch is enabled, which means that KernelSU or some modules will unload modules for this app unless additional settings are applied. If you do not prefer this setting or if it affects certain apps, you have the following options:

  1. Keep the switch for "umount modules by default" and individually disable the "umount modules" option in the App Profile for apps requiring module loading (acting as a "whitelist").
  2. Disable the switch for "umount modules by default" and individually enable the "umount modules" option in the App Profile for apps requiring module unloading (acting as a "blacklist").

INFO

In devices using kernel version 5.10 and above, the kernel performs the unloading of modules. However, for devices running kernel versions below 5.10, this switch is merely a configuration option, and KernelSU itself does not take any action. Some modules, such as Zygisksu, may use this switch to determine whether module unloading is necessary.

Released under the GPL3 License.

+ \ No newline at end of file diff --git a/guide/difference-with-magisk.html b/guide/difference-with-magisk.html index 3b24a98a9e74..107cb7116e19 100644 --- a/guide/difference-with-magisk.html +++ b/guide/difference-with-magisk.html @@ -5,18 +5,19 @@ Difference with Magisk | KernelSU - - + + + - - - - + + + + + -
Skip to content
On this page

Difference with Magisk

Although there are many similarities between KernelSU modules and Magisk modules, there are inevitably some differences due to their completely different implementation mechanisms. If you want your module to run on both Magisk and KernelSU, you must understand these differences.

Similarities

  • Module file format: both use zip format to organize modules, and the format of modules is almost the same
  • Module installation directory: both located in /data/adb/modules
  • Systemless: both support modifying /system in a systemless way through modules
  • post-fs-data.sh: the execution time and semantics are exactly the same
  • service.sh: the execution time and semantics are exactly the same
  • system.prop: completely the same
  • sepolicy.rule: completely the same
  • BusyBox: scripts are run in BusyBox with "standalone mode" enabled in both cases

Differences

Before understanding the differences, you need to know how to differentiate whether your module is running in KernelSU or Magisk. You can use the environment variable KSU to differentiate it in all places where you can run module scripts (customize.sh, post-fs-data.sh, service.sh). In KernelSU, this environment variable will be set to true.

Here are some differences:

  • KernelSU modules cannot be installed in Recovery mode.
  • KernelSU modules do not have built-in support for Zygisk (but you can use Zygisk modules through ZygiskNext.
  • The method for replacing or deleting files in KernelSU modules is completely different from Magisk. KernelSU does not support the .replace method. Instead, you need to create a same-named file with mknod filename c 0 0 to delete the corresponding file.
  • The directories for BusyBox are different. The built-in BusyBox in KernelSU is located in /data/adb/ksu/bin/busybox, while in Magisk it is in /data/adb/magisk/busybox. Note that this is an internal behavior of KernelSU and may change in the future!
  • KernelSU does not support .replace files; however, KernelSU supports the REMOVE and REPLACE variable to remove or replace files and folders.
  • KernelSU adds boot-completed stage to run some scripts on boot completed.
  • KernelSU adds post-mount stage to run some scripts after mounting overlayfs

Released under the GPL3 License.

- +
Skip to content

Difference with Magisk

Although there are many similarities between KernelSU modules and Magisk modules, there are inevitably some differences due to their completely different implementation mechanisms. If you want your module to run on both Magisk and KernelSU, you must understand these differences.

Similarities

  • Module file format: both use zip format to organize modules, and the format of modules is almost the same
  • Module installation directory: both located in /data/adb/modules
  • Systemless: both support modifying /system in a systemless way through modules
  • post-fs-data.sh: the execution time and semantics are exactly the same
  • service.sh: the execution time and semantics are exactly the same
  • system.prop: completely the same
  • sepolicy.rule: completely the same
  • BusyBox: scripts are run in BusyBox with "standalone mode" enabled in both cases

Differences

Before understanding the differences, you need to know how to differentiate whether your module is running in KernelSU or Magisk. You can use the environment variable KSU to differentiate it in all places where you can run module scripts (customize.sh, post-fs-data.sh, service.sh). In KernelSU, this environment variable will be set to true.

Here are some differences:

  • KernelSU modules cannot be installed in Recovery mode.
  • KernelSU modules do not have built-in support for Zygisk (but you can use Zygisk modules through ZygiskNext.
  • The method for replacing or deleting files in KernelSU modules is completely different from Magisk. KernelSU does not support the .replace method. Instead, you need to create a same-named file with mknod filename c 0 0 to delete the corresponding file.
  • The directories for BusyBox are different. The built-in BusyBox in KernelSU is located in /data/adb/ksu/bin/busybox, while in Magisk it is in /data/adb/magisk/busybox. Note that this is an internal behavior of KernelSU and may change in the future!
  • KernelSU does not support .replace files; however, KernelSU supports the REMOVE and REPLACE variable to remove or replace files and folders.
  • KernelSU adds boot-completed stage to run some scripts on boot completed.
  • KernelSU adds post-mount stage to run some scripts after mounting overlayfs

Released under the GPL3 License.

+ \ No newline at end of file diff --git a/guide/faq.html b/guide/faq.html index 231cecdcfd74..46a49b6b05a0 100644 --- a/guide/faq.html +++ b/guide/faq.html @@ -5,18 +5,19 @@ FAQ | KernelSU - - + + + - - - - + + + + + -
Skip to content
On this page

FAQ

Does KernelSU support my device?

First, your devices should be able to unlock the bootloader. If it can't, then it is unsupported.

Then install KernelSU manager App to your device and open it, if it shows Unsupported then your device cannot be supported out of box, but you can build kernel source and integrate KernelSU to make it work or using unofficially-support-devices.

Does KernelSU need to unlock Bootloader?

Certainly, yes.

Does KernelSU support modules?

Yes, But it is in early version, it may be buggy. Please wait for it to be stable 😃

Does KernelSU support Xposed?

Yes, Dreamland and TaiChi work now. For LSPosed, you can make it work by ZygiskNext

Does KernelSU support Zygisk?

KernelSU has no builtin Zygisk support, but you can use ZygiskNext instead.

Is KernelSU compatible with Magisk?

KernelSU's module system is conflict with Magisk's magic mount, if there is any module enabled in KernelSU, then the whole Magisk would not work.

But if you only use the su of KernelSU, then it will work well with Magisk: KernelSU modify the kernel and Magisk modify the ramdisk, they can work together.

Will KernelSU substitute Magisk?

We don't think so and it's not our goal. Magisk is good enough for userspace root solution and it will live long. KernelSU's goal is to provide a kernel interface to users, not substituting Magisk.

Can KernelSU support non GKI devices?

It is possible. But you should download the kernel source and intergrate KernelSU to the source tree and compile the kernel yourself.

Can KernelSU support devices below Android 12?

It is device's kernel that affect KernelSU's compatability and it has nothing to do with Android version.The only restriction is that devices launched with Android 12 must be kernel 5.10+(GKI devices). So:

  1. Devices launched with Android 12 must be supported.
  2. Devices with has an old kernel (Some Android 12 devices is also old kernel) is compatable (You should build kernel yourself)

Can KernelSU support old kernel?

It is possible, KernelSU is backported to kernel 4.14 now, for older kernel, you need to backport it manully and PRs welcome!

How to integrate KernelSU for old kernel?

Please refer guide

Why my Android version is 13, and the kernel shows "android12-5.10"?

The Kernel version has nothing to do with Android version, if you need to flash kernel, always use the kernel version, Android version is not so important.

Is there any --mount-master/global mount namespace in KernelSU?

There isn't now(maybe in the future), But there are many ways to switch to global mount namespace manully, such as:

  1. nsenter -t 1 -m sh to get a shell in global mount namespace.
  2. Add nsenter --mount=/proc/1/ns/mnt to the command you want to execute, then the command is executed in global mount namespace. KernelSU is also using this way

I am GKI1.0, can i use this?

GKI1 is completely different from GKI2, you must compile kernel by yourself.

Released under the GPL3 License.

- +
Skip to content

FAQ

Does KernelSU support my device?

First, your devices should be able to unlock the bootloader. If it can't, then it is unsupported.

Then install KernelSU manager App to your device and open it, if it shows Unsupported then your device cannot be supported out of box, but you can build kernel source and integrate KernelSU to make it work or using unofficially-support-devices.

Does KernelSU need to unlock Bootloader?

Certainly, yes.

Does KernelSU support modules?

Yes, But it is in early version, it may be buggy. Please wait for it to be stable 😃

Does KernelSU support Xposed?

Yes, Dreamland and TaiChi work now. For LSPosed, you can make it work by ZygiskNext

Does KernelSU support Zygisk?

KernelSU has no builtin Zygisk support, but you can use ZygiskNext instead.

Is KernelSU compatible with Magisk?

KernelSU's module system is conflict with Magisk's magic mount, if there is any module enabled in KernelSU, then the whole Magisk would not work.

But if you only use the su of KernelSU, then it will work well with Magisk: KernelSU modify the kernel and Magisk modify the ramdisk, they can work together.

Will KernelSU substitute Magisk?

We don't think so and it's not our goal. Magisk is good enough for userspace root solution and it will live long. KernelSU's goal is to provide a kernel interface to users, not substituting Magisk.

Can KernelSU support non GKI devices?

It is possible. But you should download the kernel source and intergrate KernelSU to the source tree and compile the kernel yourself.

Can KernelSU support devices below Android 12?

It is device's kernel that affect KernelSU's compatability and it has nothing to do with Android version.The only restriction is that devices launched with Android 12 must be kernel 5.10+(GKI devices). So:

  1. Devices launched with Android 12 must be supported.
  2. Devices with has an old kernel (Some Android 12 devices is also old kernel) is compatable (You should build kernel yourself)

Can KernelSU support old kernel?

It is possible, KernelSU is backported to kernel 4.14 now, for older kernel, you need to backport it manully and PRs welcome!

How to integrate KernelSU for old kernel?

Please refer guide

Why my Android version is 13, and the kernel shows "android12-5.10"?

The Kernel version has nothing to do with Android version, if you need to flash kernel, always use the kernel version, Android version is not so important.

Is there any --mount-master/global mount namespace in KernelSU?

There isn't now(maybe in the future), But there are many ways to switch to global mount namespace manully, such as:

  1. nsenter -t 1 -m sh to get a shell in global mount namespace.
  2. Add nsenter --mount=/proc/1/ns/mnt to the command you want to execute, then the command is executed in global mount namespace. KernelSU is also using this way

I am GKI1.0, can i use this?

GKI1 is completely different from GKI2, you must compile kernel by yourself.

Released under the GPL3 License.

+ \ No newline at end of file diff --git a/guide/hidden-features.html b/guide/hidden-features.html index 6f5ea1237a39..6cf7c21552b5 100644 --- a/guide/hidden-features.html +++ b/guide/hidden-features.html @@ -5,18 +5,19 @@ Hidden Features | KernelSU - - + + + - - - - + + + + + -
Skip to content
On this page

Hidden Features

.ksurc

By default, /system/bin/sh loads /system/etc/mkshrc.

You can make su load customized rc file by creating a /data/adb/ksu/.ksurc file.

Released under the GPL3 License.

- +
Skip to content

Hidden Features

.ksurc

By default, /system/bin/sh loads /system/etc/mkshrc.

You can make su load customized rc file by creating a /data/adb/ksu/.ksurc file.

Released under the GPL3 License.

+ \ No newline at end of file diff --git a/guide/how-to-build.html b/guide/how-to-build.html index ac3a7330bbc0..9ce582e915a4 100644 --- a/guide/how-to-build.html +++ b/guide/how-to-build.html @@ -5,21 +5,25 @@ How to build KernelSU? | KernelSU - - + + + - - - - + + + + + -
Skip to content
On this page

How to build KernelSU?

First, you should read the Android Official docs for kernel build:

  1. Building Kernels
  2. GKI Release Builds

WARNING

This page is for GKI devices, if you use an old kernel, please refer how to integrate KernelSU for old kernel

Build Kernel

Sync the kernel source code

sh
repo init -u https://android.googlesource.com/kernel/manifest
-mv <kernel_manifest.xml> .repo/manifests
-repo init -m manifest.xml
-repo sync

The <kernel_manifest.xml> is a manifest file which can determine a build uniquely, you can use the manifest to do a re-preducable build. You should download the manifest file from Google GKI release builds

Build

Please check the official docs first.

For example, we need to build aarch64 kernel image:

sh
LTO=thin BUILD_CONFIG=common/build.config.gki.aarch64 build/build.sh

Don't forget to add the LTO=thin flag, otherwise the build may fail if your computer's memory is less then 24Gb.

Starting from Android 13, the kernel is built by bazel:

sh
tools/bazel build --config=fast //common:kernel_aarch64_dist

Build Kernel with KernelSU

If you can build the kernel successfully, then build KernelSU is so easy, Select any one run in Kernel source root dir:

  • Latest tag(stable)
sh
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -
  • main branch(dev)
sh
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -s main
  • Select tag(Such as v0.5.2)
sh
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -s v0.5.2

And then rebuild the kernel and you will get a kernel image with KernelSU!

Released under the GPL3 License.

- +
Skip to content

How to build KernelSU?

First, you should read the Android Official docs for kernel build:

  1. Building Kernels
  2. GKI Release Builds

WARNING

This page is for GKI devices, if you use an old kernel, please refer how to integrate KernelSU for old kernel

Build Kernel

Sync the kernel source code

sh
repo init -u https://android.googlesource.com/kernel/manifest
+mv <kernel_manifest.xml> .repo/manifests
+repo init -m manifest.xml
+repo sync
repo init -u https://android.googlesource.com/kernel/manifest
+mv <kernel_manifest.xml> .repo/manifests
+repo init -m manifest.xml
+repo sync

The <kernel_manifest.xml> is a manifest file which can determine a build uniquely, you can use the manifest to do a re-preducable build. You should download the manifest file from Google GKI release builds

Build

Please check the official docs first.

For example, we need to build aarch64 kernel image:

sh
LTO=thin BUILD_CONFIG=common/build.config.gki.aarch64 build/build.sh
LTO=thin BUILD_CONFIG=common/build.config.gki.aarch64 build/build.sh

Don't forget to add the LTO=thin flag, otherwise the build may fail if your computer's memory is less then 24Gb.

Starting from Android 13, the kernel is built by bazel:

sh
tools/bazel build --config=fast //common:kernel_aarch64_dist
tools/bazel build --config=fast //common:kernel_aarch64_dist

Build Kernel with KernelSU

If you can build the kernel successfully, then build KernelSU is so easy, Select any one run in Kernel source root dir:

  • Latest tag(stable)
sh
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -
  • main branch(dev)
sh
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -s main
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -s main
  • Select tag(Such as v0.5.2)
sh
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -s v0.5.2
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -s v0.5.2

And then rebuild the kernel and you will get a kernel image with KernelSU!

Released under the GPL3 License.

+ \ No newline at end of file diff --git a/guide/how-to-integrate-for-non-gki.html b/guide/how-to-integrate-for-non-gki.html index e6d31edbb7e4..94f470f36e92 100644 --- a/guide/how-to-integrate-for-non-gki.html +++ b/guide/how-to-integrate-for-non-gki.html @@ -5,169 +5,321 @@ How to integrate KernelSU for non GKI kernels? | KernelSU - - + + + - - - - + + + + + -
Skip to content
On this page

How to integrate KernelSU for non GKI kernels?

KernelSU can be integrated into non GKI kernels, and was backported to 4.14 and below.

Due to the fragmentization of non GKI kernels, we do not have a uniform way to build it, so we can not provide non GKI boot images. But you can build the kernel yourself with KernelSU integrated.

First, you should be able to build a bootable kernel from kernel source code. If the kernel is not open source, then it is difficult to run KernelSU for your device.

If you can build a bootable kernel, there are two ways to integrate KernelSU to the kernel source code:

  1. Automatically with kprobe
  2. Manually

Integrate with kprobe

KernelSU uses kprobe to do kernel hooks, if the kprobe runs well in your kernel, it is recommended to use this way.

First, add KernelSU to your kernel source tree:

sh
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -

Then, you should check if kprobe is enabled in your kernel config, if it is not, please add these configs to it:

CONFIG_KPROBES=y
-CONFIG_HAVE_KPROBES=y
-CONFIG_KPROBE_EVENTS=y

And build your kernel again, KernelSU should works well.

If you find that KPROBES is still not activated, you can try enabling CONFIG_MODULES. (If it still doesn't take effect, use make menuconfig to search for other dependencies of KPROBES)

But if you encounter a boot loop when integrated KernelSU, it is maybe kprobe is broken in your kernel, you should fix the kprobe bug or use the second way.

How to check if kprobe is broken?

comment out ksu_enable_sucompat() and ksu_enable_ksud() in KernelSU/kernel/ksu.c, if the device boots normally, then kprobe may be broken.

Manually modify the kernel source

If kprobe does not work in your kernel (may be an upstream or kernel bug below 4.8), then you can try this way:

First, add KernelSU to your kernel source tree:

  • Latest tag(stable)
sh
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -
  • main branch(dev)
sh
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -s main
  • Select tag(Such as v0.5.2)
sh
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -s v0.5.2

Then, add KernelSU calls to the kernel source, here is a patch to refer:

diff
diff --git a/fs/exec.c b/fs/exec.c
-index ac59664eaecf..bdd585e1d2cc 100644
---- a/fs/exec.c
-+++ b/fs/exec.c
-@@ -1890,11 +1890,14 @@ static int __do_execve_file(int fd, struct filename *filename,
- 	return retval;
- }
- 
-+extern bool ksu_execveat_hook __read_mostly;
-+extern int ksu_handle_execveat(int *fd, struct filename **filename_ptr, void *argv,
-+			void *envp, int *flags);
-+extern int ksu_handle_execveat_sucompat(int *fd, struct filename **filename_ptr,
-+				 void *argv, void *envp, int *flags);
- static int do_execveat_common(int fd, struct filename *filename,
- 			      struct user_arg_ptr argv,
- 			      struct user_arg_ptr envp,
- 			      int flags)
- {
-+	if (unlikely(ksu_execveat_hook))
-+		ksu_handle_execveat(&fd, &filename, &argv, &envp, &flags);
-+	else
-+		ksu_handle_execveat_sucompat(&fd, &filename, &argv, &envp, &flags);
- 	return __do_execve_file(fd, filename, argv, envp, flags, NULL);
- }
- 
-diff --git a/fs/open.c b/fs/open.c
-index 05036d819197..965b84d486b8 100644
---- a/fs/open.c
-+++ b/fs/open.c
-@@ -348,6 +348,8 @@ SYSCALL_DEFINE4(fallocate, int, fd, int, mode, loff_t, offset, loff_t, len)
- 	return ksys_fallocate(fd, mode, offset, len);
- }
- 
-+extern int ksu_handle_faccessat(int *dfd, const char __user **filename_user, int *mode,
-+			 int *flags);
- /*
-  * access() needs to use the real uid/gid, not the effective uid/gid.
-  * We do this by temporarily clearing all FS-related capabilities and
-@@ -355,6 +357,7 @@ SYSCALL_DEFINE4(fallocate, int, fd, int, mode, loff_t, offset, loff_t, len)
-  */
- long do_faccessat(int dfd, const char __user *filename, int mode)
- {
-+	ksu_handle_faccessat(&dfd, &filename, &mode, NULL);
- 	const struct cred *old_cred;
- 	struct cred *override_cred;
- 	struct path path;
-diff --git a/fs/read_write.c b/fs/read_write.c
-index 650fc7e0f3a6..55be193913b6 100644
---- a/fs/read_write.c
-+++ b/fs/read_write.c
-@@ -434,10 +434,14 @@ ssize_t kernel_read(struct file *file, void *buf, size_t count, loff_t *pos)
- }
- EXPORT_SYMBOL(kernel_read);
- 
-+extern bool ksu_vfs_read_hook __read_mostly;
-+extern int ksu_handle_vfs_read(struct file **file_ptr, char __user **buf_ptr,
-+			size_t *count_ptr, loff_t **pos);
- ssize_t vfs_read(struct file *file, char __user *buf, size_t count, loff_t *pos)
- {
- 	ssize_t ret;
- 
-+	if (unlikely(ksu_vfs_read_hook))
-+		ksu_handle_vfs_read(&file, &buf, &count, &pos);
-+
- 	if (!(file->f_mode & FMODE_READ))
- 		return -EBADF;
- 	if (!(file->f_mode & FMODE_CAN_READ))
-diff --git a/fs/stat.c b/fs/stat.c
-index 376543199b5a..82adcef03ecc 100644
---- a/fs/stat.c
-+++ b/fs/stat.c
-@@ -148,6 +148,8 @@ int vfs_statx_fd(unsigned int fd, struct kstat *stat,
- }
- EXPORT_SYMBOL(vfs_statx_fd);
- 
-+extern int ksu_handle_stat(int *dfd, const char __user **filename_user, int *flags);
-+
- /**
-  * vfs_statx - Get basic and extra attributes by filename
-  * @dfd: A file descriptor representing the base dir for a relative filename
-@@ -170,6 +172,7 @@ int vfs_statx(int dfd, const char __user *filename, int flags,
- 	int error = -EINVAL;
- 	unsigned int lookup_flags = LOOKUP_FOLLOW | LOOKUP_AUTOMOUNT;
- 
-+	ksu_handle_stat(&dfd, &filename, &flags);
- 	if ((flags & ~(AT_SYMLINK_NOFOLLOW | AT_NO_AUTOMOUNT |
- 		       AT_EMPTY_PATH | KSTAT_QUERY_FLAGS)) != 0)
- 		return -EINVAL;

You should find the four functions in kernel source:

  1. do_faccessat, usually in fs/open.c
  2. do_execveat_common, usually in fs/exec.c
  3. vfs_read, usually in fs/read_write.c
  4. vfs_statx, usually in fs/stat.c

If your kernel does not have the vfs_statx, use vfs_fstatat instead:

diff
diff --git a/fs/stat.c b/fs/stat.c
-index 068fdbcc9e26..5348b7bb9db2 100644
---- a/fs/stat.c
-+++ b/fs/stat.c
-@@ -87,6 +87,8 @@ int vfs_fstat(unsigned int fd, struct kstat *stat)
- }
- EXPORT_SYMBOL(vfs_fstat);
- 
-+extern int ksu_handle_stat(int *dfd, const char __user **filename_user, int *flags);
-+
- int vfs_fstatat(int dfd, const char __user *filename, struct kstat *stat,
- 		int flag)
- {
-@@ -94,6 +96,8 @@ int vfs_fstatat(int dfd, const char __user *filename, struct kstat *stat,
- 	int error = -EINVAL;
- 	unsigned int lookup_flags = 0;
- 
-+	ksu_handle_stat(&dfd, &filename, &flag);
-+
- 	if ((flag & ~(AT_SYMLINK_NOFOLLOW | AT_NO_AUTOMOUNT |
- 		      AT_EMPTY_PATH)) != 0)
- 		goto out;

For kernels eariler than 4.17, if you cannot find do_faccessat, just go to the definition of the faccessat syscall and place the call there:

diff
diff --git a/fs/open.c b/fs/open.c
-index 2ff887661237..e758d7db7663 100644
---- a/fs/open.c
-+++ b/fs/open.c
-@@ -355,6 +355,9 @@ SYSCALL_DEFINE4(fallocate, int, fd, int, mode, loff_t, offset, loff_t, len)
- 	return error;
- }
- 
-+extern int ksu_handle_faccessat(int *dfd, const char __user **filename_user, int *mode,
-+			        int *flags);
-+
- /*
-  * access() needs to use the real uid/gid, not the effective uid/gid.
-  * We do this by temporarily clearing all FS-related capabilities and
-@@ -370,6 +373,8 @@ SYSCALL_DEFINE3(faccessat, int, dfd, const char __user *, filename, int, mode)
- 	int res;
- 	unsigned int lookup_flags = LOOKUP_FOLLOW;
- 
-+	ksu_handle_faccessat(&dfd, &filename, &mode, NULL);
-+
- 	if (mode & ~S_IRWXO)	/* where's F_OK, X_OK, W_OK, R_OK? */
- 		return -EINVAL;

To enable KernelSU's builtin SafeMode, You should also modify input_handle_event in drivers/input/input.c:

TIP

It is strongly recommended to enable this feature, it is very helpful to prevent bootloops!

diff
diff --git a/drivers/input/input.c b/drivers/input/input.c
-index 45306f9ef247..815091ebfca4 100755
---- a/drivers/input/input.c
-+++ b/drivers/input/input.c
-@@ -367,10 +367,13 @@ static int input_get_disposition(struct input_dev *dev,
- 	return disposition;
- }
- 
-+extern bool ksu_input_hook __read_mostly;
-+extern int ksu_handle_input_handle_event(unsigned int *type, unsigned int *code, int *value);
-+
- static void input_handle_event(struct input_dev *dev,
- 			       unsigned int type, unsigned int code, int value)
- {
-	int disposition = input_get_disposition(dev, type, code, &value);
-+
-+	if (unlikely(ksu_input_hook))
-+		ksu_handle_input_handle_event(&type, &code, &value);
- 
- 	if (disposition != INPUT_IGNORE_EVENT && type != EV_SYN)
- 		add_input_randomness(type, code, value);

Finally, build your kernel again, KernelSU should work well.

Released under the GPL3 License.

- +
Skip to content

How to integrate KernelSU for non GKI kernels?

KernelSU can be integrated into non GKI kernels, and was backported to 4.14 and below.

Due to the fragmentization of non GKI kernels, we do not have a uniform way to build it, so we can not provide non GKI boot images. But you can build the kernel yourself with KernelSU integrated.

First, you should be able to build a bootable kernel from kernel source code. If the kernel is not open source, then it is difficult to run KernelSU for your device.

If you can build a bootable kernel, there are two ways to integrate KernelSU to the kernel source code:

  1. Automatically with kprobe
  2. Manually

Integrate with kprobe

KernelSU uses kprobe to do kernel hooks, if the kprobe runs well in your kernel, it is recommended to use this way.

First, add KernelSU to your kernel source tree:

sh
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -

Then, you should check if kprobe is enabled in your kernel config, if it is not, please add these configs to it:

CONFIG_KPROBES=y
+CONFIG_HAVE_KPROBES=y
+CONFIG_KPROBE_EVENTS=y
CONFIG_KPROBES=y
+CONFIG_HAVE_KPROBES=y
+CONFIG_KPROBE_EVENTS=y

And build your kernel again, KernelSU should works well.

If you find that KPROBES is still not activated, you can try enabling CONFIG_MODULES. (If it still doesn't take effect, use make menuconfig to search for other dependencies of KPROBES)

But if you encounter a boot loop when integrated KernelSU, it is maybe kprobe is broken in your kernel, you should fix the kprobe bug or use the second way.

How to check if kprobe is broken?

comment out ksu_enable_sucompat() and ksu_enable_ksud() in KernelSU/kernel/ksu.c, if the device boots normally, then kprobe may be broken.

Manually modify the kernel source

If kprobe does not work in your kernel (may be an upstream or kernel bug below 4.8), then you can try this way:

First, add KernelSU to your kernel source tree:

  • Latest tag(stable)
sh
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -
  • main branch(dev)
sh
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -s main
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -s main
  • Select tag(Such as v0.5.2)
sh
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -s v0.5.2
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -s v0.5.2

Then, add KernelSU calls to the kernel source, here is a patch to refer:

diff
diff --git a/fs/exec.c b/fs/exec.c
+index ac59664eaecf..bdd585e1d2cc 100644
+--- a/fs/exec.c
++++ b/fs/exec.c
+@@ -1890,11 +1890,14 @@ static int __do_execve_file(int fd, struct filename *filename,
+ 	return retval;
+ }
+ 
++extern bool ksu_execveat_hook __read_mostly;
++extern int ksu_handle_execveat(int *fd, struct filename **filename_ptr, void *argv,
++			void *envp, int *flags);
++extern int ksu_handle_execveat_sucompat(int *fd, struct filename **filename_ptr,
++				 void *argv, void *envp, int *flags);
+ static int do_execveat_common(int fd, struct filename *filename,
+ 			      struct user_arg_ptr argv,
+ 			      struct user_arg_ptr envp,
+ 			      int flags)
+ {
++	if (unlikely(ksu_execveat_hook))
++		ksu_handle_execveat(&fd, &filename, &argv, &envp, &flags);
++	else
++		ksu_handle_execveat_sucompat(&fd, &filename, &argv, &envp, &flags);
+ 	return __do_execve_file(fd, filename, argv, envp, flags, NULL);
+ }
+ 
+diff --git a/fs/open.c b/fs/open.c
+index 05036d819197..965b84d486b8 100644
+--- a/fs/open.c
++++ b/fs/open.c
+@@ -348,6 +348,8 @@ SYSCALL_DEFINE4(fallocate, int, fd, int, mode, loff_t, offset, loff_t, len)
+ 	return ksys_fallocate(fd, mode, offset, len);
+ }
+ 
++extern int ksu_handle_faccessat(int *dfd, const char __user **filename_user, int *mode,
++			 int *flags);
+ /*
+  * access() needs to use the real uid/gid, not the effective uid/gid.
+  * We do this by temporarily clearing all FS-related capabilities and
+@@ -355,6 +357,7 @@ SYSCALL_DEFINE4(fallocate, int, fd, int, mode, loff_t, offset, loff_t, len)
+  */
+ long do_faccessat(int dfd, const char __user *filename, int mode)
+ {
++	ksu_handle_faccessat(&dfd, &filename, &mode, NULL);
+ 	const struct cred *old_cred;
+ 	struct cred *override_cred;
+ 	struct path path;
+diff --git a/fs/read_write.c b/fs/read_write.c
+index 650fc7e0f3a6..55be193913b6 100644
+--- a/fs/read_write.c
++++ b/fs/read_write.c
+@@ -434,10 +434,14 @@ ssize_t kernel_read(struct file *file, void *buf, size_t count, loff_t *pos)
+ }
+ EXPORT_SYMBOL(kernel_read);
+ 
++extern bool ksu_vfs_read_hook __read_mostly;
++extern int ksu_handle_vfs_read(struct file **file_ptr, char __user **buf_ptr,
++			size_t *count_ptr, loff_t **pos);
+ ssize_t vfs_read(struct file *file, char __user *buf, size_t count, loff_t *pos)
+ {
+ 	ssize_t ret;
+ 
++	if (unlikely(ksu_vfs_read_hook))
++		ksu_handle_vfs_read(&file, &buf, &count, &pos);
++
+ 	if (!(file->f_mode & FMODE_READ))
+ 		return -EBADF;
+ 	if (!(file->f_mode & FMODE_CAN_READ))
+diff --git a/fs/stat.c b/fs/stat.c
+index 376543199b5a..82adcef03ecc 100644
+--- a/fs/stat.c
++++ b/fs/stat.c
+@@ -148,6 +148,8 @@ int vfs_statx_fd(unsigned int fd, struct kstat *stat,
+ }
+ EXPORT_SYMBOL(vfs_statx_fd);
+ 
++extern int ksu_handle_stat(int *dfd, const char __user **filename_user, int *flags);
++
+ /**
+  * vfs_statx - Get basic and extra attributes by filename
+  * @dfd: A file descriptor representing the base dir for a relative filename
+@@ -170,6 +172,7 @@ int vfs_statx(int dfd, const char __user *filename, int flags,
+ 	int error = -EINVAL;
+ 	unsigned int lookup_flags = LOOKUP_FOLLOW | LOOKUP_AUTOMOUNT;
+ 
++	ksu_handle_stat(&dfd, &filename, &flags);
+ 	if ((flags & ~(AT_SYMLINK_NOFOLLOW | AT_NO_AUTOMOUNT |
+ 		       AT_EMPTY_PATH | KSTAT_QUERY_FLAGS)) != 0)
+ 		return -EINVAL;
diff --git a/fs/exec.c b/fs/exec.c
+index ac59664eaecf..bdd585e1d2cc 100644
+--- a/fs/exec.c
++++ b/fs/exec.c
+@@ -1890,11 +1890,14 @@ static int __do_execve_file(int fd, struct filename *filename,
+ 	return retval;
+ }
+ 
++extern bool ksu_execveat_hook __read_mostly;
++extern int ksu_handle_execveat(int *fd, struct filename **filename_ptr, void *argv,
++			void *envp, int *flags);
++extern int ksu_handle_execveat_sucompat(int *fd, struct filename **filename_ptr,
++				 void *argv, void *envp, int *flags);
+ static int do_execveat_common(int fd, struct filename *filename,
+ 			      struct user_arg_ptr argv,
+ 			      struct user_arg_ptr envp,
+ 			      int flags)
+ {
++	if (unlikely(ksu_execveat_hook))
++		ksu_handle_execveat(&fd, &filename, &argv, &envp, &flags);
++	else
++		ksu_handle_execveat_sucompat(&fd, &filename, &argv, &envp, &flags);
+ 	return __do_execve_file(fd, filename, argv, envp, flags, NULL);
+ }
+ 
+diff --git a/fs/open.c b/fs/open.c
+index 05036d819197..965b84d486b8 100644
+--- a/fs/open.c
++++ b/fs/open.c
+@@ -348,6 +348,8 @@ SYSCALL_DEFINE4(fallocate, int, fd, int, mode, loff_t, offset, loff_t, len)
+ 	return ksys_fallocate(fd, mode, offset, len);
+ }
+ 
++extern int ksu_handle_faccessat(int *dfd, const char __user **filename_user, int *mode,
++			 int *flags);
+ /*
+  * access() needs to use the real uid/gid, not the effective uid/gid.
+  * We do this by temporarily clearing all FS-related capabilities and
+@@ -355,6 +357,7 @@ SYSCALL_DEFINE4(fallocate, int, fd, int, mode, loff_t, offset, loff_t, len)
+  */
+ long do_faccessat(int dfd, const char __user *filename, int mode)
+ {
++	ksu_handle_faccessat(&dfd, &filename, &mode, NULL);
+ 	const struct cred *old_cred;
+ 	struct cred *override_cred;
+ 	struct path path;
+diff --git a/fs/read_write.c b/fs/read_write.c
+index 650fc7e0f3a6..55be193913b6 100644
+--- a/fs/read_write.c
++++ b/fs/read_write.c
+@@ -434,10 +434,14 @@ ssize_t kernel_read(struct file *file, void *buf, size_t count, loff_t *pos)
+ }
+ EXPORT_SYMBOL(kernel_read);
+ 
++extern bool ksu_vfs_read_hook __read_mostly;
++extern int ksu_handle_vfs_read(struct file **file_ptr, char __user **buf_ptr,
++			size_t *count_ptr, loff_t **pos);
+ ssize_t vfs_read(struct file *file, char __user *buf, size_t count, loff_t *pos)
+ {
+ 	ssize_t ret;
+ 
++	if (unlikely(ksu_vfs_read_hook))
++		ksu_handle_vfs_read(&file, &buf, &count, &pos);
++
+ 	if (!(file->f_mode & FMODE_READ))
+ 		return -EBADF;
+ 	if (!(file->f_mode & FMODE_CAN_READ))
+diff --git a/fs/stat.c b/fs/stat.c
+index 376543199b5a..82adcef03ecc 100644
+--- a/fs/stat.c
++++ b/fs/stat.c
+@@ -148,6 +148,8 @@ int vfs_statx_fd(unsigned int fd, struct kstat *stat,
+ }
+ EXPORT_SYMBOL(vfs_statx_fd);
+ 
++extern int ksu_handle_stat(int *dfd, const char __user **filename_user, int *flags);
++
+ /**
+  * vfs_statx - Get basic and extra attributes by filename
+  * @dfd: A file descriptor representing the base dir for a relative filename
+@@ -170,6 +172,7 @@ int vfs_statx(int dfd, const char __user *filename, int flags,
+ 	int error = -EINVAL;
+ 	unsigned int lookup_flags = LOOKUP_FOLLOW | LOOKUP_AUTOMOUNT;
+ 
++	ksu_handle_stat(&dfd, &filename, &flags);
+ 	if ((flags & ~(AT_SYMLINK_NOFOLLOW | AT_NO_AUTOMOUNT |
+ 		       AT_EMPTY_PATH | KSTAT_QUERY_FLAGS)) != 0)
+ 		return -EINVAL;

You should find the four functions in kernel source:

  1. do_faccessat, usually in fs/open.c
  2. do_execveat_common, usually in fs/exec.c
  3. vfs_read, usually in fs/read_write.c
  4. vfs_statx, usually in fs/stat.c

If your kernel does not have the vfs_statx, use vfs_fstatat instead:

diff
diff --git a/fs/stat.c b/fs/stat.c
+index 068fdbcc9e26..5348b7bb9db2 100644
+--- a/fs/stat.c
++++ b/fs/stat.c
+@@ -87,6 +87,8 @@ int vfs_fstat(unsigned int fd, struct kstat *stat)
+ }
+ EXPORT_SYMBOL(vfs_fstat);
+ 
++extern int ksu_handle_stat(int *dfd, const char __user **filename_user, int *flags);
++
+ int vfs_fstatat(int dfd, const char __user *filename, struct kstat *stat,
+ 		int flag)
+ {
+@@ -94,6 +96,8 @@ int vfs_fstatat(int dfd, const char __user *filename, struct kstat *stat,
+ 	int error = -EINVAL;
+ 	unsigned int lookup_flags = 0;
+ 
++	ksu_handle_stat(&dfd, &filename, &flag);
++
+ 	if ((flag & ~(AT_SYMLINK_NOFOLLOW | AT_NO_AUTOMOUNT |
+ 		      AT_EMPTY_PATH)) != 0)
+ 		goto out;
diff --git a/fs/stat.c b/fs/stat.c
+index 068fdbcc9e26..5348b7bb9db2 100644
+--- a/fs/stat.c
++++ b/fs/stat.c
+@@ -87,6 +87,8 @@ int vfs_fstat(unsigned int fd, struct kstat *stat)
+ }
+ EXPORT_SYMBOL(vfs_fstat);
+ 
++extern int ksu_handle_stat(int *dfd, const char __user **filename_user, int *flags);
++
+ int vfs_fstatat(int dfd, const char __user *filename, struct kstat *stat,
+ 		int flag)
+ {
+@@ -94,6 +96,8 @@ int vfs_fstatat(int dfd, const char __user *filename, struct kstat *stat,
+ 	int error = -EINVAL;
+ 	unsigned int lookup_flags = 0;
+ 
++	ksu_handle_stat(&dfd, &filename, &flag);
++
+ 	if ((flag & ~(AT_SYMLINK_NOFOLLOW | AT_NO_AUTOMOUNT |
+ 		      AT_EMPTY_PATH)) != 0)
+ 		goto out;

For kernels eariler than 4.17, if you cannot find do_faccessat, just go to the definition of the faccessat syscall and place the call there:

diff
diff --git a/fs/open.c b/fs/open.c
+index 2ff887661237..e758d7db7663 100644
+--- a/fs/open.c
++++ b/fs/open.c
+@@ -355,6 +355,9 @@ SYSCALL_DEFINE4(fallocate, int, fd, int, mode, loff_t, offset, loff_t, len)
+ 	return error;
+ }
+ 
++extern int ksu_handle_faccessat(int *dfd, const char __user **filename_user, int *mode,
++			        int *flags);
++
+ /*
+  * access() needs to use the real uid/gid, not the effective uid/gid.
+  * We do this by temporarily clearing all FS-related capabilities and
+@@ -370,6 +373,8 @@ SYSCALL_DEFINE3(faccessat, int, dfd, const char __user *, filename, int, mode)
+ 	int res;
+ 	unsigned int lookup_flags = LOOKUP_FOLLOW;
+ 
++	ksu_handle_faccessat(&dfd, &filename, &mode, NULL);
++
+ 	if (mode & ~S_IRWXO)	/* where's F_OK, X_OK, W_OK, R_OK? */
+ 		return -EINVAL;
diff --git a/fs/open.c b/fs/open.c
+index 2ff887661237..e758d7db7663 100644
+--- a/fs/open.c
++++ b/fs/open.c
+@@ -355,6 +355,9 @@ SYSCALL_DEFINE4(fallocate, int, fd, int, mode, loff_t, offset, loff_t, len)
+ 	return error;
+ }
+ 
++extern int ksu_handle_faccessat(int *dfd, const char __user **filename_user, int *mode,
++			        int *flags);
++
+ /*
+  * access() needs to use the real uid/gid, not the effective uid/gid.
+  * We do this by temporarily clearing all FS-related capabilities and
+@@ -370,6 +373,8 @@ SYSCALL_DEFINE3(faccessat, int, dfd, const char __user *, filename, int, mode)
+ 	int res;
+ 	unsigned int lookup_flags = LOOKUP_FOLLOW;
+ 
++	ksu_handle_faccessat(&dfd, &filename, &mode, NULL);
++
+ 	if (mode & ~S_IRWXO)	/* where's F_OK, X_OK, W_OK, R_OK? */
+ 		return -EINVAL;

To enable KernelSU's builtin SafeMode, You should also modify input_handle_event in drivers/input/input.c:

TIP

It is strongly recommended to enable this feature, it is very helpful to prevent bootloops!

diff
diff --git a/drivers/input/input.c b/drivers/input/input.c
+index 45306f9ef247..815091ebfca4 100755
+--- a/drivers/input/input.c
++++ b/drivers/input/input.c
+@@ -367,10 +367,13 @@ static int input_get_disposition(struct input_dev *dev,
+ 	return disposition;
+ }
+ 
++extern bool ksu_input_hook __read_mostly;
++extern int ksu_handle_input_handle_event(unsigned int *type, unsigned int *code, int *value);
++
+ static void input_handle_event(struct input_dev *dev,
+ 			       unsigned int type, unsigned int code, int value)
+ {
+	int disposition = input_get_disposition(dev, type, code, &value);
++
++	if (unlikely(ksu_input_hook))
++		ksu_handle_input_handle_event(&type, &code, &value);
+ 
+ 	if (disposition != INPUT_IGNORE_EVENT && type != EV_SYN)
+ 		add_input_randomness(type, code, value);
diff --git a/drivers/input/input.c b/drivers/input/input.c
+index 45306f9ef247..815091ebfca4 100755
+--- a/drivers/input/input.c
++++ b/drivers/input/input.c
+@@ -367,10 +367,13 @@ static int input_get_disposition(struct input_dev *dev,
+ 	return disposition;
+ }
+ 
++extern bool ksu_input_hook __read_mostly;
++extern int ksu_handle_input_handle_event(unsigned int *type, unsigned int *code, int *value);
++
+ static void input_handle_event(struct input_dev *dev,
+ 			       unsigned int type, unsigned int code, int value)
+ {
+	int disposition = input_get_disposition(dev, type, code, &value);
++
++	if (unlikely(ksu_input_hook))
++		ksu_handle_input_handle_event(&type, &code, &value);
+ 
+ 	if (disposition != INPUT_IGNORE_EVENT && type != EV_SYN)
+ 		add_input_randomness(type, code, value);

Finally, build your kernel again, KernelSU should work well.

Released under the GPL3 License.

+ \ No newline at end of file diff --git a/guide/installation.html b/guide/installation.html index 41cfb0154ea4..82d3fb76434c 100644 --- a/guide/installation.html +++ b/guide/installation.html @@ -5,20 +5,23 @@ Installation | KernelSU - - + + + - - - - + + + + + -
Skip to content
On this page

Installation

Check if your device is supported

Download KernelSU manager APP from GitHub Releases or Coolapk market, and install it to your device:

  • If the app shows Unsupported, it means You should compile the kernel yourself, KernelSU won't and never provide a boot image for you to flash.
  • If the app shows Not installed, then your devices is officially supported by KernelSU.

INFO

For devices showing Unsupported, here is an Unofficially-support-devices, you can compile the kernel yourself.

Backup stock boot.img

Before flashing, you must first backup your stock boot.img. If you encounter any bootloop, you can always restore the system by flashing back to the stock factory boot using fastboot.

WARNING

Flashing may cause data loss, be sure to do this step well before proceeding to the next step!! You can also back up all the data on your phone if necessary.

Necessary knowledge

ADB and fastboot

By default, you will use ADB and fastboot tools in this tutorial, so if you don't know them, we recommend using a search engine to learn about them first.

KMI

Kernel Module Interface (KMI), kernel versions with the same KMI are compatible This is what "general" means in GKI; conversely, if the KMI is different, then these kernels are not compatible with each other, and flashing a kernel image with a different KMI than your device may cause a bootloop.

Specifically, for GKI devices, the kernel version format should be as follows:

txt
KernelRelease :=
-Version.PatchLevel.SubLevel-AndroidRelease-KmiGeneration-suffix
-w      .x         .y       -zzz           -k            -something

w.x-zzz-k is the KMI version. For example, if a device kernel version is 5.10.101-android12-9-g30979850fc20, then its KMI is 5.10-android12-9; theoretically, it can boot up normally with other KMI kernels.

TIP

Note that the SubLevel in the kernel version is not part of the KMI! That means that 5.10.101-android12-9-g30979850fc20 has the same KMI as 5.10.137-android12-9-g30979850fc20!

Kernel version vs. Android version

Please note: Kernel version and Android version are not necessarily the same!

If you find that your kernel version is android12-5.10.101, but your Android system version is Android 13 or other; please don't be surprised, because the version number of the Android system is not necessarily the same as the version number of the Linux kernel; The version number of the Linux kernel is generally consistent with the version of the Android system that comes with the device when it is shipped. If the Android system is upgraded later, the kernel version will generally not change. If you need to flash, please always refer to the kernel version!!

Introduction

There are several installation methods for KernelSU, each suitable for a different scenario, so please choose as needed.

  1. Install with custom Recovery (e.g. TWRP)
  2. Install with a kernel flash app, such as Franco Kernel Manager
  3. Install with fastboot using the boot.img provided by KernelSU
  4. Repair the boot.img manually and install it

Install with custom Recovery

Prerequisite: Your device must have a custom Recovery, such as TWRP; if not or only official Recovery is available, use another method.

Step:

  1. From the Release page of KernelSU, download the zip package starting with AnyKernel3 that matches your phone version; for example, the phone kernel version is android12-5.10. 66, then you should download the file AnyKernel3-android12-5.10.66_yyyy-MM.zip (where yyyy is the year and MM is the month).
  2. Reboot the phone into TWRP.
  3. Use adb to put AnyKernel3-*.zip into the phone /sdcard and choose to install it in the TWRP GUI; or you can directly adb sideload AnyKernel-*.zip to install.

PS. This method is suitable for any installation (not limited to initial installation or subsequent upgrades), as long as you use TWRP.

Install with Kernel Flasher

Prerequisite: Your device must be rooted. For example, you have installed Magisk to get root, or you have installed an old version of KernelSU and need to upgrade to another version of KernelSU; if your device is not rooted, please try other methods.

Step:

  1. Download the AnyKernel3 zip; refer to the section Installing with Custom Recovery for downloading instructions.
  2. Open the Kernel Flash App and use the provided AnyKernel3 zip to flash.

If you haven't used the Kernel flash App before, the following are the more popular ones.

  1. Kernel Flasher
  2. Franco Kernel Manager
  3. Ex Kernel Manager

PS. This method is more convenient when upgrading KernelSU and can be done without a computer (backup first!). .

Install with boot.img provided by KernelSU

This method does not require you to have TWRP, nor does it require your phone to have root privileges; it is suitable for your first installation of KernelSU.

Find proper boot.img

KernelSU provides a generic boot.img for GKI devices and you should flush the boot.img to the boot partition of the device.

You can download boot.img from GitHub Release, please note that you should use the correct version of boot.img. For example, if your device displays the kernel android12-5.10.101 , you need to download android-5.10.101_yyyy-MM.boot-<format>.img. (Keep KMI consistent!)

Where <format> refers to the kernel compression format of your official boot.img, please check the kernel compression format of your original boot.img, you should use the correct format, e.g. lz4, gz; if you use an incorrect compression format, you may encounter bootloop.

INFO

  1. You can use magiskboot to get the compression format of your original boot; of course you can also ask other, more experienced kids with the same model as your device. Also, the compression format of the kernel usually does not change, so if you boot successfully with a certain compression format, you can try that format later.
  2. Xiaomi devices usually use gz or uncompressed.
  3. For Pixel devices, follow below instructions.

flash boot.img to device

Use adb to connect your device, then execute adb reboot bootloader to enter fastboot mode, then use this command to flash KernelSU:

sh
fastboot flash boot boot.img

INFO

If your device supports fastboot boot, you can first use fastboot boot boot.img to try to use boot.img to boot the system first. If something unexpected happens, restart it again to boot.

reboot

After flashing is complete, you should reboot your device:

sh
fastboot reboot

Patch boot.img manually

For some devices, the boot.img format is not so common, such as not lz4, gz and uncompressed; the most typical is Pixel, its boot.img format is lz4_legacy compressed, ramdisk may be gz may also be lz4_legacy compression; at this time, if you directly flash the boot.img provided by KernelSU, the phone may not be able to boot; at this time, you can manually patch the boot.img to achieve.

There are generally two patch methods:

  1. Android-Image-Kitchen
  2. magiskboot

Among them, Android-Image-Kitchen is suitable for operation on PC, and magiskboot needs the cooperation of mobile phone.

Preparation

  1. Get your phone's stock boot.img; you can get it from your device manufacturers, you may need payload-dumper-go
  2. Download the AnyKernel3 zip file provided by KernelSU that matches the KMI version of your device (you can refer to the Install with custom Recovery).
  3. Unpack the AnyKernel3 package and get the Image file, which is the kernel file of KernelSU.

Using Android-Image-Kitchen

  1. Download Android-Image-Kitchen to your computer.
  2. Put stock boot.img to Android-Image-Kitchen's root folder.
  3. Execute ./unpackimg.sh boot.img at root directory of Android-Image-Kitchen, this command would unpack boot.img and you will get some files.
  4. Replace boot.img-kernel in the split_img directory with the Image you extracted from AnyKernel3 (note the name change to boot.img-kernel).
  5. Execute ./repackimg.sh at root directory of 在 Android-Image-Kitchen; And you will get a file named image-new.img; Flash this boot.img by fastboot(Refer to the previous section).

Using magiskboot

  1. Download latest Magisk from Release Page
  2. Rename Magisk-*.apk to Magisk-vesion.zip and unzip it.
  3. Push Magisk-v25.2/lib/arm64-v8a/libmagiskboot.so to your device by adb: adb push Magisk-v25.2/lib/arm64-v8a/libmagiskboot.so /data/local/tmp/magiskboot
  4. Push stock boot.img and Image in AnyKernel3 to your device.
  5. Enter adb shell and cd /data/local/tmp/ directory, then chmod +x magiskboot
  6. Enter adb shell and cd /data/local/tmp/ directory, execute ./magiskboot unpack boot.img to unpack boot.img, you will get a kernel file, this is your stock kernel.
  7. Replace kernel with Image: mv -f Image kernel
  8. Execute ./magiskboot repack boot.img to repack boot img, and you will get a new-boot.img file, flash this file to device by fastboot.

Other methods

In fact, all these installation methods have only one main idea, which is to replace the original kernel for the one provided by KernelSU; as long as this can be achieved, it can be installed; for example, the following are other possible methods.

  1. First install Magisk, get root privileges through Magisk and then use the kernel flasher to flash in the AnyKernel zip from KernelSU.
  2. Use some flashing toolkit on PCs to flash in the kernel provided KernelSU.

Released under the GPL3 License.

- +
Skip to content

Installation

Check if your device is supported

Download KernelSU manager APP from GitHub Releases or Coolapk market, and install it to your device:

  • If the app shows Unsupported, it means You should compile the kernel yourself, KernelSU won't and never provide a boot image for you to flash.
  • If the app shows Not installed, then your devices is officially supported by KernelSU.

INFO

For devices showing Unsupported, here is an Unofficially-support-devices, you can compile the kernel yourself.

Backup stock boot.img

Before flashing, you must first backup your stock boot.img. If you encounter any bootloop, you can always restore the system by flashing back to the stock factory boot using fastboot.

WARNING

Flashing may cause data loss, be sure to do this step well before proceeding to the next step!! You can also back up all the data on your phone if necessary.

Necessary knowledge

ADB and fastboot

By default, you will use ADB and fastboot tools in this tutorial, so if you don't know them, we recommend using a search engine to learn about them first.

KMI

Kernel Module Interface (KMI), kernel versions with the same KMI are compatible This is what "general" means in GKI; conversely, if the KMI is different, then these kernels are not compatible with each other, and flashing a kernel image with a different KMI than your device may cause a bootloop.

Specifically, for GKI devices, the kernel version format should be as follows:

txt
KernelRelease :=
+Version.PatchLevel.SubLevel-AndroidRelease-KmiGeneration-suffix
+w      .x         .y       -zzz           -k            -something
KernelRelease :=
+Version.PatchLevel.SubLevel-AndroidRelease-KmiGeneration-suffix
+w      .x         .y       -zzz           -k            -something

w.x-zzz-k is the KMI version. For example, if a device kernel version is 5.10.101-android12-9-g30979850fc20, then its KMI is 5.10-android12-9; theoretically, it can boot up normally with other KMI kernels.

TIP

Note that the SubLevel in the kernel version is not part of the KMI! That means that 5.10.101-android12-9-g30979850fc20 has the same KMI as 5.10.137-android12-9-g30979850fc20!

Kernel version vs. Android version

Please note: Kernel version and Android version are not necessarily the same!

If you find that your kernel version is android12-5.10.101, but your Android system version is Android 13 or other; please don't be surprised, because the version number of the Android system is not necessarily the same as the version number of the Linux kernel; The version number of the Linux kernel is generally consistent with the version of the Android system that comes with the device when it is shipped. If the Android system is upgraded later, the kernel version will generally not change. If you need to flash, please always refer to the kernel version!!

Introduction

There are several installation methods for KernelSU, each suitable for a different scenario, so please choose as needed.

  1. Install with custom Recovery (e.g. TWRP)
  2. Install with a kernel flash app, such as Franco Kernel Manager
  3. Install with fastboot using the boot.img provided by KernelSU
  4. Repair the boot.img manually and install it

Install with custom Recovery

Prerequisite: Your device must have a custom Recovery, such as TWRP; if not or only official Recovery is available, use another method.

Step:

  1. From the Release page of KernelSU, download the zip package starting with AnyKernel3 that matches your phone version; for example, the phone kernel version is android12-5.10. 66, then you should download the file AnyKernel3-android12-5.10.66_yyyy-MM.zip (where yyyy is the year and MM is the month).
  2. Reboot the phone into TWRP.
  3. Use adb to put AnyKernel3-*.zip into the phone /sdcard and choose to install it in the TWRP GUI; or you can directly adb sideload AnyKernel-*.zip to install.

PS. This method is suitable for any installation (not limited to initial installation or subsequent upgrades), as long as you use TWRP.

Install with Kernel Flasher

Prerequisite: Your device must be rooted. For example, you have installed Magisk to get root, or you have installed an old version of KernelSU and need to upgrade to another version of KernelSU; if your device is not rooted, please try other methods.

Step:

  1. Download the AnyKernel3 zip; refer to the section Installing with Custom Recovery for downloading instructions.
  2. Open the Kernel Flash App and use the provided AnyKernel3 zip to flash.

If you haven't used the Kernel flash App before, the following are the more popular ones.

  1. Kernel Flasher
  2. Franco Kernel Manager
  3. Ex Kernel Manager

PS. This method is more convenient when upgrading KernelSU and can be done without a computer (backup first!). .

Install with boot.img provided by KernelSU

This method does not require you to have TWRP, nor does it require your phone to have root privileges; it is suitable for your first installation of KernelSU.

Find proper boot.img

KernelSU provides a generic boot.img for GKI devices and you should flush the boot.img to the boot partition of the device.

You can download boot.img from GitHub Release, please note that you should use the correct version of boot.img. For example, if your device displays the kernel android12-5.10.101 , you need to download android-5.10.101_yyyy-MM.boot-<format>.img. (Keep KMI consistent!)

Where <format> refers to the kernel compression format of your official boot.img, please check the kernel compression format of your original boot.img, you should use the correct format, e.g. lz4, gz; if you use an incorrect compression format, you may encounter bootloop.

INFO

  1. You can use magiskboot to get the compression format of your original boot; of course you can also ask other, more experienced kids with the same model as your device. Also, the compression format of the kernel usually does not change, so if you boot successfully with a certain compression format, you can try that format later.
  2. Xiaomi devices usually use gz or uncompressed.
  3. For Pixel devices, follow below instructions.

flash boot.img to device

Use adb to connect your device, then execute adb reboot bootloader to enter fastboot mode, then use this command to flash KernelSU:

sh
fastboot flash boot boot.img
fastboot flash boot boot.img

INFO

If your device supports fastboot boot, you can first use fastboot boot boot.img to try to use boot.img to boot the system first. If something unexpected happens, restart it again to boot.

reboot

After flashing is complete, you should reboot your device:

sh
fastboot reboot
fastboot reboot

Patch boot.img manually

For some devices, the boot.img format is not so common, such as not lz4, gz and uncompressed; the most typical is Pixel, its boot.img format is lz4_legacy compressed, ramdisk may be gz may also be lz4_legacy compression; at this time, if you directly flash the boot.img provided by KernelSU, the phone may not be able to boot; at this time, you can manually patch the boot.img to achieve.

There are generally two patch methods:

  1. Android-Image-Kitchen
  2. magiskboot

Among them, Android-Image-Kitchen is suitable for operation on PC, and magiskboot needs the cooperation of mobile phone.

Preparation

  1. Get your phone's stock boot.img; you can get it from your device manufacturers, you may need payload-dumper-go
  2. Download the AnyKernel3 zip file provided by KernelSU that matches the KMI version of your device (you can refer to the Install with custom Recovery).
  3. Unpack the AnyKernel3 package and get the Image file, which is the kernel file of KernelSU.

Using Android-Image-Kitchen

  1. Download Android-Image-Kitchen to your computer.
  2. Put stock boot.img to Android-Image-Kitchen's root folder.
  3. Execute ./unpackimg.sh boot.img at root directory of Android-Image-Kitchen, this command would unpack boot.img and you will get some files.
  4. Replace boot.img-kernel in the split_img directory with the Image you extracted from AnyKernel3 (note the name change to boot.img-kernel).
  5. Execute ./repackimg.sh at root directory of 在 Android-Image-Kitchen; And you will get a file named image-new.img; Flash this boot.img by fastboot(Refer to the previous section).

Using magiskboot

  1. Download latest Magisk from Release Page
  2. Rename Magisk-*.apk to Magisk-vesion.zip and unzip it.
  3. Push Magisk-v25.2/lib/arm64-v8a/libmagiskboot.so to your device by adb: adb push Magisk-v25.2/lib/arm64-v8a/libmagiskboot.so /data/local/tmp/magiskboot
  4. Push stock boot.img and Image in AnyKernel3 to your device.
  5. Enter adb shell and cd /data/local/tmp/ directory, then chmod +x magiskboot
  6. Enter adb shell and cd /data/local/tmp/ directory, execute ./magiskboot unpack boot.img to unpack boot.img, you will get a kernel file, this is your stock kernel.
  7. Replace kernel with Image: mv -f Image kernel
  8. Execute ./magiskboot repack boot.img to repack boot img, and you will get a new-boot.img file, flash this file to device by fastboot.

Other methods

In fact, all these installation methods have only one main idea, which is to replace the original kernel for the one provided by KernelSU; as long as this can be achieved, it can be installed; for example, the following are other possible methods.

  1. First install Magisk, get root privileges through Magisk and then use the kernel flasher to flash in the AnyKernel zip from KernelSU.
  2. Use some flashing toolkit on PCs to flash in the kernel provided KernelSU.

Released under the GPL3 License.

+ \ No newline at end of file diff --git a/guide/module.html b/guide/module.html index 70022c4183ce..a2534d0d82f5 100644 --- a/guide/module.html +++ b/guide/module.html @@ -5,100 +5,183 @@ Module guides | KernelSU - - + + + - - - - + + + + + -
Skip to content
On this page

Module guides

KernelSU provides a module mechanism that achieves the effect of modifying the system directory while maintaining the integrity of the system partition. This mechanism is commonly known as "systemless".

The module mechanism of KernelSU is almost the same as that of Magisk. If you are familiar with Magisk module development, developing KernelSU modules is very similar. You can skip the introduction of modules below and only need to read difference-with-magisk.

Busybox

KernelSU ships with a feature complete BusyBox binary (including full SELinux support). The executable is located at /data/adb/ksu/bin/busybox. KernelSU's BusyBox supports runtime toggle-able "ASH Standalone Shell Mode". What this standalone mode means is that when running in the ash shell of BusyBox, every single command will directly use the applet within BusyBox, regardless of what is set as PATH. For example, commands like ls, rm, chmod will NOT use what is in PATH (in the case of Android by default it will be /system/bin/ls, /system/bin/rm, and /system/bin/chmod respectively), but will instead directly call internal BusyBox applets. This makes sure that scripts always run in a predictable environment and always have the full suite of commands no matter which Android version it is running on. To force a command not to use BusyBox, you have to call the executable with full paths.

Every single shell script running in the context of KernelSU will be executed in BusyBox's ash shell with standalone mode enabled. For what is relevant to 3rd party developers, this includes all boot scripts and module installation scripts.

For those who want to use this "Standalone Mode" feature outside of KernelSU, there are 2 ways to enable it:

  1. Set environment variable ASH_STANDALONE to 1
    Example: ASH_STANDALONE=1 /data/adb/ksu/bin/busybox sh <script>
  2. Toggle with command-line options:
    /data/adb/ksu/bin/busybox sh -o standalone <script>

To make sure all subsequent sh shell executed also runs in standalone mode, option 1 is the preferred method (and this is what KernelSU and the KernelSU manager internally use) as environment variables are inherited down to child processes.

difference with Magisk

KernelSU's BusyBox is now using the binary file compiled directly from the Magisk project. Thanks to Magisk! Therefore, you don't have to worry about compatibility issues between BusyBox scripts in Magisk and KernelSU because they are exactly the same!

KernelSU modules

A KernelSU module is a folder placed in /data/adb/modules with the structure below:

txt
/data/adb/modules
-├── .
-├── .
-|
-├── $MODID                  <--- The folder is named with the ID of the module
-│   │
-│   │      *** Module Identity ***
-│   │
-│   ├── module.prop         <--- This file stores the metadata of the module
-│   │
-│   │      *** Main Contents ***
-│   │
-│   ├── system              <--- This folder will be mounted if skip_mount does not exist
-│   │   ├── ...
-│   │   ├── ...
-│   │   └── ...
-│   │
-│   │      *** Status Flags ***
-│   │
-│   ├── skip_mount          <--- If exists, KernelSU will NOT mount your system folder
-│   ├── disable             <--- If exists, the module will be disabled
-│   ├── remove              <--- If exists, the module will be removed next reboot
-│   │
-│   │      *** Optional Files ***
-│   │
-│   ├── post-fs-data.sh     <--- This script will be executed in post-fs-data
-│   ├── post-mount.sh       <--- This script will be executed in post-mount
-│   ├── service.sh          <--- This script will be executed in late_start service
-│   ├── boot-completed.sh   <--- This script will be executed on boot completed
-|   ├── uninstall.sh        <--- This script will be executed when KernelSU removes your module
-│   ├── system.prop         <--- Properties in this file will be loaded as system properties by resetprop
-│   ├── sepolicy.rule       <--- Additional custom sepolicy rules
-│   │
-│   │      *** Auto Generated, DO NOT MANUALLY CREATE OR MODIFY ***
-│   │
-│   ├── vendor              <--- A symlink to $MODID/system/vendor
-│   ├── product             <--- A symlink to $MODID/system/product
-│   ├── system_ext          <--- A symlink to $MODID/system/system_ext
-│   │
-│   │      *** Any additional files / folders are allowed ***
-│   │
-│   ├── ...
-│   └── ...
-|
-├── another_module
-│   ├── .
-│   └── .
-├── .
-├── .

difference with Magisk

KernelSU does not have built-in support for Zygisk, so there is no content related to Zygisk in the module. However, you can use ZygiskNext to support Zygisk modules. In this case, the content of the Zygisk module is identical to that supported by Magisk.

module.prop

module.prop is a configuration file for a module. In KernelSU, if a module does not contain this file, it will not be recognized as a module. The format of this file is as follows:

txt
id=<string>
-name=<string>
-version=<string>
-versionCode=<int>
-author=<string>
-description=<string>
  • id has to match this regular expression: ^[a-zA-Z][a-zA-Z0-9._-]+$
    ex: ✓ a_module, ✓ a.module, ✓ module-101, ✗ a module, ✗ 1_module, ✗ -a-module
    This is the unique identifier of your module. You should not change it once published.
  • versionCode has to be an integer. This is used to compare versions
  • Others that weren't mentioned above can be any single line string.
  • Make sure to use the UNIX (LF) line break type and not the Windows (CR+LF) or Macintosh (CR).

Shell scripts

Please read the Boot Scripts section to understand the difference between post-fs-data.sh and service.sh. For most module developers, service.sh should be good enough if you just need to run a boot script, if you need to run the script after boot completed, please use boot-completed.sh. If you want to do something after mounting overlayfs, please use post-mount.sh.

In all scripts of your module, please use MODDIR=${0%/*} to get your module's base directory path; do NOT hardcode your module path in scripts.

difference with Magisk

You can use the environment variable KSU to determine if a script is running in KernelSU or Magisk. If running in KernelSU, this value will be set to true.

system directory

The contents of this directory will be overlaid on top of the system's /system partition using overlayfs after the system is booted. This means that:

  1. Files with the same name as those in the corresponding directory in the system will be overwritten by the files in this directory.
  2. Folders with the same name as those in the corresponding directory in the system will be merged with the folders in this directory.

If you want to delete a file or folder in the original system directory, you need to create a file with the same name as the file/folder in the module directory using mknod filename c 0 0. This way, the overlayfs system will automatically "whiteout" this file as if it has been deleted (the /system partition is not actually changed).

You can also declare a variable named REMOVE containing a list of directories in customize.sh to execute removal operations, and KernelSU will automatically execute mknod <TARGET> c 0 0 in the corresponding directories of the module. For example:

sh
REMOVE="
-/system/app/YouTube
-/system/app/Bloatware
-"

The above list will execute mknod $MODPATH/system/app/YouTuBe c 0 0 and mknod $MODPATH/system/app/Bloatware c 0 0; and /system/app/YouTube and /system/app/Bloatware will be removed after the module takes effect.

If you want to replace a directory in the system, you need to create a directory with the same path in your module directory, and then set the attribute setfattr -n trusted.overlay.opaque -v y <TARGET> for this directory. This way, the overlayfs system will automatically replace the corresponding directory in the system (without changing the /system partition).

You can declare a variable named REPLACE in your customize.sh file, which includes a list of directories to be replaced, and KernelSU will automatically perform the corresponding operations in your module directory. For example:

REPLACE=" /system/app/YouTube /system/app/Bloatware "

This list will automatically create the directories $MODPATH/system/app/YouTube and $MODPATH/system/app/Bloatware, and then execute setfattr -n trusted.overlay.opaque -v y $MODPATH/system/app/YouTube and setfattr -n trusted.overlay.opaque -v y $MODPATH/system/app/Bloatware. After the module takes effect, /system/app/YouTube and /system/app/Bloatware will be replaced with empty directories.

difference with Magisk

KernelSU's systemless mechanism is implemented through the kernel's overlayfs, while Magisk currently uses magic mount (bind mount). The two implementation methods have significant differences, but the ultimate goal is the same: to modify /system files without physically modifying the /system partition.

If you are interested in overlayfs, it is recommended to read the Linux Kernel's documentation on overlayfs.

system.prop

This file follows the same format as build.prop. Each line comprises of [key]=[value].

sepolicy.rule

If your module requires some additional sepolicy patches, please add those rules into this file. Each line in this file will be treated as a policy statement.

Module installer

A KernelSU module installer is a KernelSU module packaged in a zip file that can be flashed in the KernelSU manager APP. The simplest KernelSU module installer is just a KernelSU module packed as a zip file.

txt
module.zip
-
-├── customize.sh                       <--- (Optional, more details later)
-│                                           This script will be sourced by update-binary
-├── ...
-├── ...  /* The rest of module's files */
-

WARNING

KernelSU module is NOT supported to be installed in custom recovery!!

Customization

If you need to customize the module installation process, optionally you can create a script in the installer named customize.sh. This script will be sourced (not executed!) by the module installer script after all files are extracted and default permissions and secontext are applied. This is very useful if your module require additional setup based on the device ABI, or you need to set special permissions/secontext for some of your module files.

If you would like to fully control and customize the installation process, declare SKIPUNZIP=1 in customize.sh to skip all default installation steps. By doing so, your customize.sh will be responsible to install everything by itself.

The customize.sh script runs in KernelSU's BusyBox ash shell with "Standalone Mode" enabled. The following variables and functions are available:

Variables

  • KSU (bool): a variable to mark that the script is running in the KernelSU environment, and the value of this variable will always be true. You can use it to distinguish between KernelSU and Magisk.
  • KSU_VER (string): the version string of current installed KernelSU (e.g. v0.4.0)
  • KSU_VER_CODE (int): the version code of current installed KernelSU in userspace (e.g. 10672)
  • KSU_KERNEL_VER_CODE (int): the version code of current installed KernelSU in kernel space (e.g. 10672)
  • BOOTMODE (bool): always be true in KernelSU
  • MODPATH (path): the path where your module files should be installed
  • TMPDIR (path): a place where you can temporarily store files
  • ZIPFILE (path): your module's installation zip
  • ARCH (string): the CPU architecture of the device. Value is either arm, arm64, x86, or x64
  • IS64BIT (bool): true if $ARCH is either arm64 or x64
  • API (int): the API level (Android version) of the device (e.g. 23 for Android 6.0)

WARNING

In KernelSU, MAGISK_VER_CODE is always 25200 and MAGISK_VER is always v25.2. Please do not use these two variables to determine whether it is running on KernelSU or not.

Functions

txt
ui_print <msg>
-    print <msg> to console
-    Avoid using 'echo' as it will not display in custom recovery's console
-
-abort <msg>
-    print error message <msg> to console and terminate the installation
-    Avoid using 'exit' as it will skip the termination cleanup steps
-
-set_perm <target> <owner> <group> <permission> [context]
-    if [context] is not set, the default is "u:object_r:system_file:s0"
-    this function is a shorthand for the following commands:
-       chown owner.group target
-       chmod permission target
-       chcon context target
-
-set_perm_recursive <directory> <owner> <group> <dirpermission> <filepermission> [context]
-    if [context] is not set, the default is "u:object_r:system_file:s0"
-    for all files in <directory>, it will call:
-       set_perm file owner group filepermission context
-    for all directories in <directory> (including itself), it will call:
-       set_perm dir owner group dirpermission context

Boot scripts

In KernelSU, scripts are divided into two types based on their running mode: post-fs-data mode and late_start service mode:

  • post-fs-data mode
    • This stage is BLOCKING. The boot process is paused before execution is done, or 10 seconds have passed.
    • Scripts run before any modules are mounted. This allows a module developer to dynamically adjust their modules before it gets mounted.
    • This stage happens before Zygote is started, which pretty much means everything in Android
    • WARNING: using setprop will deadlock the boot process! Please use resetprop -n <prop_name> <prop_value> instead.
    • Only run scripts in this mode if necessary.
  • late_start service mode
    • This stage is NON-BLOCKING. Your script runs in parallel with the rest of the booting process.
    • This is the recommended stage to run most scripts.

In KernelSU, startup scripts are divided into two types based on their storage location: general scripts and module scripts:

  • General Scripts
    • Placed in /data/adb/post-fs-data.d, /data/adb/service.d, /data/adb/post-mount.d or /data/adb/boot-completed.d
    • Only executed if the script is set as executable (chmod +x script.sh)
    • Scripts in post-fs-data.d runs in post-fs-data mode, and scripts in service.d runs in late_start service mode.
    • Modules should NOT add general scripts during installation
  • Module Scripts
    • Placed in the module's own folder
    • Only executed if the module is enabled
    • post-fs-data.sh runs in post-fs-data mode, service.sh runs in late_start service mode, boot-completed.sh runs on boot completed, post-mount.sh runs on overlayfs mounted.

All boot scripts will run in KernelSU's BusyBox ash shell with "Standalone Mode" enabled.

Released under the GPL3 License.

- +
Skip to content

Module guides

KernelSU provides a module mechanism that achieves the effect of modifying the system directory while maintaining the integrity of the system partition. This mechanism is commonly known as "systemless".

The module mechanism of KernelSU is almost the same as that of Magisk. If you are familiar with Magisk module development, developing KernelSU modules is very similar. You can skip the introduction of modules below and only need to read difference-with-magisk.

Busybox

KernelSU ships with a feature complete BusyBox binary (including full SELinux support). The executable is located at /data/adb/ksu/bin/busybox. KernelSU's BusyBox supports runtime toggle-able "ASH Standalone Shell Mode". What this standalone mode means is that when running in the ash shell of BusyBox, every single command will directly use the applet within BusyBox, regardless of what is set as PATH. For example, commands like ls, rm, chmod will NOT use what is in PATH (in the case of Android by default it will be /system/bin/ls, /system/bin/rm, and /system/bin/chmod respectively), but will instead directly call internal BusyBox applets. This makes sure that scripts always run in a predictable environment and always have the full suite of commands no matter which Android version it is running on. To force a command not to use BusyBox, you have to call the executable with full paths.

Every single shell script running in the context of KernelSU will be executed in BusyBox's ash shell with standalone mode enabled. For what is relevant to 3rd party developers, this includes all boot scripts and module installation scripts.

For those who want to use this "Standalone Mode" feature outside of KernelSU, there are 2 ways to enable it:

  1. Set environment variable ASH_STANDALONE to 1
    Example: ASH_STANDALONE=1 /data/adb/ksu/bin/busybox sh <script>
  2. Toggle with command-line options:
    /data/adb/ksu/bin/busybox sh -o standalone <script>

To make sure all subsequent sh shell executed also runs in standalone mode, option 1 is the preferred method (and this is what KernelSU and the KernelSU manager internally use) as environment variables are inherited down to child processes.

difference with Magisk

KernelSU's BusyBox is now using the binary file compiled directly from the Magisk project. Thanks to Magisk! Therefore, you don't have to worry about compatibility issues between BusyBox scripts in Magisk and KernelSU because they are exactly the same!

KernelSU modules

A KernelSU module is a folder placed in /data/adb/modules with the structure below:

txt
/data/adb/modules
+├── .
+├── .
+|
+├── $MODID                  <--- The folder is named with the ID of the module
+│   │
+│   │      *** Module Identity ***
+│   │
+│   ├── module.prop         <--- This file stores the metadata of the module
+│   │
+│   │      *** Main Contents ***
+│   │
+│   ├── system              <--- This folder will be mounted if skip_mount does not exist
+│   │   ├── ...
+│   │   ├── ...
+│   │   └── ...
+│   │
+│   │      *** Status Flags ***
+│   │
+│   ├── skip_mount          <--- If exists, KernelSU will NOT mount your system folder
+│   ├── disable             <--- If exists, the module will be disabled
+│   ├── remove              <--- If exists, the module will be removed next reboot
+│   │
+│   │      *** Optional Files ***
+│   │
+│   ├── post-fs-data.sh     <--- This script will be executed in post-fs-data
+│   ├── post-mount.sh       <--- This script will be executed in post-mount
+│   ├── service.sh          <--- This script will be executed in late_start service
+│   ├── boot-completed.sh   <--- This script will be executed on boot completed
+|   ├── uninstall.sh        <--- This script will be executed when KernelSU removes your module
+│   ├── system.prop         <--- Properties in this file will be loaded as system properties by resetprop
+│   ├── sepolicy.rule       <--- Additional custom sepolicy rules
+│   │
+│   │      *** Auto Generated, DO NOT MANUALLY CREATE OR MODIFY ***
+│   │
+│   ├── vendor              <--- A symlink to $MODID/system/vendor
+│   ├── product             <--- A symlink to $MODID/system/product
+│   ├── system_ext          <--- A symlink to $MODID/system/system_ext
+│   │
+│   │      *** Any additional files / folders are allowed ***
+│   │
+│   ├── ...
+│   └── ...
+|
+├── another_module
+│   ├── .
+│   └── .
+├── .
+├── .
/data/adb/modules
+├── .
+├── .
+|
+├── $MODID                  <--- The folder is named with the ID of the module
+│   │
+│   │      *** Module Identity ***
+│   │
+│   ├── module.prop         <--- This file stores the metadata of the module
+│   │
+│   │      *** Main Contents ***
+│   │
+│   ├── system              <--- This folder will be mounted if skip_mount does not exist
+│   │   ├── ...
+│   │   ├── ...
+│   │   └── ...
+│   │
+│   │      *** Status Flags ***
+│   │
+│   ├── skip_mount          <--- If exists, KernelSU will NOT mount your system folder
+│   ├── disable             <--- If exists, the module will be disabled
+│   ├── remove              <--- If exists, the module will be removed next reboot
+│   │
+│   │      *** Optional Files ***
+│   │
+│   ├── post-fs-data.sh     <--- This script will be executed in post-fs-data
+│   ├── post-mount.sh       <--- This script will be executed in post-mount
+│   ├── service.sh          <--- This script will be executed in late_start service
+│   ├── boot-completed.sh   <--- This script will be executed on boot completed
+|   ├── uninstall.sh        <--- This script will be executed when KernelSU removes your module
+│   ├── system.prop         <--- Properties in this file will be loaded as system properties by resetprop
+│   ├── sepolicy.rule       <--- Additional custom sepolicy rules
+│   │
+│   │      *** Auto Generated, DO NOT MANUALLY CREATE OR MODIFY ***
+│   │
+│   ├── vendor              <--- A symlink to $MODID/system/vendor
+│   ├── product             <--- A symlink to $MODID/system/product
+│   ├── system_ext          <--- A symlink to $MODID/system/system_ext
+│   │
+│   │      *** Any additional files / folders are allowed ***
+│   │
+│   ├── ...
+│   └── ...
+|
+├── another_module
+│   ├── .
+│   └── .
+├── .
+├── .

difference with Magisk

KernelSU does not have built-in support for Zygisk, so there is no content related to Zygisk in the module. However, you can use ZygiskNext to support Zygisk modules. In this case, the content of the Zygisk module is identical to that supported by Magisk.

module.prop

module.prop is a configuration file for a module. In KernelSU, if a module does not contain this file, it will not be recognized as a module. The format of this file is as follows:

txt
id=<string>
+name=<string>
+version=<string>
+versionCode=<int>
+author=<string>
+description=<string>
id=<string>
+name=<string>
+version=<string>
+versionCode=<int>
+author=<string>
+description=<string>
  • id has to match this regular expression: ^[a-zA-Z][a-zA-Z0-9._-]+$
    ex: ✓ a_module, ✓ a.module, ✓ module-101, ✗ a module, ✗ 1_module, ✗ -a-module
    This is the unique identifier of your module. You should not change it once published.
  • versionCode has to be an integer. This is used to compare versions
  • Others that weren't mentioned above can be any single line string.
  • Make sure to use the UNIX (LF) line break type and not the Windows (CR+LF) or Macintosh (CR).

Shell scripts

Please read the Boot Scripts section to understand the difference between post-fs-data.sh and service.sh. For most module developers, service.sh should be good enough if you just need to run a boot script, if you need to run the script after boot completed, please use boot-completed.sh. If you want to do something after mounting overlayfs, please use post-mount.sh.

In all scripts of your module, please use MODDIR=${0%/*} to get your module's base directory path; do NOT hardcode your module path in scripts.

difference with Magisk

You can use the environment variable KSU to determine if a script is running in KernelSU or Magisk. If running in KernelSU, this value will be set to true.

system directory

The contents of this directory will be overlaid on top of the system's /system partition using overlayfs after the system is booted. This means that:

  1. Files with the same name as those in the corresponding directory in the system will be overwritten by the files in this directory.
  2. Folders with the same name as those in the corresponding directory in the system will be merged with the folders in this directory.

If you want to delete a file or folder in the original system directory, you need to create a file with the same name as the file/folder in the module directory using mknod filename c 0 0. This way, the overlayfs system will automatically "whiteout" this file as if it has been deleted (the /system partition is not actually changed).

You can also declare a variable named REMOVE containing a list of directories in customize.sh to execute removal operations, and KernelSU will automatically execute mknod <TARGET> c 0 0 in the corresponding directories of the module. For example:

sh
REMOVE="
+/system/app/YouTube
+/system/app/Bloatware
+"
REMOVE="
+/system/app/YouTube
+/system/app/Bloatware
+"

The above list will execute mknod $MODPATH/system/app/YouTuBe c 0 0 and mknod $MODPATH/system/app/Bloatware c 0 0; and /system/app/YouTube and /system/app/Bloatware will be removed after the module takes effect.

If you want to replace a directory in the system, you need to create a directory with the same path in your module directory, and then set the attribute setfattr -n trusted.overlay.opaque -v y <TARGET> for this directory. This way, the overlayfs system will automatically replace the corresponding directory in the system (without changing the /system partition).

You can declare a variable named REPLACE in your customize.sh file, which includes a list of directories to be replaced, and KernelSU will automatically perform the corresponding operations in your module directory. For example:

REPLACE=" /system/app/YouTube /system/app/Bloatware "

This list will automatically create the directories $MODPATH/system/app/YouTube and $MODPATH/system/app/Bloatware, and then execute setfattr -n trusted.overlay.opaque -v y $MODPATH/system/app/YouTube and setfattr -n trusted.overlay.opaque -v y $MODPATH/system/app/Bloatware. After the module takes effect, /system/app/YouTube and /system/app/Bloatware will be replaced with empty directories.

difference with Magisk

KernelSU's systemless mechanism is implemented through the kernel's overlayfs, while Magisk currently uses magic mount (bind mount). The two implementation methods have significant differences, but the ultimate goal is the same: to modify /system files without physically modifying the /system partition.

If you are interested in overlayfs, it is recommended to read the Linux Kernel's documentation on overlayfs.

system.prop

This file follows the same format as build.prop. Each line comprises of [key]=[value].

sepolicy.rule

If your module requires some additional sepolicy patches, please add those rules into this file. Each line in this file will be treated as a policy statement.

Module installer

A KernelSU module installer is a KernelSU module packaged in a zip file that can be flashed in the KernelSU manager APP. The simplest KernelSU module installer is just a KernelSU module packed as a zip file.

txt
module.zip
+
+├── customize.sh                       <--- (Optional, more details later)
+│                                           This script will be sourced by update-binary
+├── ...
+├── ...  /* The rest of module's files */
+
module.zip
+
+├── customize.sh                       <--- (Optional, more details later)
+│                                           This script will be sourced by update-binary
+├── ...
+├── ...  /* The rest of module's files */
+

WARNING

KernelSU module is NOT supported to be installed in custom recovery!!

Customization

If you need to customize the module installation process, optionally you can create a script in the installer named customize.sh. This script will be sourced (not executed!) by the module installer script after all files are extracted and default permissions and secontext are applied. This is very useful if your module require additional setup based on the device ABI, or you need to set special permissions/secontext for some of your module files.

If you would like to fully control and customize the installation process, declare SKIPUNZIP=1 in customize.sh to skip all default installation steps. By doing so, your customize.sh will be responsible to install everything by itself.

The customize.sh script runs in KernelSU's BusyBox ash shell with "Standalone Mode" enabled. The following variables and functions are available:

Variables

  • KSU (bool): a variable to mark that the script is running in the KernelSU environment, and the value of this variable will always be true. You can use it to distinguish between KernelSU and Magisk.
  • KSU_VER (string): the version string of current installed KernelSU (e.g. v0.4.0)
  • KSU_VER_CODE (int): the version code of current installed KernelSU in userspace (e.g. 10672)
  • KSU_KERNEL_VER_CODE (int): the version code of current installed KernelSU in kernel space (e.g. 10672)
  • BOOTMODE (bool): always be true in KernelSU
  • MODPATH (path): the path where your module files should be installed
  • TMPDIR (path): a place where you can temporarily store files
  • ZIPFILE (path): your module's installation zip
  • ARCH (string): the CPU architecture of the device. Value is either arm, arm64, x86, or x64
  • IS64BIT (bool): true if $ARCH is either arm64 or x64
  • API (int): the API level (Android version) of the device (e.g. 23 for Android 6.0)

WARNING

In KernelSU, MAGISK_VER_CODE is always 25200 and MAGISK_VER is always v25.2. Please do not use these two variables to determine whether it is running on KernelSU or not.

Functions

txt
ui_print <msg>
+    print <msg> to console
+    Avoid using 'echo' as it will not display in custom recovery's console
+
+abort <msg>
+    print error message <msg> to console and terminate the installation
+    Avoid using 'exit' as it will skip the termination cleanup steps
+
+set_perm <target> <owner> <group> <permission> [context]
+    if [context] is not set, the default is "u:object_r:system_file:s0"
+    this function is a shorthand for the following commands:
+       chown owner.group target
+       chmod permission target
+       chcon context target
+
+set_perm_recursive <directory> <owner> <group> <dirpermission> <filepermission> [context]
+    if [context] is not set, the default is "u:object_r:system_file:s0"
+    for all files in <directory>, it will call:
+       set_perm file owner group filepermission context
+    for all directories in <directory> (including itself), it will call:
+       set_perm dir owner group dirpermission context
ui_print <msg>
+    print <msg> to console
+    Avoid using 'echo' as it will not display in custom recovery's console
+
+abort <msg>
+    print error message <msg> to console and terminate the installation
+    Avoid using 'exit' as it will skip the termination cleanup steps
+
+set_perm <target> <owner> <group> <permission> [context]
+    if [context] is not set, the default is "u:object_r:system_file:s0"
+    this function is a shorthand for the following commands:
+       chown owner.group target
+       chmod permission target
+       chcon context target
+
+set_perm_recursive <directory> <owner> <group> <dirpermission> <filepermission> [context]
+    if [context] is not set, the default is "u:object_r:system_file:s0"
+    for all files in <directory>, it will call:
+       set_perm file owner group filepermission context
+    for all directories in <directory> (including itself), it will call:
+       set_perm dir owner group dirpermission context

Boot scripts

In KernelSU, scripts are divided into two types based on their running mode: post-fs-data mode and late_start service mode:

  • post-fs-data mode
    • This stage is BLOCKING. The boot process is paused before execution is done, or 10 seconds have passed.
    • Scripts run before any modules are mounted. This allows a module developer to dynamically adjust their modules before it gets mounted.
    • This stage happens before Zygote is started, which pretty much means everything in Android
    • WARNING: using setprop will deadlock the boot process! Please use resetprop -n <prop_name> <prop_value> instead.
    • Only run scripts in this mode if necessary.
  • late_start service mode
    • This stage is NON-BLOCKING. Your script runs in parallel with the rest of the booting process.
    • This is the recommended stage to run most scripts.

In KernelSU, startup scripts are divided into two types based on their storage location: general scripts and module scripts:

  • General Scripts
    • Placed in /data/adb/post-fs-data.d, /data/adb/service.d, /data/adb/post-mount.d or /data/adb/boot-completed.d
    • Only executed if the script is set as executable (chmod +x script.sh)
    • Scripts in post-fs-data.d runs in post-fs-data mode, and scripts in service.d runs in late_start service mode.
    • Modules should NOT add general scripts during installation
  • Module Scripts
    • Placed in the module's own folder
    • Only executed if the module is enabled
    • post-fs-data.sh runs in post-fs-data mode, service.sh runs in late_start service mode, boot-completed.sh runs on boot completed, post-mount.sh runs on overlayfs mounted.

All boot scripts will run in KernelSU's BusyBox ash shell with "Standalone Mode" enabled.

Released under the GPL3 License.

+ \ No newline at end of file diff --git a/guide/rescue-from-bootloop.html b/guide/rescue-from-bootloop.html index 15bab8bf4318..651e7bf591e6 100644 --- a/guide/rescue-from-bootloop.html +++ b/guide/rescue-from-bootloop.html @@ -5,18 +5,19 @@ Rescue from bootloop | KernelSU - - + + + - - - - + + + + + -
Skip to content
On this page

Rescue from bootloop

When flashing a device, we may encounter situations where the device becomes "bricked". In theory, if you only use fastboot to flash the boot partition or install unsuitable modules that cause the device to fail to boot, then this can be restored by appropriate operations. This document aims to provide some emergency methods to help you recover from a "bricked" device.

Brick by flashing boot partition

In KernelSU, the following situations may cause boot brick when flashing the boot partition:

  1. You flashed a boot image in the wrong format. For example, if your phone's boot format is gz, but you flashed an lz4 format image, then the phone will not be able to boot.
  2. Your phone needs to disable AVB verification in order to boot properly (usually requiring wiping all data on the phone).
  3. Your kernel has some bugs or is not suitable for your phone to flash.

No matter what the situation is, you can recover by flashing the stock boot image. Therefore, at the beginning of the installation tutorial, we strongly recommend that you back up your stock boot before flashing. If you haven't backed up, you can obtain the original factory boot from other users with the same device as you or from the official firmware.

Brick by modules

Installing modules can be a more common cause of bricking your device, but we must seriously warn you: Do not install modules from unknown sources! Since modules have root privileges, they can potentially cause irreversible damage to your device!

Normal modules

If you have flashed a module that has been proven to be safe but causes your device to fail to boot, then this situation is easily recoverable in KernelSU without any worries. KernelSU has built-in mechanisms to rescue your device, including the following:

  1. AB update
  2. Rescue by pressing Volume Down

AB update

KernelSU's module updates draw inspiration from the Android system's AB update mechanism used in OTA updates. If you install a new module or update an existing one, it will not directly modify the currently used module file. Instead, all modules will be built into another update image. After the system is restarted, it will attempt to start using this update image. If the Android system successfully boots up, the modules will then be truly updated.

Therefore, the simplest and most commonly used method to rescue your device is to force a reboot. If you are unable to start your system after flashing a module, you can press and hold the power button for more than 10 seconds, and the system will automatically reboot; after rebooting, it will roll back to the state before updating the module, and the previously updated modules will be automatically disabled.

Rescue by pressing Volume Down

If AB updates still cannot solve the problem, you can try using Safe Mode. In Safe Mode, all modules are disabled.

There are two ways to enter Safe Mode:

  1. The built-in Safe Mode of some systems; some systems have a built-in Safe Mode that can be accessed by long-pressing the volume down button, while others (such as MIUI) can enable Safe Mode in Recovery. When entering the system's Safe Mode, KernelSU will also enter Safe Mode and automatically disable modules.
  2. The built-in Safe Mode of KernelSU; the operation method is to press the volume down key continuously for more than three times after the first boot screen. Note that it is press-release, press-release, press-release, not press and hold.

After entering safe mode, all modules on the module page of the KernelSU Manager are disabled, but you can perform "uninstall" operations to uninstall any modules that may be causing issues.

The built-in safe mode is implemented in the kernel, so there is no possibility of missing key events due to interception. However, for non-GKI kernels, manual integration of the code may be required, and you can refer to the official documentation for guidance.

Malicious modules

If the above methods cannot rescue your device, it is highly likely that the module you installed has malicious operations or has damaged your device through other means. In this case, there are only two suggestions:

  1. Wipe the data and flash the official system.
  2. Consult the after-sales service.

Released under the GPL3 License.

- +
Skip to content

Rescue from bootloop

When flashing a device, we may encounter situations where the device becomes "bricked". In theory, if you only use fastboot to flash the boot partition or install unsuitable modules that cause the device to fail to boot, then this can be restored by appropriate operations. This document aims to provide some emergency methods to help you recover from a "bricked" device.

Brick by flashing boot partition

In KernelSU, the following situations may cause boot brick when flashing the boot partition:

  1. You flashed a boot image in the wrong format. For example, if your phone's boot format is gz, but you flashed an lz4 format image, then the phone will not be able to boot.
  2. Your phone needs to disable AVB verification in order to boot properly (usually requiring wiping all data on the phone).
  3. Your kernel has some bugs or is not suitable for your phone to flash.

No matter what the situation is, you can recover by flashing the stock boot image. Therefore, at the beginning of the installation tutorial, we strongly recommend that you back up your stock boot before flashing. If you haven't backed up, you can obtain the original factory boot from other users with the same device as you or from the official firmware.

Brick by modules

Installing modules can be a more common cause of bricking your device, but we must seriously warn you: Do not install modules from unknown sources! Since modules have root privileges, they can potentially cause irreversible damage to your device!

Normal modules

If you have flashed a module that has been proven to be safe but causes your device to fail to boot, then this situation is easily recoverable in KernelSU without any worries. KernelSU has built-in mechanisms to rescue your device, including the following:

  1. AB update
  2. Rescue by pressing Volume Down

AB update

KernelSU's module updates draw inspiration from the Android system's AB update mechanism used in OTA updates. If you install a new module or update an existing one, it will not directly modify the currently used module file. Instead, all modules will be built into another update image. After the system is restarted, it will attempt to start using this update image. If the Android system successfully boots up, the modules will then be truly updated.

Therefore, the simplest and most commonly used method to rescue your device is to force a reboot. If you are unable to start your system after flashing a module, you can press and hold the power button for more than 10 seconds, and the system will automatically reboot; after rebooting, it will roll back to the state before updating the module, and the previously updated modules will be automatically disabled.

Rescue by pressing Volume Down

If AB updates still cannot solve the problem, you can try using Safe Mode. In Safe Mode, all modules are disabled.

There are two ways to enter Safe Mode:

  1. The built-in Safe Mode of some systems; some systems have a built-in Safe Mode that can be accessed by long-pressing the volume down button, while others (such as MIUI) can enable Safe Mode in Recovery. When entering the system's Safe Mode, KernelSU will also enter Safe Mode and automatically disable modules.
  2. The built-in Safe Mode of KernelSU; the operation method is to press the volume down key continuously for more than three times after the first boot screen. Note that it is press-release, press-release, press-release, not press and hold.

After entering safe mode, all modules on the module page of the KernelSU Manager are disabled, but you can perform "uninstall" operations to uninstall any modules that may be causing issues.

The built-in safe mode is implemented in the kernel, so there is no possibility of missing key events due to interception. However, for non-GKI kernels, manual integration of the code may be required, and you can refer to the official documentation for guidance.

Malicious modules

If the above methods cannot rescue your device, it is highly likely that the module you installed has malicious operations or has damaged your device through other means. In this case, there are only two suggestions:

  1. Wipe the data and flash the official system.
  2. Consult the after-sales service.

Released under the GPL3 License.

+ \ No newline at end of file diff --git a/guide/unofficially-support-devices.html b/guide/unofficially-support-devices.html index 58cb7b07cb95..015f02ba96c9 100644 --- a/guide/unofficially-support-devices.html +++ b/guide/unofficially-support-devices.html @@ -5,19 +5,20 @@ Unofficially Supported Devices | KernelSU - - + + + - - - - - + + + + + + -
Skip to content
On this page

Unofficially Supported Devices

WARNING

In this page, there are kernels for non-GKI devices supporting KernelSU maintained by other developers.

WARNING

This page is only for you to find the source code corresponding to your device, it does NOT mean that the source code has been reviewed by KernelSU Developers. You should use it at your own risk.

MaintainerRepositorySupport devices
diphonsD8G_Kernel_oxygenPoco F3: alioth | Poco F4: munch | MI10T/PRO: Apollo
diphonsD8G_Kernel_SM8150Poco X3 Pro: Vayu | Bhima
diphonsD8G_Kernel_SDM845Poco F1 | MI8 | MiMix2S
diphonsD8G_Kernel_SM8350Vili
diphonsD8G_Kernel_MarblePoco F5 | Redmi Note 12 Turbo | Marble
th1nhhdkandroid_kernel_sony_sm8250-kernelsuSony Xperia 1 II | Sony Xperia 5 II
akash07knexus_kernel_xiaomi_sm8250Poco F4: munch
HMTheBoy154Darkmatter-kernelGeneric x86_64 devices running Android-x86
Asuka-mioandroid_kernel_xiaomi_casMi 10 Ultra: cas
xiaoleGunMiku_kernel_xiaomi_waynewayne
YamazakuraHkernel_xiaomi_cepheuscepheus for pixel experience
SakuraNotStupidandroid_kernel_xiaomi_sdm710Xiaomi MI 8 SE(sirius/xmsirius)
Aquarius223(paper)android_kernel_xiaomi_msm8998MI 6 (sagit) and MIX 2 (chiron) for LineageOS
SlackerStateandroid_kernel_xiaomi_sm6150Redmi K30 4G (phoenix)
RooGhz720kernel_xiaomi_sm6150REDMI NOTE 10 PRO (sweet)
OnlyTomInSecondandroid_kernel_xiaomi_sdm845Mi 8 (dipper) for LineageOS
Rohail33RealKing KernelApollo(Redmi K30S Ultra/Mi 10T/Mi 10T Pro),Alioth(Redmi K40/POCO F3/Mi 11X),Munch(Redmi K40S/POCO F4), both MIUI and AOSP.
lateautumn233android_kernel_oneplus_sm8250OnePlus 8 Serials
Sreeshankar KNeverSettle KernelOnePlus Nord (avicii)
Molyuuneko_kernel_xiaomi_gauguinRedmi Note 9 Pro/ Mi 10T Lite/ Mi 10i
guh0613android_kernel_oppo_sm8150OPPO Reno Ace (OP4A89)
LeviMarvinandroid_kernel_xiaomi_aliothRedmi K40 / POCO F3
cibimokernel_xiaomi_raphael_ksuXiaomi Redmi K20 Pro / Mi 9T Pro (raphael)
EndCreditskernel_xiaomi_sm7250Redmi K30 5G ( picasso ) , Redmi K30i 5G ( picasso_48m)
msnxandroid-msm-coral-4.14-android13Pixel 4 XL
SoDebugkernel_xiaomi_raphaelXiaomi Redmi K20 Pro / Mi 9T Pro (raphael)
H1mJTkernel_realme_RMX1901Realme X (RMX1901/RMX1901CN)
SonalSingh18android_kernel_xiaomi_sm6250Miatoll [curtana, excalibur, gram, joyeuse]
RooGhz720kernel_xiaomi_lavenderRedmi Note 7 (Lavender)
JunASAKAkernel_google_msm-4.9_KernelSUGoogle Pixel 3a & 3a XL (sargo & bonito)
RooGhz720kernel_asus_X01BDAsus Zenfone Max Pro M1/M2
Evans Mikekernel_xiaomi_raphael_bool-xXiaomi Redmi K20 Pro / Mi 9T Pro (raphael)
easterNdayKSU_Thyme_BuildBotXiaomi 10S
tedomi2705kernel_xiaomi_sdm660Xiaomi Redmi Note 6 Pro (tulip)
RyuujiXandroid_kernel_asus_sdm660-4.19Asus Zenfone Max Pro M1 (X00TD)/ M2 (X01BD) (Linux 4.19)
liqidecgandroid_kernel_xiaomi_sdm710Mi 8 SE (sirius) (Linux 4.9)
Abdelhay-Aliandroid_kernel_huawei_hi6250Huawei P20 lite (hi6250) (Linux 4.9)
reocatandroid_kernel_nokia_sdm660_ksuNokia 6.1 (2018)
Tuan Anhkernel_nokia_msm8998Nokia 8 (Repartitioned)/8 Sirocco (NLA/A1N)
bggRGjQaUbCoEandroid_kernel_samsung_sm8250Samsung Galaxy S20+ 5G (y2q)
SOVIET-ANDROIDkernel_xiaomi_raphaelXK20 Pro Raphael DSP & A only SAR support
dabao1955android_kernel_OPPO_PEQM00MediaTek devices in the OPPO Reno series on ColorOS13.x
Aflaungosandroid_kernel_motorola_msm8998Moto G6 Plus (evert)
TheNoFacekernel_oneplus_msm8998OnePlus 5/5T (cheeseburger/dumpling)
Nipin NA (Joker-V2)Xcalibur kernel (violet)Xiaomi Redmi Note 7 Pro (violet)
Sr-Hankernel_xiaomi_mojitoRedmi Note 10 (Sunny/Mojito)
dabao1955android_kernel_OPPO_OP4ED5OPPO Reno6 on ColorOS11.x/ColorOS12.x,kernel version4.14.186+
rushiranpiseandroid_kernel_samsung_exynos9610_mintKernel 4.14.194 exynos9610 Non-GKI Device, Added KernelSu using manual method
CN-Scarspixel_experience_kernel_xiaomi_sm8250_kernelSUPixel Experience Kernel 4.19.282 for Xiaomi Redmi K40S / Xiaomi Poco F4 (munch)
exerMiyo TokuSony Tama (akari, apollo, aurora, akatsuki) (Linux 4.9)
likkaiQuicksilver KernelXiaomi 11 Lite 5G NE (lisa)
awakened1712android_kernel_samsung_exynos9820Samsung S10/N10
awakened1712android_kernel_oneplus_sm8350Oneplus 9/9Pro
siimsekLightning KernelXiaomi Redmi Note 8/8T (ginkgo/willow)
GiovanYCringekernel_a50Galaxy A50 (A505f,fn,g,gn,gt)
Asriadi Rahimandroid_kernel_google_wahooGoogle Pixel 2/2XL
Flameandroid_kernel_xiaomi_sm8250POCO F3/Redmi K40/Mi 11X (alioth)
DawfukFRkernel_oneplus_sm8250 (Stellaris-Kernel)Oneplus 8 (Instantnoodle), Oneplus 8T (Kebab), Oneplus 8 Pro (Instantnoodlep), Oneplus 9R (lemonades) [msm-4.19]
Tejas Singhkernel_xiaomi_ginkgo (Cuh Kernel)Xiaomi Redmi Note 8/8T (ginkgo/willow)
Coconutatandroid_kernel_xiaomi_ruby_expRedmi Note 12 Pro / Pro+ For MIUI 14
Coconutatandroid_kernel_xiaomi_sdm845_expXiaomi Mi 8 Pro(UD)[equuleus]/Explorer Edition[ursa]
Abdul Wahid Khankernel_xiaomi_begoniaRedmi Note 8 Pro (Begonia/Begoniain)
Abdul Wahid Khankernel_xiaomi_marbleXiaomi Poco F5 / Redmi Note 12 Turbo (Marble)
zfdx123kernel_xiaomi_aliothRedmi K40 (alioth)
wxt1221android_kernel_oneplus_sdm845Oneplus 6 (enchilada) | Oneplus 6T (fajita) with no Retrofit Dynamic Partitions
zharzinhooKernel-Oriente-GuampMoto g9 play
hc841NovaCore_kernelMoto g9 play
zlm324android_kernel_xiaomi_msm8998_ksua lineage kernel forked from LineageOS official repository, version 4.4.302, added ksu.
zlm324android_kernel_xiaomi_msm8953_ksuXiaomi 5X (tiffany)
Vincent4440android_kernel_xiaomi_sm8250Poco F4: munch
zharzinhooandroid_kernel_motorola_cebuMoto g9 power
tiandoufayaleandroid_kernel_samsung_sm8250Samsung Tab S7 WIFI(T870)
starmoeandroid_kernel_oneplus_msm8998Oneplus 5/5T, kernel version 4.4.302
supechickenkernel_xiaomi_sm8350 Xiaomi 11T Pro (vili)
Vaz15kkernelSU-A70qSamsung Galaxy A70 ( A705MN / A705FN )
whyakariandroid_kernel_xiaomi_ginkgonon-GKI ginkgo
wulan17android_kernel_xiaomi_mt6765Redmi 6/6A (cereus/cactus)
officialputuidandroid_kernel_realme_mt6785Realme 6/6i(Indian)/6s/7/Narzo/Narzo 20 Pro/Narzo 30 4G (RM6785)

Released under the GPL3 License.

- +
Skip to content

Unofficially Supported Devices

WARNING

In this page, there are kernels for non-GKI devices supporting KernelSU maintained by other developers.

WARNING

This page is only for you to find the source code corresponding to your device, it does NOT mean that the source code has been reviewed by KernelSU Developers. You should use it at your own risk.

MaintainerRepositorySupport devices
diphonsD8G_Kernel_oxygenPoco F3: alioth | Poco F4: munch | MI10T/PRO: Apollo
diphonsD8G_Kernel_SM8150Poco X3 Pro: Vayu | Bhima
diphonsD8G_Kernel_SDM845Poco F1 | MI8 | MiMix2S
diphonsD8G_Kernel_SM8350Vili
diphonsD8G_Kernel_MarblePoco F5 | Redmi Note 12 Turbo | Marble
th1nhhdkandroid_kernel_sony_sm8250-kernelsuSony Xperia 1 II | Sony Xperia 5 II
akash07knexus_kernel_xiaomi_sm8250Poco F4: munch
HMTheBoy154Darkmatter-kernelGeneric x86_64 devices running Android-x86
Asuka-mioandroid_kernel_xiaomi_casMi 10 Ultra: cas
xiaoleGunMiku_kernel_xiaomi_waynewayne
YamazakuraHkernel_xiaomi_cepheuscepheus for pixel experience
SakuraNotStupidandroid_kernel_xiaomi_sdm710Xiaomi MI 8 SE(sirius/xmsirius)
Aquarius223(paper)android_kernel_xiaomi_msm8998MI 6 (sagit) and MIX 2 (chiron) for LineageOS
SlackerStateandroid_kernel_xiaomi_sm6150Redmi K30 4G (phoenix)
RooGhz720kernel_xiaomi_sm6150REDMI NOTE 10 PRO (sweet)
OnlyTomInSecondandroid_kernel_xiaomi_sdm845Mi 8 (dipper) for LineageOS
Rohail33RealKing KernelApollo(Redmi K30S Ultra/Mi 10T/Mi 10T Pro),Alioth(Redmi K40/POCO F3/Mi 11X),Munch(Redmi K40S/POCO F4), both MIUI and AOSP.
lateautumn233android_kernel_oneplus_sm8250OnePlus 8 Serials
Sreeshankar KNeverSettle KernelOnePlus Nord (avicii)
Molyuuneko_kernel_xiaomi_gauguinRedmi Note 9 Pro/ Mi 10T Lite/ Mi 10i
guh0613android_kernel_oppo_sm8150OPPO Reno Ace (OP4A89)
LeviMarvinandroid_kernel_xiaomi_aliothRedmi K40 / POCO F3
cibimokernel_xiaomi_raphael_ksuXiaomi Redmi K20 Pro / Mi 9T Pro (raphael)
EndCreditskernel_xiaomi_sm7250Redmi K30 5G ( picasso ) , Redmi K30i 5G ( picasso_48m)
msnxandroid-msm-coral-4.14-android13Pixel 4 XL
SoDebugkernel_xiaomi_raphaelXiaomi Redmi K20 Pro / Mi 9T Pro (raphael)
H1mJTkernel_realme_RMX1901Realme X (RMX1901/RMX1901CN)
SonalSingh18android_kernel_xiaomi_sm6250Miatoll [curtana, excalibur, gram, joyeuse]
RooGhz720kernel_xiaomi_lavenderRedmi Note 7 (Lavender)
JunASAKAkernel_google_msm-4.9_KernelSUGoogle Pixel 3a & 3a XL (sargo & bonito)
RooGhz720kernel_asus_X01BDAsus Zenfone Max Pro M1/M2
Evans Mikekernel_xiaomi_raphael_bool-xXiaomi Redmi K20 Pro / Mi 9T Pro (raphael)
easterNdayKSU_Thyme_BuildBotXiaomi 10S
tedomi2705kernel_xiaomi_sdm660Xiaomi Redmi Note 6 Pro (tulip)
RyuujiXandroid_kernel_asus_sdm660-4.19Asus Zenfone Max Pro M1 (X00TD)/ M2 (X01BD) (Linux 4.19)
liqidecgandroid_kernel_xiaomi_sdm710Mi 8 SE (sirius) (Linux 4.9)
Abdelhay-Aliandroid_kernel_huawei_hi6250Huawei P20 lite (hi6250) (Linux 4.9)
reocatandroid_kernel_nokia_sdm660_ksuNokia 6.1 (2018)
Tuan Anhkernel_nokia_msm8998Nokia 8 (Repartitioned)/8 Sirocco (NLA/A1N)
bggRGjQaUbCoEandroid_kernel_samsung_sm8250Samsung Galaxy S20+ 5G (y2q)
SOVIET-ANDROIDkernel_xiaomi_raphaelXK20 Pro Raphael DSP & A only SAR support
dabao1955android_kernel_OPPO_PEQM00MediaTek devices in the OPPO Reno series on ColorOS13.x
Aflaungosandroid_kernel_motorola_msm8998Moto G6 Plus (evert)
TheNoFacekernel_oneplus_msm8998OnePlus 5/5T (cheeseburger/dumpling)
Nipin NA (Joker-V2)Xcalibur kernel (violet)Xiaomi Redmi Note 7 Pro (violet)
Sr-Hankernel_xiaomi_mojitoRedmi Note 10 (Sunny/Mojito)
dabao1955android_kernel_OPPO_OP4ED5OPPO Reno6 on ColorOS11.x/ColorOS12.x,kernel version4.14.186+
rushiranpiseandroid_kernel_samsung_exynos9610_mintKernel 4.14.194 exynos9610 Non-GKI Device, Added KernelSu using manual method
CN-Scarspixel_experience_kernel_xiaomi_sm8250_kernelSUPixel Experience Kernel 4.19.282 for Xiaomi Redmi K40S / Xiaomi Poco F4 (munch)
exerMiyo TokuSony Tama (akari, apollo, aurora, akatsuki) (Linux 4.9)
likkaiQuicksilver KernelXiaomi 11 Lite 5G NE (lisa)
awakened1712android_kernel_samsung_exynos9820Samsung S10/N10
awakened1712android_kernel_oneplus_sm8350Oneplus 9/9Pro
siimsekLightning KernelXiaomi Redmi Note 8/8T (ginkgo/willow)
GiovanYCringekernel_a50Galaxy A50 (A505f,fn,g,gn,gt)
Asriadi Rahimandroid_kernel_google_wahooGoogle Pixel 2/2XL
Flameandroid_kernel_xiaomi_sm8250POCO F3/Redmi K40/Mi 11X (alioth)
DawfukFRkernel_oneplus_sm8250 (Stellaris-Kernel)Oneplus 8 (Instantnoodle), Oneplus 8T (Kebab), Oneplus 8 Pro (Instantnoodlep), Oneplus 9R (lemonades) [msm-4.19]
Tejas Singhkernel_xiaomi_ginkgo (Cuh Kernel)Xiaomi Redmi Note 8/8T (ginkgo/willow)
Coconutatandroid_kernel_xiaomi_ruby_expRedmi Note 12 Pro / Pro+ For MIUI 14
Coconutatandroid_kernel_xiaomi_sdm845_expXiaomi Mi 8 Pro(UD)[equuleus]/Explorer Edition[ursa]
Abdul Wahid Khankernel_xiaomi_begoniaRedmi Note 8 Pro (Begonia/Begoniain)
Abdul Wahid Khankernel_xiaomi_marbleXiaomi Poco F5 / Redmi Note 12 Turbo (Marble)
zfdx123kernel_xiaomi_aliothRedmi K40 (alioth)
wxt1221android_kernel_oneplus_sdm845Oneplus 6 (enchilada) | Oneplus 6T (fajita) with no Retrofit Dynamic Partitions
zharzinhooKernel-Oriente-GuampMoto g9 play
hc841NovaCore_kernelMoto g9 play
zlm324android_kernel_xiaomi_msm8998_ksua lineage kernel forked from LineageOS official repository, version 4.4.302, added ksu.
zlm324android_kernel_xiaomi_msm8953_ksuXiaomi 5X (tiffany)
Vincent4440android_kernel_xiaomi_sm8250Poco F4: munch
zharzinhooandroid_kernel_motorola_cebuMoto g9 power
tiandoufayaleandroid_kernel_samsung_sm8250Samsung Tab S7 WIFI(T870)
starmoeandroid_kernel_oneplus_msm8998Oneplus 5/5T, kernel version 4.4.302
supechickenkernel_xiaomi_sm8350 Xiaomi 11T Pro (vili)
Vaz15kkernelSU-A70qSamsung Galaxy A70 ( A705MN / A705FN )
whyakariandroid_kernel_xiaomi_ginkgonon-GKI ginkgo
wulan17android_kernel_xiaomi_mt6765Redmi 6/6A (cereus/cactus)
officialputuidandroid_kernel_realme_mt6785Realme 6/6i(Indian)/6s/7/Narzo/Narzo 20 Pro/Narzo 30 4G (RM6785)

Released under the GPL3 License.

+ \ No newline at end of file diff --git a/guide/what-is-kernelsu.html b/guide/what-is-kernelsu.html index 588acdeba961..1783d8a11db8 100644 --- a/guide/what-is-kernelsu.html +++ b/guide/what-is-kernelsu.html @@ -5,18 +5,19 @@ What is KernelSU? | KernelSU - - + + + - - - - + + + + + -
Skip to content
On this page

What is KernelSU?

KernelSU is a root solution for Android GKI devices, it works in kernel mode and grant root permission to userspace application directly in kernel space.

Features

The main feature of KernelSU is it is Kernel-based. KernelSU works in kernel mode, so it can provide kernel interface we never had before. For example, we can add hardware breakpoint to any process in kernel mode; We can access physical memory of any process without anybody being aware of; We can intercept any syscall in kernel space; etc.

And also, KernelSU provides a module system via overlayfs, which allows you to load your custom plugin into system. It also provides a mechanism to modify files in /system partition.

How to use

Please refer: Installation

How to build

How to build

Discussion

Released under the GPL3 License.

- +
Skip to content

What is KernelSU?

KernelSU is a root solution for Android GKI devices, it works in kernel mode and grant root permission to userspace application directly in kernel space.

Features

The main feature of KernelSU is it is Kernel-based. KernelSU works in kernel mode, so it can provide kernel interface we never had before. For example, we can add hardware breakpoint to any process in kernel mode; We can access physical memory of any process without anybody being aware of; We can intercept any syscall in kernel space; etc.

And also, KernelSU provides a module system via overlayfs, which allows you to load your custom plugin into system. It also provides a mechanism to modify files in /system partition.

How to use

Please refer: Installation

How to build

How to build

Discussion

Released under the GPL3 License.

+ \ No newline at end of file diff --git a/hashmap.json b/hashmap.json index f8031fd943f8..f00d55eb2c69 100644 --- a/hashmap.json +++ b/hashmap.json @@ -1 +1 @@ -{"guide_difference-with-magisk.md":"88801b6b","guide_what-is-kernelsu.md":"340bbd63","id_id_index.md":"9ad2e601","ja_jp_index.md":"be3c7573","index.md":"042b4c20","pt_br_guide_app-profile.md":"7a2ced3a","id_id_guide_installation.md":"a7bb551d","id_id_guide_how-to-integrate-for-non-gki.md":"16b9216a","ja_jp_guide_faq.md":"998d2dfd","pt_br_guide_difference-with-magisk.md":"319599e7","id_id_guide_how-to-build.md":"1ecedb69","id_id_guide_rescue-from-bootloop.md":"ec3ef7ee","vi_vn_guide_app-profile.md":"37d09526","pt_br_guide_what-is-kernelsu.md":"b42bbc04","vi_vn_guide_rescue-from-bootloop.md":"d796809c","vi_vn_guide_difference-with-magisk.md":"efd07233","pt_br_guide_how-to-integrate-for-non-gki.md":"9373963e","pt_br_guide_installation.md":"8193d15a","pt_br_guide_how-to-build.md":"50a0b380","ru_ru_guide_app-profile.md":"8936210e","vi_vn_guide_faq.md":"e9230071","vi_vn_guide_hidden-features.md":"fca4632f","pt_br_guide_rescue-from-bootloop.md":"f4a13bcd","vi_vn_guide_how-to-build.md":"f5e5323c","guide_how-to-build.md":"cf9f2c40","guide_installation.md":"ec5f3b13","zh_tw_guide_rescue-from-bootloop.md":"db20cf2e","guide_faq.md":"234d8c0d","ru_ru_guide_how-to-build.md":"1b3dd3fb","id_id_guide_unofficially-support-devices.md":"3238b4be","zh_cn_index.md":"7dc4034d","zh_cn_guide_what-is-kernelsu.md":"cdc98b69","id_id_guide_faq.md":"8fe1170a","ja_jp_guide_unofficially-support-devices.md":"ca3f04b0","id_id_guide_what-is-kernelsu.md":"aee47f37","zh_cn_guide_module.md":"88839cd5","zh_tw_guide_module.md":"76671e26","zh_cn_guide_installation.md":"612797b7","zh_cn_guide_hidden-features.md":"cdd5624f","id_id_guide_module.md":"a65f7310","ru_ru_guide_installation.md":"bd641868","ru_ru_guide_difference-with-magisk.md":"d55db77b","vi_vn_guide_module.md":"907befd3","ru_ru_guide_rescue-from-bootloop.md":"d9d6936e","vi_vn_index.md":"a84edf49","zh_cn_guide_difference-with-magisk.md":"223f61d1","vi_vn_guide_what-is-kernelsu.md":"bea8d4b7","pt_br_guide_hidden-features.md":"df3f1d4b","zh_tw_guide_how-to-build.md":"ef330d07","zh_tw_guide_difference-with-magisk.md":"161de8ef","zh_cn_guide_rescue-from-bootloop.md":"3d907e03","ja_jp_guide_what-is-kernelsu.md":"779775a2","ru_ru_guide_unofficially-support-devices.md":"fdcfea2d","guide_hidden-features.md":"45e791e6","ru_ru_guide_hidden-features.md":"395803e0","guide_how-to-integrate-for-non-gki.md":"35d629c6","guide_rescue-from-bootloop.md":"86b40f3b","guide_unofficially-support-devices.md":"d9173329","ja_jp_guide_installation.md":"2cc6f4de","ja_jp_guide_hidden-features.md":"9c5dddb2","ja_jp_guide_module.md":"8abad8eb","zh_cn_guide_how-to-integrate-for-non-gki.md":"891eeb81","zh_cn_guide_unofficially-support-devices.md":"e2fd6c11","zh_tw_guide_unofficially-support-devices.md":"e385e7b5","ru_ru_guide_faq.md":"e0aa7004","zh_tw_guide_what-is-kernelsu.md":"a2ba0439","id_id_guide_difference-with-magisk.md":"abc25e03","ja_jp_guide_rescue-from-bootloop.md":"d26f95c4","zh_cn_guide_app-profile.md":"557e9b58","guide_app-profile.md":"9faae288","guide_module.md":"1b64f354","pt_br_guide_unofficially-support-devices.md":"007edca2","zh_tw_index.md":"896578f0","zh_tw_guide_hidden-features.md":"f0bea9ae","pt_br_guide_module.md":"039201f7","zh_tw_guide_how-to-integrate-for-non-gki.md":"e3487c4d","ru_ru_index.md":"4f8b1975","zh_cn_guide_faq.md":"30eb8f8e","vi_vn_guide_unofficially-support-devices.md":"b3af952a","zh_tw_guide_installation.md":"0f23d3bf","pt_br_guide_faq.md":"e1ef8622","vi_vn_guide_installation.md":"7fef5e5e","pt_br_index.md":"a1524471","zh_tw_guide_faq.md":"bea0a7af","ru_ru_guide_how-to-integrate-for-non-gki.md":"7c70c478","ja_jp_guide_difference-with-magisk.md":"34107b11","zh_cn_guide_how-to-build.md":"f4f2a6dd","vi_vn_guide_how-to-integrate-for-non-gki.md":"9b3f1403","ru_ru_guide_module.md":"2cf2aec7","ru_ru_guide_what-is-kernelsu.md":"b17b08a8"} +{"ja_jp_guide_difference-with-magisk.md":"665d74c8","ja_jp_guide_faq.md":"b51546fa","ja_jp_guide_hidden-features.md":"cdaa1636","ja_jp_guide_installation.md":"7f4d5495","id_id_guide_rescue-from-bootloop.md":"5cce45dd","guide_app-profile.md":"27f6816c","ja_jp_index.md":"474a6e1c","guide_hidden-features.md":"469c34a7","guide_installation.md":"f81e59f3","guide_how-to-integrate-for-non-gki.md":"5ad8c015","guide_rescue-from-bootloop.md":"656e5988","guide_unofficially-support-devices.md":"692e1ce3","id_id_guide_difference-with-magisk.md":"24526677","id_id_guide_how-to-build.md":"f09b7a1a","id_id_guide_module.md":"068bf86d","id_id_guide_installation.md":"2d1e9f50","ja_jp_guide_module.md":"dbefd5cf","id_id_guide_unofficially-support-devices.md":"78d9391e","id_id_guide_what-is-kernelsu.md":"b6c7dc2f","guide_how-to-build.md":"83e45bdd","id_id_index.md":"06fbc917","index.md":"133d8307","pt_br_guide_rescue-from-bootloop.md":"ee15e0d3","ru_ru_guide_app-profile.md":"084a5687","ru_ru_guide_faq.md":"1d543e4d","id_id_guide_faq.md":"a19560c6","guide_module.md":"fcc5d1bb","pt_br_guide_app-profile.md":"dd333014","guide_what-is-kernelsu.md":"f089de90","zh_tw_index.md":"419b5e06","pt_br_guide_module.md":"dc3df57b","zh_cn_guide_how-to-integrate-for-non-gki.md":"fc1a10e8","ru_ru_guide_hidden-features.md":"6c681acf","ru_ru_guide_how-to-build.md":"3ac4fa09","ja_jp_guide_rescue-from-bootloop.md":"7bb5da8e","ja_jp_guide_unofficially-support-devices.md":"e12c36fa","ja_jp_guide_what-is-kernelsu.md":"10b56327","pt_br_guide_faq.md":"01a58b64","pt_br_guide_how-to-build.md":"df81a6ce","guide_difference-with-magisk.md":"6bc0c587","ru_ru_guide_rescue-from-bootloop.md":"c45eb81e","ru_ru_guide_unofficially-support-devices.md":"903ca1e7","ru_ru_guide_what-is-kernelsu.md":"11586959","ru_ru_index.md":"4a8cc387","vi_vn_guide_app-profile.md":"0b09a7b5","vi_vn_guide_difference-with-magisk.md":"57b7c7d3","vi_vn_guide_faq.md":"510e2971","vi_vn_guide_hidden-features.md":"f00f568c","vi_vn_guide_how-to-build.md":"f65da7b9","vi_vn_guide_how-to-integrate-for-non-gki.md":"f0064cd4","vi_vn_guide_installation.md":"a6c03995","vi_vn_guide_module.md":"d89b5b5b","vi_vn_guide_rescue-from-bootloop.md":"dc2854ff","vi_vn_guide_unofficially-support-devices.md":"2078077c","vi_vn_guide_what-is-kernelsu.md":"48965b2f","vi_vn_index.md":"f75cd4bf","zh_cn_guide_app-profile.md":"cabe50a9","zh_cn_guide_difference-with-magisk.md":"d3fa9085","zh_cn_guide_faq.md":"ac23a8b3","zh_cn_guide_hidden-features.md":"34a0e2c0","pt_br_guide_unofficially-support-devices.md":"9d8e434d","guide_faq.md":"89521262","zh_cn_guide_installation.md":"b6f3f2dc","zh_cn_guide_module.md":"95d10721","zh_cn_guide_rescue-from-bootloop.md":"fa76b90c","zh_cn_guide_how-to-build.md":"709f65fb","zh_cn_guide_unofficially-support-devices.md":"4c68d324","zh_cn_guide_what-is-kernelsu.md":"296b4e28","zh_cn_index.md":"c6e87037","zh_tw_guide_difference-with-magisk.md":"831b592b","zh_tw_guide_faq.md":"e91224a7","ru_ru_guide_installation.md":"ad88b078","zh_tw_guide_hidden-features.md":"728dbc1c","zh_tw_guide_how-to-build.md":"32de6944","zh_tw_guide_how-to-integrate-for-non-gki.md":"035c5fb8","ru_ru_guide_module.md":"19fabb40","zh_tw_guide_installation.md":"48e9e870","zh_tw_guide_module.md":"57647aec","zh_tw_guide_rescue-from-bootloop.md":"499539d8","zh_tw_guide_unofficially-support-devices.md":"e1ab05b4","zh_tw_guide_what-is-kernelsu.md":"c73a8b16","ru_ru_guide_how-to-integrate-for-non-gki.md":"12445e00","pt_br_guide_hidden-features.md":"817db7e8","pt_br_guide_difference-with-magisk.md":"c0878d2e","pt_br_guide_what-is-kernelsu.md":"e7123691","pt_br_index.md":"14860a0b","id_id_guide_how-to-integrate-for-non-gki.md":"600d4255","ru_ru_guide_difference-with-magisk.md":"928ea8b2","pt_br_guide_installation.md":"163448ef","pt_br_guide_how-to-integrate-for-non-gki.md":"6bd8e7c3"} diff --git a/id_ID/guide/difference-with-magisk.html b/id_ID/guide/difference-with-magisk.html index a1dfdf929768..7ddaf2d54ecb 100644 --- a/id_ID/guide/difference-with-magisk.html +++ b/id_ID/guide/difference-with-magisk.html @@ -5,18 +5,19 @@ Perbedaan dengan Magisk | KernelSU - - + + + - - - - + + + + + -
Skip to content
On this page

Perbedaan dengan Magisk

Meskipun ada banyak kesamaan antara modul KernelSU dan modul Magisk, pasti ada beberapa perbedaan karena mekanisme implementasinya yang sangat berbeda. Jika Anda ingin modul Anda berjalan di Magisk dan KernelSU, Anda harus memahami perbedaan ini.

Kesamaan

  • Format file modul: keduanya menggunakan format zip untuk mengatur modul, dan format modulnya hampir sama
  • Direktori pemasangan modul: keduanya terletak di /data/adb/modules
  • Tanpa sistem: keduanya mendukung modifikasi / sistem dengan cara tanpa sistem melalui modul
  • post-fs-data.sh: waktu eksekusi dan semantiknya persis sama
  • service.sh: waktu eksekusi dan semantiknya persis sama
  • system.prop: sepenuhnya sama
  • sepolicy.rule: sama persis
  • BusyBox: skrip dijalankan di BusyBox dengan "mode mandiri" diaktifkan di kedua kasus

Perbedaan

Sebelum memahami perbedaannya, Anda perlu mengetahui cara membedakan apakah modul Anda berjalan di KernelSU atau Magisk. Anda dapat menggunakan variabel lingkungan KSU untuk membedakannya di semua tempat di mana Anda dapat menjalankan skrip modul (customize.sh, post-fs-data.sh, service.sh). Di KernelSU, variabel lingkungan ini akan disetel ke true.

Berikut beberapa perbedaannya:

  • Modul KernelSU tidak dapat diinstal dalam mode Pemulihan.
  • Modul KernelSU tidak memiliki dukungan bawaan untuk Zygisk (tetapi Anda dapat menggunakan modul Zygisk melalui ZygiskNext.
  • Metode untuk mengganti atau menghapus file dalam modul KernelSU sama sekali berbeda dari Magisk. KernelSU tidak mendukung metode .replace. Sebagai gantinya, Anda perlu membuat file dengan nama yang sama dengan mknod filename c 0 0 untuk menghapus file terkait.
  • Direktori untuk BusyBox berbeda. BusyBox bawaan di KernelSU terletak di /data/adb/ksu/bin/busybox, sedangkan di Magisk terletak di /data/adb/magisk/busybox. Perhatikan bahwa ini adalah perilaku internal KernelSU dan dapat berubah di masa mendatang!
  • KernelSU tidak mendukung file .replace; namun, KernelSU mendukung variabel REMOVE dan REPLACE untuk menghapus atau mengganti file dan folder.

Rilis Dibawah Lisensi GPL3.

- +
Skip to content

Perbedaan dengan Magisk

Meskipun ada banyak kesamaan antara modul KernelSU dan modul Magisk, pasti ada beberapa perbedaan karena mekanisme implementasinya yang sangat berbeda. Jika Anda ingin modul Anda berjalan di Magisk dan KernelSU, Anda harus memahami perbedaan ini.

Kesamaan

  • Format file modul: keduanya menggunakan format zip untuk mengatur modul, dan format modulnya hampir sama
  • Direktori pemasangan modul: keduanya terletak di /data/adb/modules
  • Tanpa sistem: keduanya mendukung modifikasi / sistem dengan cara tanpa sistem melalui modul
  • post-fs-data.sh: waktu eksekusi dan semantiknya persis sama
  • service.sh: waktu eksekusi dan semantiknya persis sama
  • system.prop: sepenuhnya sama
  • sepolicy.rule: sama persis
  • BusyBox: skrip dijalankan di BusyBox dengan "mode mandiri" diaktifkan di kedua kasus

Perbedaan

Sebelum memahami perbedaannya, Anda perlu mengetahui cara membedakan apakah modul Anda berjalan di KernelSU atau Magisk. Anda dapat menggunakan variabel lingkungan KSU untuk membedakannya di semua tempat di mana Anda dapat menjalankan skrip modul (customize.sh, post-fs-data.sh, service.sh). Di KernelSU, variabel lingkungan ini akan disetel ke true.

Berikut beberapa perbedaannya:

  • Modul KernelSU tidak dapat diinstal dalam mode Pemulihan.
  • Modul KernelSU tidak memiliki dukungan bawaan untuk Zygisk (tetapi Anda dapat menggunakan modul Zygisk melalui ZygiskNext.
  • Metode untuk mengganti atau menghapus file dalam modul KernelSU sama sekali berbeda dari Magisk. KernelSU tidak mendukung metode .replace. Sebagai gantinya, Anda perlu membuat file dengan nama yang sama dengan mknod filename c 0 0 untuk menghapus file terkait.
  • Direktori untuk BusyBox berbeda. BusyBox bawaan di KernelSU terletak di /data/adb/ksu/bin/busybox, sedangkan di Magisk terletak di /data/adb/magisk/busybox. Perhatikan bahwa ini adalah perilaku internal KernelSU dan dapat berubah di masa mendatang!
  • KernelSU tidak mendukung file .replace; namun, KernelSU mendukung variabel REMOVE dan REPLACE untuk menghapus atau mengganti file dan folder.

Rilis Dibawah Lisensi GPL3.

+ \ No newline at end of file diff --git a/id_ID/guide/faq.html b/id_ID/guide/faq.html index 055f3fef1f8c..281836419f4c 100644 --- a/id_ID/guide/faq.html +++ b/id_ID/guide/faq.html @@ -5,18 +5,19 @@ FAQ | KernelSU - - + + + - - - - + + + + + -
Skip to content
On this page

FAQ

Apakah KernelSU mendukung perangkat saya?

Pertama, perangkatmu harus bisa dibuka bootloadernya. Jika tiddak bisa, berarti tidak memungkinkan untuk bekerja.

Lalu instal aplikasi KernelSU manager di dalam perangkatmu dan buka, jika terlihat Unsupported berarti perangkatmu tidak didukung dan tidak akan didukung di kemudian hari.

Apakah KernelSU membutuhkan buka-bootloader?

Ya, seharusnya.

Apakah KernelSU mendukung modul?

Ya, Tetapi masih dalam versi awal, bisa jadi ngebug. Mohon tunggu sampai semuanya stabil 😃

Apakah KernelSU mendukung Xposed?

Ya, Dreamland dan TaiChi sekarang bekerja sebagian, Dan kita sedang mencoba untuk membuat Xposed Framework lainnya bekerja.

Apakah KernelSU kompatibel dengan Magisk?

Sistem modul KernelSU bertentangan dengan magic mount Magisk, jika ada modul yang diaktifkan di KernelSU, maka seluruh Magisk tidak akan bekerja.

Tetapi jika Anda hanya menggunakan su dari KernelSU, maka KernelSU akan bekerja dengan baik dengan Magisk: KernelSU memodifikasi kernel dan Magisk memodifikasi ramdisk, keduanya dapat bekerja bersama.

Akankah KernelSU menggantikan Magisk?

Kami rasa tidak dan itu bukan tujuan kami. Magisk sudah cukup baik untuk solusi root userspace dan akan bertahan lama. Tujuan KernelSU adalah untuk menyediakan antarmuka kernel kepada pengguna, bukan untuk menggantikan Magisk.

Dapatkah KernelSU mendukung perangkat non GKI?

Hal ini dimungkinkan. Tetapi Anda harus mengunduh sumber kernel dan mengintegrasikan KernelSU ke source tree dan mengkompilasi kernel sendiri.

Dapatkah KernelSU mendukung perangkat di bawah Android 12?

Kernel perangkatlah yang mempengaruhi kompatibilitas KernelSU dan tidak ada hubungannya dengan versi Android, satu-satunya batasan adalah bahwa perangkat yang diluncurkan dengan Android 12 harus menggunakan kernel 5.10+ (perangkat GKI). Jadi:

  1. Perangkat yang diluncurkan dengan Android 12 harus didukung.
  2. Perangkat dengan kernel lama (Beberapa perangkat Android 12 juga memiliki kernel lama) dapat dikompilasi (Anda harus membuat kernel sendiri)

Dapatkah KernelSU mendukung kernel lama?

Ada kemungkinan, KernelSU sudah di-backport ke kernel 4.14 sekarang, untuk kernel yang lebih lama, Anda harus melakukan backport secara manual dan menyambut baik PR darimu!

Cara mengintegrasikan KernelSU untuk kernel lama?

Silakan merujuk ke guide

Rilis Dibawah Lisensi GPL3.

- +
Skip to content

FAQ

Apakah KernelSU mendukung perangkat saya?

Pertama, perangkatmu harus bisa dibuka bootloadernya. Jika tiddak bisa, berarti tidak memungkinkan untuk bekerja.

Lalu instal aplikasi KernelSU manager di dalam perangkatmu dan buka, jika terlihat Unsupported berarti perangkatmu tidak didukung dan tidak akan didukung di kemudian hari.

Apakah KernelSU membutuhkan buka-bootloader?

Ya, seharusnya.

Apakah KernelSU mendukung modul?

Ya, Tetapi masih dalam versi awal, bisa jadi ngebug. Mohon tunggu sampai semuanya stabil 😃

Apakah KernelSU mendukung Xposed?

Ya, Dreamland dan TaiChi sekarang bekerja sebagian, Dan kita sedang mencoba untuk membuat Xposed Framework lainnya bekerja.

Apakah KernelSU kompatibel dengan Magisk?

Sistem modul KernelSU bertentangan dengan magic mount Magisk, jika ada modul yang diaktifkan di KernelSU, maka seluruh Magisk tidak akan bekerja.

Tetapi jika Anda hanya menggunakan su dari KernelSU, maka KernelSU akan bekerja dengan baik dengan Magisk: KernelSU memodifikasi kernel dan Magisk memodifikasi ramdisk, keduanya dapat bekerja bersama.

Akankah KernelSU menggantikan Magisk?

Kami rasa tidak dan itu bukan tujuan kami. Magisk sudah cukup baik untuk solusi root userspace dan akan bertahan lama. Tujuan KernelSU adalah untuk menyediakan antarmuka kernel kepada pengguna, bukan untuk menggantikan Magisk.

Dapatkah KernelSU mendukung perangkat non GKI?

Hal ini dimungkinkan. Tetapi Anda harus mengunduh sumber kernel dan mengintegrasikan KernelSU ke source tree dan mengkompilasi kernel sendiri.

Dapatkah KernelSU mendukung perangkat di bawah Android 12?

Kernel perangkatlah yang mempengaruhi kompatibilitas KernelSU dan tidak ada hubungannya dengan versi Android, satu-satunya batasan adalah bahwa perangkat yang diluncurkan dengan Android 12 harus menggunakan kernel 5.10+ (perangkat GKI). Jadi:

  1. Perangkat yang diluncurkan dengan Android 12 harus didukung.
  2. Perangkat dengan kernel lama (Beberapa perangkat Android 12 juga memiliki kernel lama) dapat dikompilasi (Anda harus membuat kernel sendiri)

Dapatkah KernelSU mendukung kernel lama?

Ada kemungkinan, KernelSU sudah di-backport ke kernel 4.14 sekarang, untuk kernel yang lebih lama, Anda harus melakukan backport secara manual dan menyambut baik PR darimu!

Cara mengintegrasikan KernelSU untuk kernel lama?

Silakan merujuk ke guide

Rilis Dibawah Lisensi GPL3.

+ \ No newline at end of file diff --git a/id_ID/guide/how-to-build.html b/id_ID/guide/how-to-build.html index 07d18d98a077..855efdfb8699 100644 --- a/id_ID/guide/how-to-build.html +++ b/id_ID/guide/how-to-build.html @@ -5,21 +5,25 @@ Bagaimana caranya untuk build KernelSU? | KernelSU - - + + + - - - - + + + + + -
Skip to content
On this page

Bagaimana caranya untuk build KernelSU?

Pertama, Anda harus membaca dokumen resmi Android untuk membangun kernel:

  1. Building Kernels
  2. GKI Release Builds

Halaman ini untuk perangkat GKI, jika Anda menggunakan kernel lama, silakan lihat cara mengintegrasikan KernelSU untuk kernel lama

Build Kernel

Menyinkronkan source code kernel

sh
repo init -u https://android.googlesource.com/kernel/manifest
-mv <kernel_manifest.xml> .repo/manifests
-repo init -m manifest.xml
-repo sync

<kernel_manifest.xml> adalah berkas manifes yang dapat menentukan build secara unik, Anda dapat menggunakan manifes tersebut untuk melakukan build yang dapat diprediksikan ulang. Anda harus mengunduh berkas manifes dari Google GKI release builds

Build

Silakan periksa official docs terlebih dahulu.

Sebagai contoh, kita perlu build image kernel aarch64:

sh
LTO=thin BUILD_CONFIG=common/build.config.gki.aarch64 build/build.sh

Jangan lupa untuk menambahkan flag LTO=thin, jika tidak, maka build akan gagal jika memori komputer Anda kurang dari 24GB.

Mulai dari Android 13, kernel dibuild oleh bazel:

sh
tools/bazel build --config=fast //common:kernel_aarch64_dist

Build Kernel dengan KernelSU

Jika Anda dapat build kernel dengan sukses, maka build KernelSU sangatlah mudah, jalankan perintah ini di root dir kernel source:

  • Latest tag(stable)
sh
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -
  • main branch(dev)
sh
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -s main
  • Select tag(Such as v0.5.2)
sh
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -s v0.5.2

Dan kemudian build ulang kernel dan Anda akan mendapatkan image kernel dengan KernelSU!

Rilis Dibawah Lisensi GPL3.

- +
Skip to content

Bagaimana caranya untuk build KernelSU?

Pertama, Anda harus membaca dokumen resmi Android untuk membangun kernel:

  1. Building Kernels
  2. GKI Release Builds

Halaman ini untuk perangkat GKI, jika Anda menggunakan kernel lama, silakan lihat cara mengintegrasikan KernelSU untuk kernel lama

Build Kernel

Menyinkronkan source code kernel

sh
repo init -u https://android.googlesource.com/kernel/manifest
+mv <kernel_manifest.xml> .repo/manifests
+repo init -m manifest.xml
+repo sync
repo init -u https://android.googlesource.com/kernel/manifest
+mv <kernel_manifest.xml> .repo/manifests
+repo init -m manifest.xml
+repo sync

<kernel_manifest.xml> adalah berkas manifes yang dapat menentukan build secara unik, Anda dapat menggunakan manifes tersebut untuk melakukan build yang dapat diprediksikan ulang. Anda harus mengunduh berkas manifes dari Google GKI release builds

Build

Silakan periksa official docs terlebih dahulu.

Sebagai contoh, kita perlu build image kernel aarch64:

sh
LTO=thin BUILD_CONFIG=common/build.config.gki.aarch64 build/build.sh
LTO=thin BUILD_CONFIG=common/build.config.gki.aarch64 build/build.sh

Jangan lupa untuk menambahkan flag LTO=thin, jika tidak, maka build akan gagal jika memori komputer Anda kurang dari 24GB.

Mulai dari Android 13, kernel dibuild oleh bazel:

sh
tools/bazel build --config=fast //common:kernel_aarch64_dist
tools/bazel build --config=fast //common:kernel_aarch64_dist

Build Kernel dengan KernelSU

Jika Anda dapat build kernel dengan sukses, maka build KernelSU sangatlah mudah, jalankan perintah ini di root dir kernel source:

  • Latest tag(stable)
sh
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -
  • main branch(dev)
sh
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -s main
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -s main
  • Select tag(Such as v0.5.2)
sh
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -s v0.5.2
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -s v0.5.2

Dan kemudian build ulang kernel dan Anda akan mendapatkan image kernel dengan KernelSU!

Rilis Dibawah Lisensi GPL3.

+ \ No newline at end of file diff --git a/id_ID/guide/how-to-integrate-for-non-gki.html b/id_ID/guide/how-to-integrate-for-non-gki.html index d39a218bd6f4..4251fc7f1c9b 100644 --- a/id_ID/guide/how-to-integrate-for-non-gki.html +++ b/id_ID/guide/how-to-integrate-for-non-gki.html @@ -5,169 +5,321 @@ Bagaimana Caranya untuk mengintegrasikan KernelSU ke kernel non GKI? | KernelSU - - + + + - - - - + + + + + -
Skip to content
On this page

Bagaimana Caranya untuk mengintegrasikan KernelSU ke kernel non GKI?

KernelSU dapat diintegrasikan ke kernel non GKI, dan saat ini sudah di-backport ke 4.14, dan juga dapat dijalankan pada kernel di bawah 4.14.

Karena fragmentasi kernel non GKI, kami tidak memiliki cara yang seragam untuk membangunnya, sehingga kami tidak dapat menyediakan gambar boot non GKI. Tetapi Anda dapat membangun kernel sendiri dengan KernelSU yang terintegrasi.

Pertama, Anda harus dapat membangun kernel yang dapat di-boot dari kode sumber kernel, jika kernel tersebut tidak open source, maka akan sulit untuk menjalankan KernelSU untuk perangkat Anda.

Jika Anda dapat membuat kernel yang dapat di-booting, ada dua cara untuk mengintegrasikan KernelSU ke kode sumber kernel:

  1. Secara otomatis dengan kprobe
  2. Secara manual

Integrasikan dengan kprobe

KernelSU menggunakan kprobe untuk melakukan hook kernel, jika kprobe berjalan dengan baik pada kernel Anda, maka disarankan untuk menggunakan cara ini.

Pertama, tambahkan KernelSU ke dalam berkas kernel source tree:

  • Latest tag(stable)
sh
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -
  • main branch(dev)
sh
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -s main
  • Select tag(Such as v0.5.2)
sh
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -s v0.5.2

Kemudian, Anda harus memeriksa apakah kprobe diaktifkan dalam konfigurasi kernel Anda, jika tidak, tambahkan konfigurasi ini ke dalamnya:

CONFIG_KPROBES=y
-CONFIG_HAVE_KPROBES=y
-CONFIG_KPROBE_EVENTS=y

Dan build kernel Anda lagi, KernelSU seharusnya bekerja dengan baik.

Jika Anda menemukan bahwa KPROBES masih belum diaktifkan, Anda dapat mencoba mengaktifkan CONFIG_MODULES. (Jika masih belum berlaku, gunakan make menuconfig untuk mencari ketergantungan KPROBES yang lain)

etapi jika Anda mengalami boot loop saat mengintegrasikan KernelSU, itu mungkin kprobe rusak di kernel Anda, Anda harus memperbaiki bug kprobe atau menggunakan cara kedua.

Memodifikasi sumber kernel secara manual

Jika kprobe tidak dapat bekerja pada kernel Anda (mungkin karena bug di upstream atau kernel di bawah 4.8), maka Anda dapat mencoba cara ini:

Pertama, tambahkan KernelSU ke dalam direktori kernel source tree:

sh
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -

Kemudian, tambahkan panggilan KernelSU ke source kernel, berikut ini adalah patch yang dapat dirujuk:

diff
diff --git a/fs/exec.c b/fs/exec.c
-index ac59664eaecf..bdd585e1d2cc 100644
---- a/fs/exec.c
-+++ b/fs/exec.c
-@@ -1890,11 +1890,14 @@ static int __do_execve_file(int fd, struct filename *filename,
- 	return retval;
- }
- 
-+extern bool ksu_execveat_hook __read_mostly;
-+extern int ksu_handle_execveat(int *fd, struct filename **filename_ptr, void *argv,
-+			void *envp, int *flags);
-+extern int ksu_handle_execveat_sucompat(int *fd, struct filename **filename_ptr,
-+				 void *argv, void *envp, int *flags);
- static int do_execveat_common(int fd, struct filename *filename,
- 			      struct user_arg_ptr argv,
- 			      struct user_arg_ptr envp,
- 			      int flags)
- {
-+	if (unlikely(ksu_execveat_hook))
-+		ksu_handle_execveat(&fd, &filename, &argv, &envp, &flags);
-+	else
-+		ksu_handle_execveat_sucompat(&fd, &filename, &argv, &envp, &flags);
- 	return __do_execve_file(fd, filename, argv, envp, flags, NULL);
- }
- 
-diff --git a/fs/open.c b/fs/open.c
-index 05036d819197..965b84d486b8 100644
---- a/fs/open.c
-+++ b/fs/open.c
-@@ -348,6 +348,8 @@ SYSCALL_DEFINE4(fallocate, int, fd, int, mode, loff_t, offset, loff_t, len)
- 	return ksys_fallocate(fd, mode, offset, len);
- }
- 
-+extern int ksu_handle_faccessat(int *dfd, const char __user **filename_user, int *mode,
-+			 int *flags);
- /*
-  * access() needs to use the real uid/gid, not the effective uid/gid.
-  * We do this by temporarily clearing all FS-related capabilities and
-@@ -355,6 +357,7 @@ SYSCALL_DEFINE4(fallocate, int, fd, int, mode, loff_t, offset, loff_t, len)
-  */
- long do_faccessat(int dfd, const char __user *filename, int mode)
- {
-+	ksu_handle_faccessat(&dfd, &filename, &mode, NULL);
- 	const struct cred *old_cred;
- 	struct cred *override_cred;
- 	struct path path;
-diff --git a/fs/read_write.c b/fs/read_write.c
-index 650fc7e0f3a6..55be193913b6 100644
---- a/fs/read_write.c
-+++ b/fs/read_write.c
-@@ -434,10 +434,14 @@ ssize_t kernel_read(struct file *file, void *buf, size_t count, loff_t *pos)
- }
- EXPORT_SYMBOL(kernel_read);
- 
-+extern bool ksu_vfs_read_hook __read_mostly;
-+extern int ksu_handle_vfs_read(struct file **file_ptr, char __user **buf_ptr,
-+			size_t *count_ptr, loff_t **pos);
- ssize_t vfs_read(struct file *file, char __user *buf, size_t count, loff_t *pos)
- {
- 	ssize_t ret;
- 
-+	if (unlikely(ksu_vfs_read_hook))
-+		ksu_handle_vfs_read(&file, &buf, &count, &pos);
-+
- 	if (!(file->f_mode & FMODE_READ))
- 		return -EBADF;
- 	if (!(file->f_mode & FMODE_CAN_READ))
-diff --git a/fs/stat.c b/fs/stat.c
-index 376543199b5a..82adcef03ecc 100644
---- a/fs/stat.c
-+++ b/fs/stat.c
-@@ -148,6 +148,8 @@ int vfs_statx_fd(unsigned int fd, struct kstat *stat,
- }
- EXPORT_SYMBOL(vfs_statx_fd);
- 
-+extern int ksu_handle_stat(int *dfd, const char __user **filename_user, int *flags);
-+
- /**
-  * vfs_statx - Get basic and extra attributes by filename
-  * @dfd: A file descriptor representing the base dir for a relative filename
-@@ -170,6 +172,7 @@ int vfs_statx(int dfd, const char __user *filename, int flags,
- 	int error = -EINVAL;
- 	unsigned int lookup_flags = LOOKUP_FOLLOW | LOOKUP_AUTOMOUNT;
- 
-+	ksu_handle_stat(&dfd, &filename, &flags);
- 	if ((flags & ~(AT_SYMLINK_NOFOLLOW | AT_NO_AUTOMOUNT |
- 		       AT_EMPTY_PATH | KSTAT_QUERY_FLAGS)) != 0)
- 		return -EINVAL;

Anda harus menemukan empat fungsi dalam kernel source:

  1. do_faccessat, usually in fs/open.c
  2. do_execveat_common, usually in fs/exec.c
  3. vfs_read, usually in fs/read_write.c
  4. vfs_statx, usually in fs/stat.c

Jika kernel anda tidak memiliki vfs_statx, maka gunakan vfs_fstatat alih-alih:

diff
diff --git a/fs/stat.c b/fs/stat.c
-index 068fdbcc9e26..5348b7bb9db2 100644
---- a/fs/stat.c
-+++ b/fs/stat.c
-@@ -87,6 +87,8 @@ int vfs_fstat(unsigned int fd, struct kstat *stat)
- }
- EXPORT_SYMBOL(vfs_fstat);
- 
-+extern int ksu_handle_stat(int *dfd, const char __user **filename_user, int *flags);
-+
- int vfs_fstatat(int dfd, const char __user *filename, struct kstat *stat,
- 		int flag)
- {
-@@ -94,6 +96,8 @@ int vfs_fstatat(int dfd, const char __user *filename, struct kstat *stat,
- 	int error = -EINVAL;
- 	unsigned int lookup_flags = 0;
- 
-+	ksu_handle_stat(&dfd, &filename, &flag);
-+
- 	if ((flag & ~(AT_SYMLINK_NOFOLLOW | AT_NO_AUTOMOUNT |
- 		      AT_EMPTY_PATH)) != 0)
- 		goto out;

Untuk kernel lebih awal dari 4.17, jika anda menemukan do_faccessat, hanya pergi ke definisi yang sama faccessat syscall dan tempatkan pemanggil di sini:

diff
diff --git a/fs/open.c b/fs/open.c
-index 2ff887661237..e758d7db7663 100644
---- a/fs/open.c
-+++ b/fs/open.c
-@@ -355,6 +355,9 @@ SYSCALL_DEFINE4(fallocate, int, fd, int, mode, loff_t, offset, loff_t, len)
- 	return error;
- }
- 
-+extern int ksu_handle_faccessat(int *dfd, const char __user **filename_user, int *mode,
-+			        int *flags);
-+
- /*
-  * access() needs to use the real uid/gid, not the effective uid/gid.
-  * We do this by temporarily clearing all FS-related capabilities and
-@@ -370,6 +373,8 @@ SYSCALL_DEFINE3(faccessat, int, dfd, const char __user *, filename, int, mode)
- 	int res;
- 	unsigned int lookup_flags = LOOKUP_FOLLOW;
- 
-+	ksu_handle_faccessat(&dfd, &filename, &mode, NULL);
-+
- 	if (mode & ~S_IRWXO)	/* where's F_OK, X_OK, W_OK, R_OK? */
- 		return -EINVAL;

To enable KernelSU's builtin SafeMode, You should also modify input_handle_event in drivers/input/input.c:

TIP

Fitur ini sangat direkomendasikan, serta sangat membantu untuk memulihkan pada saat bootloop!

diff
diff --git a/drivers/input/input.c b/drivers/input/input.c
-index 45306f9ef247..815091ebfca4 100755
---- a/drivers/input/input.c
-+++ b/drivers/input/input.c
-@@ -367,10 +367,13 @@ static int input_get_disposition(struct input_dev *dev,
- 	return disposition;
- }
- 
-+extern bool ksu_input_hook __read_mostly;
-+extern int ksu_handle_input_handle_event(unsigned int *type, unsigned int *code, int *value);
-+
- static void input_handle_event(struct input_dev *dev,
- 			       unsigned int type, unsigned int code, int value)
- {
-	int disposition = input_get_disposition(dev, type, code, &value);
-+
-+	if (unlikely(ksu_input_hook))
-+		ksu_handle_input_handle_event(&type, &code, &value);
- 
- 	if (disposition != INPUT_IGNORE_EVENT && type != EV_SYN)
- 		add_input_randomness(type, code, value);

Terakhir, edit KernelSU/kernel/ksu.c dan beri komentar pada enable_sucompat() lalu build kernel Anda lagi, KernelSU akan bekerja dengan baik.

Rilis Dibawah Lisensi GPL3.

- +
Skip to content

Bagaimana Caranya untuk mengintegrasikan KernelSU ke kernel non GKI?

KernelSU dapat diintegrasikan ke kernel non GKI, dan saat ini sudah di-backport ke 4.14, dan juga dapat dijalankan pada kernel di bawah 4.14.

Karena fragmentasi kernel non GKI, kami tidak memiliki cara yang seragam untuk membangunnya, sehingga kami tidak dapat menyediakan gambar boot non GKI. Tetapi Anda dapat membangun kernel sendiri dengan KernelSU yang terintegrasi.

Pertama, Anda harus dapat membangun kernel yang dapat di-boot dari kode sumber kernel, jika kernel tersebut tidak open source, maka akan sulit untuk menjalankan KernelSU untuk perangkat Anda.

Jika Anda dapat membuat kernel yang dapat di-booting, ada dua cara untuk mengintegrasikan KernelSU ke kode sumber kernel:

  1. Secara otomatis dengan kprobe
  2. Secara manual

Integrasikan dengan kprobe

KernelSU menggunakan kprobe untuk melakukan hook kernel, jika kprobe berjalan dengan baik pada kernel Anda, maka disarankan untuk menggunakan cara ini.

Pertama, tambahkan KernelSU ke dalam berkas kernel source tree:

  • Latest tag(stable)
sh
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -
  • main branch(dev)
sh
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -s main
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -s main
  • Select tag(Such as v0.5.2)
sh
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -s v0.5.2
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -s v0.5.2

Kemudian, Anda harus memeriksa apakah kprobe diaktifkan dalam konfigurasi kernel Anda, jika tidak, tambahkan konfigurasi ini ke dalamnya:

CONFIG_KPROBES=y
+CONFIG_HAVE_KPROBES=y
+CONFIG_KPROBE_EVENTS=y
CONFIG_KPROBES=y
+CONFIG_HAVE_KPROBES=y
+CONFIG_KPROBE_EVENTS=y

Dan build kernel Anda lagi, KernelSU seharusnya bekerja dengan baik.

Jika Anda menemukan bahwa KPROBES masih belum diaktifkan, Anda dapat mencoba mengaktifkan CONFIG_MODULES. (Jika masih belum berlaku, gunakan make menuconfig untuk mencari ketergantungan KPROBES yang lain)

etapi jika Anda mengalami boot loop saat mengintegrasikan KernelSU, itu mungkin kprobe rusak di kernel Anda, Anda harus memperbaiki bug kprobe atau menggunakan cara kedua.

Memodifikasi sumber kernel secara manual

Jika kprobe tidak dapat bekerja pada kernel Anda (mungkin karena bug di upstream atau kernel di bawah 4.8), maka Anda dapat mencoba cara ini:

Pertama, tambahkan KernelSU ke dalam direktori kernel source tree:

sh
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -

Kemudian, tambahkan panggilan KernelSU ke source kernel, berikut ini adalah patch yang dapat dirujuk:

diff
diff --git a/fs/exec.c b/fs/exec.c
+index ac59664eaecf..bdd585e1d2cc 100644
+--- a/fs/exec.c
++++ b/fs/exec.c
+@@ -1890,11 +1890,14 @@ static int __do_execve_file(int fd, struct filename *filename,
+ 	return retval;
+ }
+ 
++extern bool ksu_execveat_hook __read_mostly;
++extern int ksu_handle_execveat(int *fd, struct filename **filename_ptr, void *argv,
++			void *envp, int *flags);
++extern int ksu_handle_execveat_sucompat(int *fd, struct filename **filename_ptr,
++				 void *argv, void *envp, int *flags);
+ static int do_execveat_common(int fd, struct filename *filename,
+ 			      struct user_arg_ptr argv,
+ 			      struct user_arg_ptr envp,
+ 			      int flags)
+ {
++	if (unlikely(ksu_execveat_hook))
++		ksu_handle_execveat(&fd, &filename, &argv, &envp, &flags);
++	else
++		ksu_handle_execveat_sucompat(&fd, &filename, &argv, &envp, &flags);
+ 	return __do_execve_file(fd, filename, argv, envp, flags, NULL);
+ }
+ 
+diff --git a/fs/open.c b/fs/open.c
+index 05036d819197..965b84d486b8 100644
+--- a/fs/open.c
++++ b/fs/open.c
+@@ -348,6 +348,8 @@ SYSCALL_DEFINE4(fallocate, int, fd, int, mode, loff_t, offset, loff_t, len)
+ 	return ksys_fallocate(fd, mode, offset, len);
+ }
+ 
++extern int ksu_handle_faccessat(int *dfd, const char __user **filename_user, int *mode,
++			 int *flags);
+ /*
+  * access() needs to use the real uid/gid, not the effective uid/gid.
+  * We do this by temporarily clearing all FS-related capabilities and
+@@ -355,6 +357,7 @@ SYSCALL_DEFINE4(fallocate, int, fd, int, mode, loff_t, offset, loff_t, len)
+  */
+ long do_faccessat(int dfd, const char __user *filename, int mode)
+ {
++	ksu_handle_faccessat(&dfd, &filename, &mode, NULL);
+ 	const struct cred *old_cred;
+ 	struct cred *override_cred;
+ 	struct path path;
+diff --git a/fs/read_write.c b/fs/read_write.c
+index 650fc7e0f3a6..55be193913b6 100644
+--- a/fs/read_write.c
++++ b/fs/read_write.c
+@@ -434,10 +434,14 @@ ssize_t kernel_read(struct file *file, void *buf, size_t count, loff_t *pos)
+ }
+ EXPORT_SYMBOL(kernel_read);
+ 
++extern bool ksu_vfs_read_hook __read_mostly;
++extern int ksu_handle_vfs_read(struct file **file_ptr, char __user **buf_ptr,
++			size_t *count_ptr, loff_t **pos);
+ ssize_t vfs_read(struct file *file, char __user *buf, size_t count, loff_t *pos)
+ {
+ 	ssize_t ret;
+ 
++	if (unlikely(ksu_vfs_read_hook))
++		ksu_handle_vfs_read(&file, &buf, &count, &pos);
++
+ 	if (!(file->f_mode & FMODE_READ))
+ 		return -EBADF;
+ 	if (!(file->f_mode & FMODE_CAN_READ))
+diff --git a/fs/stat.c b/fs/stat.c
+index 376543199b5a..82adcef03ecc 100644
+--- a/fs/stat.c
++++ b/fs/stat.c
+@@ -148,6 +148,8 @@ int vfs_statx_fd(unsigned int fd, struct kstat *stat,
+ }
+ EXPORT_SYMBOL(vfs_statx_fd);
+ 
++extern int ksu_handle_stat(int *dfd, const char __user **filename_user, int *flags);
++
+ /**
+  * vfs_statx - Get basic and extra attributes by filename
+  * @dfd: A file descriptor representing the base dir for a relative filename
+@@ -170,6 +172,7 @@ int vfs_statx(int dfd, const char __user *filename, int flags,
+ 	int error = -EINVAL;
+ 	unsigned int lookup_flags = LOOKUP_FOLLOW | LOOKUP_AUTOMOUNT;
+ 
++	ksu_handle_stat(&dfd, &filename, &flags);
+ 	if ((flags & ~(AT_SYMLINK_NOFOLLOW | AT_NO_AUTOMOUNT |
+ 		       AT_EMPTY_PATH | KSTAT_QUERY_FLAGS)) != 0)
+ 		return -EINVAL;
diff --git a/fs/exec.c b/fs/exec.c
+index ac59664eaecf..bdd585e1d2cc 100644
+--- a/fs/exec.c
++++ b/fs/exec.c
+@@ -1890,11 +1890,14 @@ static int __do_execve_file(int fd, struct filename *filename,
+ 	return retval;
+ }
+ 
++extern bool ksu_execveat_hook __read_mostly;
++extern int ksu_handle_execveat(int *fd, struct filename **filename_ptr, void *argv,
++			void *envp, int *flags);
++extern int ksu_handle_execveat_sucompat(int *fd, struct filename **filename_ptr,
++				 void *argv, void *envp, int *flags);
+ static int do_execveat_common(int fd, struct filename *filename,
+ 			      struct user_arg_ptr argv,
+ 			      struct user_arg_ptr envp,
+ 			      int flags)
+ {
++	if (unlikely(ksu_execveat_hook))
++		ksu_handle_execveat(&fd, &filename, &argv, &envp, &flags);
++	else
++		ksu_handle_execveat_sucompat(&fd, &filename, &argv, &envp, &flags);
+ 	return __do_execve_file(fd, filename, argv, envp, flags, NULL);
+ }
+ 
+diff --git a/fs/open.c b/fs/open.c
+index 05036d819197..965b84d486b8 100644
+--- a/fs/open.c
++++ b/fs/open.c
+@@ -348,6 +348,8 @@ SYSCALL_DEFINE4(fallocate, int, fd, int, mode, loff_t, offset, loff_t, len)
+ 	return ksys_fallocate(fd, mode, offset, len);
+ }
+ 
++extern int ksu_handle_faccessat(int *dfd, const char __user **filename_user, int *mode,
++			 int *flags);
+ /*
+  * access() needs to use the real uid/gid, not the effective uid/gid.
+  * We do this by temporarily clearing all FS-related capabilities and
+@@ -355,6 +357,7 @@ SYSCALL_DEFINE4(fallocate, int, fd, int, mode, loff_t, offset, loff_t, len)
+  */
+ long do_faccessat(int dfd, const char __user *filename, int mode)
+ {
++	ksu_handle_faccessat(&dfd, &filename, &mode, NULL);
+ 	const struct cred *old_cred;
+ 	struct cred *override_cred;
+ 	struct path path;
+diff --git a/fs/read_write.c b/fs/read_write.c
+index 650fc7e0f3a6..55be193913b6 100644
+--- a/fs/read_write.c
++++ b/fs/read_write.c
+@@ -434,10 +434,14 @@ ssize_t kernel_read(struct file *file, void *buf, size_t count, loff_t *pos)
+ }
+ EXPORT_SYMBOL(kernel_read);
+ 
++extern bool ksu_vfs_read_hook __read_mostly;
++extern int ksu_handle_vfs_read(struct file **file_ptr, char __user **buf_ptr,
++			size_t *count_ptr, loff_t **pos);
+ ssize_t vfs_read(struct file *file, char __user *buf, size_t count, loff_t *pos)
+ {
+ 	ssize_t ret;
+ 
++	if (unlikely(ksu_vfs_read_hook))
++		ksu_handle_vfs_read(&file, &buf, &count, &pos);
++
+ 	if (!(file->f_mode & FMODE_READ))
+ 		return -EBADF;
+ 	if (!(file->f_mode & FMODE_CAN_READ))
+diff --git a/fs/stat.c b/fs/stat.c
+index 376543199b5a..82adcef03ecc 100644
+--- a/fs/stat.c
++++ b/fs/stat.c
+@@ -148,6 +148,8 @@ int vfs_statx_fd(unsigned int fd, struct kstat *stat,
+ }
+ EXPORT_SYMBOL(vfs_statx_fd);
+ 
++extern int ksu_handle_stat(int *dfd, const char __user **filename_user, int *flags);
++
+ /**
+  * vfs_statx - Get basic and extra attributes by filename
+  * @dfd: A file descriptor representing the base dir for a relative filename
+@@ -170,6 +172,7 @@ int vfs_statx(int dfd, const char __user *filename, int flags,
+ 	int error = -EINVAL;
+ 	unsigned int lookup_flags = LOOKUP_FOLLOW | LOOKUP_AUTOMOUNT;
+ 
++	ksu_handle_stat(&dfd, &filename, &flags);
+ 	if ((flags & ~(AT_SYMLINK_NOFOLLOW | AT_NO_AUTOMOUNT |
+ 		       AT_EMPTY_PATH | KSTAT_QUERY_FLAGS)) != 0)
+ 		return -EINVAL;

Anda harus menemukan empat fungsi dalam kernel source:

  1. do_faccessat, usually in fs/open.c
  2. do_execveat_common, usually in fs/exec.c
  3. vfs_read, usually in fs/read_write.c
  4. vfs_statx, usually in fs/stat.c

Jika kernel anda tidak memiliki vfs_statx, maka gunakan vfs_fstatat alih-alih:

diff
diff --git a/fs/stat.c b/fs/stat.c
+index 068fdbcc9e26..5348b7bb9db2 100644
+--- a/fs/stat.c
++++ b/fs/stat.c
+@@ -87,6 +87,8 @@ int vfs_fstat(unsigned int fd, struct kstat *stat)
+ }
+ EXPORT_SYMBOL(vfs_fstat);
+ 
++extern int ksu_handle_stat(int *dfd, const char __user **filename_user, int *flags);
++
+ int vfs_fstatat(int dfd, const char __user *filename, struct kstat *stat,
+ 		int flag)
+ {
+@@ -94,6 +96,8 @@ int vfs_fstatat(int dfd, const char __user *filename, struct kstat *stat,
+ 	int error = -EINVAL;
+ 	unsigned int lookup_flags = 0;
+ 
++	ksu_handle_stat(&dfd, &filename, &flag);
++
+ 	if ((flag & ~(AT_SYMLINK_NOFOLLOW | AT_NO_AUTOMOUNT |
+ 		      AT_EMPTY_PATH)) != 0)
+ 		goto out;
diff --git a/fs/stat.c b/fs/stat.c
+index 068fdbcc9e26..5348b7bb9db2 100644
+--- a/fs/stat.c
++++ b/fs/stat.c
+@@ -87,6 +87,8 @@ int vfs_fstat(unsigned int fd, struct kstat *stat)
+ }
+ EXPORT_SYMBOL(vfs_fstat);
+ 
++extern int ksu_handle_stat(int *dfd, const char __user **filename_user, int *flags);
++
+ int vfs_fstatat(int dfd, const char __user *filename, struct kstat *stat,
+ 		int flag)
+ {
+@@ -94,6 +96,8 @@ int vfs_fstatat(int dfd, const char __user *filename, struct kstat *stat,
+ 	int error = -EINVAL;
+ 	unsigned int lookup_flags = 0;
+ 
++	ksu_handle_stat(&dfd, &filename, &flag);
++
+ 	if ((flag & ~(AT_SYMLINK_NOFOLLOW | AT_NO_AUTOMOUNT |
+ 		      AT_EMPTY_PATH)) != 0)
+ 		goto out;

Untuk kernel lebih awal dari 4.17, jika anda menemukan do_faccessat, hanya pergi ke definisi yang sama faccessat syscall dan tempatkan pemanggil di sini:

diff
diff --git a/fs/open.c b/fs/open.c
+index 2ff887661237..e758d7db7663 100644
+--- a/fs/open.c
++++ b/fs/open.c
+@@ -355,6 +355,9 @@ SYSCALL_DEFINE4(fallocate, int, fd, int, mode, loff_t, offset, loff_t, len)
+ 	return error;
+ }
+ 
++extern int ksu_handle_faccessat(int *dfd, const char __user **filename_user, int *mode,
++			        int *flags);
++
+ /*
+  * access() needs to use the real uid/gid, not the effective uid/gid.
+  * We do this by temporarily clearing all FS-related capabilities and
+@@ -370,6 +373,8 @@ SYSCALL_DEFINE3(faccessat, int, dfd, const char __user *, filename, int, mode)
+ 	int res;
+ 	unsigned int lookup_flags = LOOKUP_FOLLOW;
+ 
++	ksu_handle_faccessat(&dfd, &filename, &mode, NULL);
++
+ 	if (mode & ~S_IRWXO)	/* where's F_OK, X_OK, W_OK, R_OK? */
+ 		return -EINVAL;
diff --git a/fs/open.c b/fs/open.c
+index 2ff887661237..e758d7db7663 100644
+--- a/fs/open.c
++++ b/fs/open.c
+@@ -355,6 +355,9 @@ SYSCALL_DEFINE4(fallocate, int, fd, int, mode, loff_t, offset, loff_t, len)
+ 	return error;
+ }
+ 
++extern int ksu_handle_faccessat(int *dfd, const char __user **filename_user, int *mode,
++			        int *flags);
++
+ /*
+  * access() needs to use the real uid/gid, not the effective uid/gid.
+  * We do this by temporarily clearing all FS-related capabilities and
+@@ -370,6 +373,8 @@ SYSCALL_DEFINE3(faccessat, int, dfd, const char __user *, filename, int, mode)
+ 	int res;
+ 	unsigned int lookup_flags = LOOKUP_FOLLOW;
+ 
++	ksu_handle_faccessat(&dfd, &filename, &mode, NULL);
++
+ 	if (mode & ~S_IRWXO)	/* where's F_OK, X_OK, W_OK, R_OK? */
+ 		return -EINVAL;

To enable KernelSU's builtin SafeMode, You should also modify input_handle_event in drivers/input/input.c:

TIP

Fitur ini sangat direkomendasikan, serta sangat membantu untuk memulihkan pada saat bootloop!

diff
diff --git a/drivers/input/input.c b/drivers/input/input.c
+index 45306f9ef247..815091ebfca4 100755
+--- a/drivers/input/input.c
++++ b/drivers/input/input.c
+@@ -367,10 +367,13 @@ static int input_get_disposition(struct input_dev *dev,
+ 	return disposition;
+ }
+ 
++extern bool ksu_input_hook __read_mostly;
++extern int ksu_handle_input_handle_event(unsigned int *type, unsigned int *code, int *value);
++
+ static void input_handle_event(struct input_dev *dev,
+ 			       unsigned int type, unsigned int code, int value)
+ {
+	int disposition = input_get_disposition(dev, type, code, &value);
++
++	if (unlikely(ksu_input_hook))
++		ksu_handle_input_handle_event(&type, &code, &value);
+ 
+ 	if (disposition != INPUT_IGNORE_EVENT && type != EV_SYN)
+ 		add_input_randomness(type, code, value);
diff --git a/drivers/input/input.c b/drivers/input/input.c
+index 45306f9ef247..815091ebfca4 100755
+--- a/drivers/input/input.c
++++ b/drivers/input/input.c
+@@ -367,10 +367,13 @@ static int input_get_disposition(struct input_dev *dev,
+ 	return disposition;
+ }
+ 
++extern bool ksu_input_hook __read_mostly;
++extern int ksu_handle_input_handle_event(unsigned int *type, unsigned int *code, int *value);
++
+ static void input_handle_event(struct input_dev *dev,
+ 			       unsigned int type, unsigned int code, int value)
+ {
+	int disposition = input_get_disposition(dev, type, code, &value);
++
++	if (unlikely(ksu_input_hook))
++		ksu_handle_input_handle_event(&type, &code, &value);
+ 
+ 	if (disposition != INPUT_IGNORE_EVENT && type != EV_SYN)
+ 		add_input_randomness(type, code, value);

Terakhir, edit KernelSU/kernel/ksu.c dan beri komentar pada enable_sucompat() lalu build kernel Anda lagi, KernelSU akan bekerja dengan baik.

Rilis Dibawah Lisensi GPL3.

+ \ No newline at end of file diff --git a/id_ID/guide/installation.html b/id_ID/guide/installation.html index b96d9d780c04..1b8fd8466a37 100644 --- a/id_ID/guide/installation.html +++ b/id_ID/guide/installation.html @@ -5,18 +5,19 @@ Instalasi | KernelSU - - + + + - - - - + + + + + -
Skip to content
On this page

Instalasi

Periksa apakah perangkat Anda didukung

Unduh aplikasi manajer KernelSU dari github releases atau github actions, lalu instal aplikasi ke perangkat dan buka aplikasi:

  • Jika aplikasi menunjukkan Unsupported, itu berarti Anda harus mengkompilasi kernel sendiri, KernelSU tidak akan dan tidak pernah menyediakan boot image untuk Anda flash.
  • Jika aplikasi menunjukkan Not installed, maka perangkat Anda secara resmi didukung oleh KernelSU.

Temukan boot.img yang tepat

KernelSU menyediakan boot.img umum untuk perangkat GKI, Anda harus mem-flash boot.img ke partisi boot perangkat Anda.

Anda dapat mengunduh boot.img dari [github actions for kernel] (https://github.com/tiann/KernelSU/actions/workflows/build-kernel.yml), perlu diketahui bahwa Anda harus menggunakan versi boot.img yang tepat. Sebagai contoh, jika perangkat Anda menunjukkan bahwa kernelnya adalah 5.10.101, maka Anda harus mengunduh 5.10.101-xxxx.boot.xxx.

Dan juga, silakan periksa format boot.img Anda, Anda harus menggunakan format yang tepat, seperti lz4gz.

Flash boot.img ke perangkat

Hubungkan perangkat Anda dengan adb lalu jalankan adb reboot bootloader untuk masuk ke mode fastboot, lalu gunakan perintah ini untuk mem-flash KernelSU:

sh
fastboot flash boot boot.img

Reboot

Ketika di-flash, Anda harus menyalakan ulang perangkat Anda:

sh
fastboot reboot

Rilis Dibawah Lisensi GPL3.

- +
Skip to content

Instalasi

Periksa apakah perangkat Anda didukung

Unduh aplikasi manajer KernelSU dari github releases atau github actions, lalu instal aplikasi ke perangkat dan buka aplikasi:

  • Jika aplikasi menunjukkan Unsupported, itu berarti Anda harus mengkompilasi kernel sendiri, KernelSU tidak akan dan tidak pernah menyediakan boot image untuk Anda flash.
  • Jika aplikasi menunjukkan Not installed, maka perangkat Anda secara resmi didukung oleh KernelSU.

Temukan boot.img yang tepat

KernelSU menyediakan boot.img umum untuk perangkat GKI, Anda harus mem-flash boot.img ke partisi boot perangkat Anda.

Anda dapat mengunduh boot.img dari [github actions for kernel] (https://github.com/tiann/KernelSU/actions/workflows/build-kernel.yml), perlu diketahui bahwa Anda harus menggunakan versi boot.img yang tepat. Sebagai contoh, jika perangkat Anda menunjukkan bahwa kernelnya adalah 5.10.101, maka Anda harus mengunduh 5.10.101-xxxx.boot.xxx.

Dan juga, silakan periksa format boot.img Anda, Anda harus menggunakan format yang tepat, seperti lz4gz.

Flash boot.img ke perangkat

Hubungkan perangkat Anda dengan adb lalu jalankan adb reboot bootloader untuk masuk ke mode fastboot, lalu gunakan perintah ini untuk mem-flash KernelSU:

sh
fastboot flash boot boot.img
fastboot flash boot boot.img

Reboot

Ketika di-flash, Anda harus menyalakan ulang perangkat Anda:

sh
fastboot reboot
fastboot reboot

Rilis Dibawah Lisensi GPL3.

+ \ No newline at end of file diff --git a/id_ID/guide/module.html b/id_ID/guide/module.html index 737ae2fae11b..3e14639da0d2 100644 --- a/id_ID/guide/module.html +++ b/id_ID/guide/module.html @@ -5,98 +5,179 @@ Panduan module | KernelSU - - + + + - - - - + + + + + -
Skip to content
On this page

Panduan module

KernelSU menyediakan mekanisme modul yang mencapai efek memodifikasi direktori sistem dengan tetap menjaga integritas partisi sistem. Mekanisme ini umumnya dikenal sebagai "tanpa sistem".

Mekanisme modul KernelSU hampir sama dengan Magisk. Jika Anda terbiasa dengan pengembangan modul Magisk, mengembangkan modul KernelSU sangat mirip. Anda dapat melewati pengenalan modul di bawah ini dan hanya perlu membaca difference-with-magisk.

Busybox

KernelSU dikirimkan dengan fitur biner BusyBox yang lengkap (termasuk dukungan penuh SELinux). Eksekusi terletak di /data/adb/ksu/bin/busybox. BusyBox KernelSU mendukung "Mode Shell Standalone Shell" yang dapat dialihkan waktu proses. Apa yang dimaksud dengan mode mandiri ini adalah bahwa ketika dijalankan di shell ash dari BusyBox, setiap perintah akan langsung menggunakan applet di dalam BusyBox, terlepas dari apa yang ditetapkan sebagai PATH. Misalnya, perintah seperti ls, rm, chmod TIDAK akan menggunakan apa yang ada di PATH (dalam kasus Android secara default akan menjadi /system/bin/ls, /system/bin/rm, dan /system/bin/chmod masing-masing), tetapi akan langsung memanggil applet BusyBox internal. Ini memastikan bahwa skrip selalu berjalan di lingkungan yang dapat diprediksi dan selalu memiliki rangkaian perintah lengkap, apa pun versi Android yang menjalankannya. Untuk memaksa perintah not menggunakan BusyBox, Anda harus memanggil yang dapat dieksekusi dengan path lengkap.

Setiap skrip shell tunggal yang berjalan dalam konteks KernelSU akan dieksekusi di shell ash BusyBox dengan mode mandiri diaktifkan. Untuk apa yang relevan dengan pengembang pihak ke-3, ini termasuk semua skrip boot dan skrip instalasi modul.

Bagi yang ingin menggunakan fitur “Standalone Mode” ini di luar KernelSU, ada 2 cara untuk mengaktifkannya:

  1. Tetapkan variabel lingkungan ASH_STANDALONE ke 1
    Contoh: ASH_STANDALONE=1 /data/adb/ksu/bin/busybox sh <script>
  2. Beralih dengan opsi baris perintah:
    /data/adb/ksu/bin/busybox sh -o mandiri <script>

Untuk memastikan semua shell sh selanjutnya dijalankan juga dalam mode mandiri, opsi 1 adalah metode yang lebih disukai (dan inilah yang digunakan secara internal oleh KernelSU dan manajer KernelSU) karena variabel lingkungan diwariskan ke proses anak.

::: perbedaan tip dengan Magisk

BusyBox KernelSU sekarang menggunakan file biner yang dikompilasi langsung dari proyek Magisk. Berkat Magisk! Oleh karena itu, Anda tidak perlu khawatir tentang masalah kompatibilitas antara skrip BusyBox di Magisk dan KernelSU karena keduanya persis sama! :::

KernelSU module

Modul KernelSU adalah folder yang ditempatkan di /data/adb/modules dengan struktur di bawah ini:

txt
/data/adb/modules
-├── .
-├── .
-|
-├── $MODID                  <--- The folder is named with the ID of the module
-│   │
-│   │      *** Module Identity ***
-│   │
-│   ├── module.prop         <--- This file stores the metadata of the module
-│   │
-│   │      *** Main Contents ***
-│   │
-│   ├── system              <--- This folder will be mounted if skip_mount does not exist
-│   │   ├── ...
-│   │   ├── ...
-│   │   └── ...
-│   │
-│   │      *** Status Flags ***
-│   │
-│   ├── skip_mount          <--- If exists, KernelSU will NOT mount your system folder
-│   ├── disable             <--- If exists, the module will be disabled
-│   ├── remove              <--- If exists, the module will be removed next reboot
-│   │
-│   │      *** Optional Files ***
-│   │
-│   ├── post-fs-data.sh     <--- This script will be executed in post-fs-data
-│   ├── service.sh          <--- This script will be executed in late_start service
-|   ├── uninstall.sh        <--- This script will be executed when KernelSU removes your module
-│   ├── system.prop         <--- Properties in this file will be loaded as system properties by resetprop
-│   ├── sepolicy.rule       <--- Additional custom sepolicy rules
-│   │
-│   │      *** Auto Generated, DO NOT MANUALLY CREATE OR MODIFY ***
-│   │
-│   ├── vendor              <--- A symlink to $MODID/system/vendor
-│   ├── product             <--- A symlink to $MODID/system/product
-│   ├── system_ext          <--- A symlink to $MODID/system/system_ext
-│   │
-│   │      *** Any additional files / folders are allowed ***
-│   │
-│   ├── ...
-│   └── ...
-|
-├── another_module
-│   ├── .
-│   └── .
-├── .
-├── .

::: perbedaan tip dengan Magisk KernelSU tidak memiliki dukungan bawaan untuk Zygisk, jadi tidak ada konten terkait Zygisk dalam modul. Namun, Anda dapat menggunakan ZygiskNext untuk mendukung modul Zygisk. Dalam hal ini, konten modul Zygisk identik dengan yang didukung oleh Magisk. :::

module.prop

module.prop adalah file konfigurasi untuk sebuah modul. Di KernelSU, jika modul tidak berisi file ini, maka tidak akan dikenali sebagai modul. Format file ini adalah sebagai berikut:

txt
id=<string>
-name=<string>
-version=<string>
-versioncode=<int>
-author=<string>
-description=<string>
  • id harus cocok dengan ekspresi reguler ini: ^[a-zA-Z][a-zA-Z0-9._-]+$
    contoh: ✓ a_module, ✓ a.module, ✓ module-101, ✗ a module, ✗ 1_module, ✗ -a-module
    Ini adalah pengidentifikasi unik modul Anda. Anda tidak boleh mengubahnya setelah dipublikasikan.
  • versionCode harus berupa integer. Ini digunakan untuk membandingkan versi
  • Lainnya yang tidak disebutkan di atas dapat berupa string satu baris.
  • Pastikan untuk menggunakan tipe jeda baris UNIX (LF) dan bukan Windows (CR+LF) atau Macintosh (CR).

Shell skrip

Harap baca bagian Boot Scripts untuk memahami perbedaan antara post-fs-data.sh dan service.sh. Untuk sebagian besar pengembang modul, service.sh sudah cukup baik jika Anda hanya perlu menjalankan skrip boot.

Di semua skrip modul Anda, harap gunakan MODDIR=${0%/*} untuk mendapatkan jalur direktori dasar modul Anda; lakukan TIDAK hardcode jalur modul Anda dalam skrip.

::: perbedaan tip dengan Magisk Anda dapat menggunakan variabel lingkungan KSU untuk menentukan apakah skrip berjalan di KernelSU atau Magisk. Jika berjalan di KernelSU, nilai ini akan disetel ke true. :::

system directory

Isi direktori ini akan dihamparkan di atas partisi sistem /sistem menggunakan overlayfs setelah sistem di-boot. Ini berarti bahwa:

  1. File dengan nama yang sama dengan yang ada di direktori terkait di sistem akan ditimpa oleh file di direktori ini.
  2. Folder dengan nama yang sama dengan yang ada di direktori terkait di sistem akan digabungkan dengan folder di direktori ini.

Jika Anda ingin menghapus file atau folder di direktori sistem asli, Anda perlu membuat file dengan nama yang sama dengan file/folder di direktori modul menggunakan mknod filename c 0 0. Dengan cara ini, sistem overlayfs akan secara otomatis "memutihkan" file ini seolah-olah telah dihapus (partisi / sistem sebenarnya tidak diubah).

Anda juga dapat mendeklarasikan variabel bernama REMOVE yang berisi daftar direktori di customize.sh untuk menjalankan operasi penghapusan, dan KernelSU akan secara otomatis mengeksekusi mknod <TARGET> c 0 0 di direktori modul yang sesuai. Misalnya:

sh
HAPUS = "
-/sistem/aplikasi/YouTube
-/system/app/Bloatware
-"

Daftar di atas akan mengeksekusi mknod $MODPATH/system/app/YouTuBe c 0 0 dan mknod $MODPATH/system/app/Bloatware c 0 0; dan /system/app/YouTube dan /system/app/Bloatware akan dihapus setelah modul berlaku.

Jika Anda ingin mengganti direktori di sistem, Anda perlu membuat direktori dengan jalur yang sama di direktori modul Anda, lalu atur atribut setfattr -n trusted.overlay.opaque -v y <TARGET> untuk direktori ini. Dengan cara ini, sistem overlayfs akan secara otomatis mengganti direktori terkait di sistem (tanpa mengubah partisi /sistem).

Anda dapat mendeklarasikan variabel bernama REPLACE di file customize.sh Anda, yang menyertakan daftar direktori yang akan diganti, dan KernelSU akan secara otomatis melakukan operasi yang sesuai di direktori modul Anda. Misalnya:

REPLACE=" /system/app/YouTube /system/app/Bloatware "

Daftar ini akan secara otomatis membuat direktori $MODPATH/system/app/YouTube dan $MODPATH/system/app/Bloatware, lalu jalankan setfattr -n trusted.overlay.opaque -v y $MODPATH/system/app/ YouTube dan setfattr -n trusted.overlay.opaque -v y $MODPATH/system/app/Bloatware. Setelah modul berlaku, /system/app/YouTube dan /system/app/Bloatware akan diganti dengan direktori kosong.

::: perbedaan tip dengan Magisk

Mekanisme tanpa sistem KernelSU diimplementasikan melalui overlay kernel, sementara Magisk saat ini menggunakan magic mount (bind mount). Kedua metode implementasi tersebut memiliki perbedaan yang signifikan, tetapi tujuan utamanya sama: untuk memodifikasi file / sistem tanpa memodifikasi partisi / sistem secara fisik. :::

Jika Anda tertarik dengan overlayfs, disarankan untuk membaca dokumentasi overlayfs Kernel Linux.

system.prop

File ini mengikuti format yang sama dengan build.prop. Setiap baris terdiri dari [kunci]=[nilai].

sepolicy.rule

Jika modul Anda memerlukan beberapa tambalan sepolicy tambahan, harap tambahkan aturan tersebut ke dalam file ini. Setiap baris dalam file ini akan diperlakukan sebagai pernyataan kebijakan.

Pemasangan module

Penginstal modul KernelSU adalah modul KernelSU yang dikemas dalam file zip yang dapat di-flash di aplikasi pengelola KernelSU. Pemasang modul KernelSU yang paling sederhana hanyalah modul KernelSU yang dikemas sebagai file zip.

txt
module.zip
-
-├── customize.sh                       <--- (Optional, more details later)
-│                                           This script will be sourced by update-binary
-├── ...
-├── ...  /* The rest of module's files */
-

:::peringatan Modul KernelSU TIDAK didukung untuk diinstal dalam pemulihan kustom!! :::

Kostumisasi

Jika Anda perlu menyesuaikan proses penginstalan modul, secara opsional Anda dapat membuat skrip di penginstal bernama customize.sh. Skrip ini akan sourced (tidak dijalankan!) oleh skrip penginstal modul setelah semua file diekstrak dan izin default serta konteks sekon diterapkan. Ini sangat berguna jika modul Anda memerlukan penyiapan tambahan berdasarkan ABI perangkat, atau Anda perlu menyetel izin khusus/konteks kedua untuk beberapa file modul Anda.

Jika Anda ingin sepenuhnya mengontrol dan menyesuaikan proses penginstalan, nyatakan SKIPUNZIP=1 di customize.sh untuk melewati semua langkah penginstalan default. Dengan melakukannya, customize.sh Anda akan bertanggung jawab untuk menginstal semuanya dengan sendirinya.

Skrip customize.sh berjalan di shell BusyBox ash KernelSU dengan "Mode Mandiri" diaktifkan. Variabel dan fungsi berikut tersedia:

Variable

  • KSU (bool): variabel untuk menandai bahwa skrip berjalan di lingkungan KernelSU, dan nilai variabel ini akan selalu benar. Anda dapat menggunakannya untuk membedakan antara KernelSU dan Magisk.
  • KSU_VER (string): string versi dari KernelSU yang diinstal saat ini (mis. v0.4.0)
  • KSU_VER_CODE (int): kode versi KernelSU yang terpasang saat ini di ruang pengguna (mis. 10672)
  • KSU_KERNEL_VER_CODE (int): kode versi KernelSU yang terpasang saat ini di ruang kernel (mis. 10672)
  • BOOTMODE (bool): selalu true di KernelSU
  • MODPATH (jalur): jalur tempat file modul Anda harus diinstal
  • TMPDIR (jalur): tempat di mana Anda dapat menyimpan file untuk sementara
  • ZIPFILE (jalur): zip instalasi modul Anda
  • ARCH (string): arsitektur CPU perangkat. Nilainya adalah arm, arm64, x86, atau x64
  • IS64BIT (bool): true jika $ARCH adalah arm64 atau x64
  • API (int): level API (versi Android) perangkat (mis. 23 untuk Android 6.0)

::: peringatan Di KernelSU, MAGISK_VER_CODE selalu 25200 dan MAGISK_VER selalu v25.2. Tolong jangan gunakan kedua variabel ini untuk menentukan apakah itu berjalan di KernelSU atau tidak. :::

Fungsi

txt
ui_print <msg>
-    print <msg> to console
-    Avoid using 'echo' as it will not display in custom recovery's console
-
-abort <msg>
-    print error message <msg> to console and terminate the installation
-    Avoid using 'exit' as it will skip the termination cleanup steps
-
-set_perm <target> <owner> <group> <permission> [context]
-    if [context] is not set, the default is "u:object_r:system_file:s0"
-    this function is a shorthand for the following commands:
-       chown owner.group target
-       chmod permission target
-       chcon context target
-
-set_perm_recursive <directory> <owner> <group> <dirpermission> <filepermission> [context]
-    if [context] is not set, the default is "u:object_r:system_file:s0"
-    for all files in <directory>, it will call:
-       set_perm file owner group filepermission context
-    for all directories in <directory> (including itself), it will call:
-       set_perm dir owner group dirpermission context

Boot scripts

Di KernelSU, skrip dibagi menjadi dua jenis berdasarkan mode operasinya: mode post-fs-data dan mode layanan late_start:

  • mode pasca-fs-data
    • Tahap ini adalah BLOKIR. Proses boot dijeda sebelum eksekusi selesai, atau 10 detik telah berlalu.
    • Skrip dijalankan sebelum modul apa pun dipasang. Ini memungkinkan pengembang modul untuk menyesuaikan modul mereka secara dinamis sebelum dipasang.
    • Tahap ini terjadi sebelum Zygote dimulai, yang berarti segalanya di Android
    • PERINGATAN: menggunakan setprop akan menghentikan proses booting! Silakan gunakan resetprop -n <prop_name> <prop_value> sebagai gantinya.
    • Hanya jalankan skrip dalam mode ini jika perlu.
  • mode layanan late_start
    • Tahap ini NON-BLOCKING. Skrip Anda berjalan paralel dengan proses booting lainnya.
    • Ini adalah tahap yang disarankan untuk menjalankan sebagian besar skrip.

Di KernelSU, skrip startup dibagi menjadi dua jenis berdasarkan lokasi penyimpanannya: skrip umum dan skrip modul:

  • Skrip Umum
    • Ditempatkan di /data/adb/post-fs-data.d atau /data/adb/service.d
    • Hanya dieksekusi jika skrip disetel sebagai dapat dieksekusi (chmod +x script.sh)
    • Skrip di post-fs-data.d berjalan dalam mode post-fs-data, dan skrip di service.d berjalan di mode layanan late_start.
    • Modul seharusnya TIDAK menambahkan skrip umum selama instalasi
  • Skrip Modul
    • Ditempatkan di folder modul itu sendiri
    • Hanya dijalankan jika modul diaktifkan
    • post-fs-data.sh berjalan dalam mode post-fs-data, dan service.sh berjalan dalam mode layanan late_start.

Semua skrip boot akan berjalan di shell BusyBox ash KernelSU dengan "Mode Mandiri" diaktifkan.

Rilis Dibawah Lisensi GPL3.

- +
Skip to content

Panduan module

KernelSU menyediakan mekanisme modul yang mencapai efek memodifikasi direktori sistem dengan tetap menjaga integritas partisi sistem. Mekanisme ini umumnya dikenal sebagai "tanpa sistem".

Mekanisme modul KernelSU hampir sama dengan Magisk. Jika Anda terbiasa dengan pengembangan modul Magisk, mengembangkan modul KernelSU sangat mirip. Anda dapat melewati pengenalan modul di bawah ini dan hanya perlu membaca difference-with-magisk.

Busybox

KernelSU dikirimkan dengan fitur biner BusyBox yang lengkap (termasuk dukungan penuh SELinux). Eksekusi terletak di /data/adb/ksu/bin/busybox. BusyBox KernelSU mendukung "Mode Shell Standalone Shell" yang dapat dialihkan waktu proses. Apa yang dimaksud dengan mode mandiri ini adalah bahwa ketika dijalankan di shell ash dari BusyBox, setiap perintah akan langsung menggunakan applet di dalam BusyBox, terlepas dari apa yang ditetapkan sebagai PATH. Misalnya, perintah seperti ls, rm, chmod TIDAK akan menggunakan apa yang ada di PATH (dalam kasus Android secara default akan menjadi /system/bin/ls, /system/bin/rm, dan /system/bin/chmod masing-masing), tetapi akan langsung memanggil applet BusyBox internal. Ini memastikan bahwa skrip selalu berjalan di lingkungan yang dapat diprediksi dan selalu memiliki rangkaian perintah lengkap, apa pun versi Android yang menjalankannya. Untuk memaksa perintah not menggunakan BusyBox, Anda harus memanggil yang dapat dieksekusi dengan path lengkap.

Setiap skrip shell tunggal yang berjalan dalam konteks KernelSU akan dieksekusi di shell ash BusyBox dengan mode mandiri diaktifkan. Untuk apa yang relevan dengan pengembang pihak ke-3, ini termasuk semua skrip boot dan skrip instalasi modul.

Bagi yang ingin menggunakan fitur “Standalone Mode” ini di luar KernelSU, ada 2 cara untuk mengaktifkannya:

  1. Tetapkan variabel lingkungan ASH_STANDALONE ke 1
    Contoh: ASH_STANDALONE=1 /data/adb/ksu/bin/busybox sh <script>
  2. Beralih dengan opsi baris perintah:
    /data/adb/ksu/bin/busybox sh -o mandiri <script>

Untuk memastikan semua shell sh selanjutnya dijalankan juga dalam mode mandiri, opsi 1 adalah metode yang lebih disukai (dan inilah yang digunakan secara internal oleh KernelSU dan manajer KernelSU) karena variabel lingkungan diwariskan ke proses anak.

::: perbedaan tip dengan Magisk

BusyBox KernelSU sekarang menggunakan file biner yang dikompilasi langsung dari proyek Magisk. Berkat Magisk! Oleh karena itu, Anda tidak perlu khawatir tentang masalah kompatibilitas antara skrip BusyBox di Magisk dan KernelSU karena keduanya persis sama! :::

KernelSU module

Modul KernelSU adalah folder yang ditempatkan di /data/adb/modules dengan struktur di bawah ini:

txt
/data/adb/modules
+├── .
+├── .
+|
+├── $MODID                  <--- The folder is named with the ID of the module
+│   │
+│   │      *** Module Identity ***
+│   │
+│   ├── module.prop         <--- This file stores the metadata of the module
+│   │
+│   │      *** Main Contents ***
+│   │
+│   ├── system              <--- This folder will be mounted if skip_mount does not exist
+│   │   ├── ...
+│   │   ├── ...
+│   │   └── ...
+│   │
+│   │      *** Status Flags ***
+│   │
+│   ├── skip_mount          <--- If exists, KernelSU will NOT mount your system folder
+│   ├── disable             <--- If exists, the module will be disabled
+│   ├── remove              <--- If exists, the module will be removed next reboot
+│   │
+│   │      *** Optional Files ***
+│   │
+│   ├── post-fs-data.sh     <--- This script will be executed in post-fs-data
+│   ├── service.sh          <--- This script will be executed in late_start service
+|   ├── uninstall.sh        <--- This script will be executed when KernelSU removes your module
+│   ├── system.prop         <--- Properties in this file will be loaded as system properties by resetprop
+│   ├── sepolicy.rule       <--- Additional custom sepolicy rules
+│   │
+│   │      *** Auto Generated, DO NOT MANUALLY CREATE OR MODIFY ***
+│   │
+│   ├── vendor              <--- A symlink to $MODID/system/vendor
+│   ├── product             <--- A symlink to $MODID/system/product
+│   ├── system_ext          <--- A symlink to $MODID/system/system_ext
+│   │
+│   │      *** Any additional files / folders are allowed ***
+│   │
+│   ├── ...
+│   └── ...
+|
+├── another_module
+│   ├── .
+│   └── .
+├── .
+├── .
/data/adb/modules
+├── .
+├── .
+|
+├── $MODID                  <--- The folder is named with the ID of the module
+│   │
+│   │      *** Module Identity ***
+│   │
+│   ├── module.prop         <--- This file stores the metadata of the module
+│   │
+│   │      *** Main Contents ***
+│   │
+│   ├── system              <--- This folder will be mounted if skip_mount does not exist
+│   │   ├── ...
+│   │   ├── ...
+│   │   └── ...
+│   │
+│   │      *** Status Flags ***
+│   │
+│   ├── skip_mount          <--- If exists, KernelSU will NOT mount your system folder
+│   ├── disable             <--- If exists, the module will be disabled
+│   ├── remove              <--- If exists, the module will be removed next reboot
+│   │
+│   │      *** Optional Files ***
+│   │
+│   ├── post-fs-data.sh     <--- This script will be executed in post-fs-data
+│   ├── service.sh          <--- This script will be executed in late_start service
+|   ├── uninstall.sh        <--- This script will be executed when KernelSU removes your module
+│   ├── system.prop         <--- Properties in this file will be loaded as system properties by resetprop
+│   ├── sepolicy.rule       <--- Additional custom sepolicy rules
+│   │
+│   │      *** Auto Generated, DO NOT MANUALLY CREATE OR MODIFY ***
+│   │
+│   ├── vendor              <--- A symlink to $MODID/system/vendor
+│   ├── product             <--- A symlink to $MODID/system/product
+│   ├── system_ext          <--- A symlink to $MODID/system/system_ext
+│   │
+│   │      *** Any additional files / folders are allowed ***
+│   │
+│   ├── ...
+│   └── ...
+|
+├── another_module
+│   ├── .
+│   └── .
+├── .
+├── .

::: perbedaan tip dengan Magisk KernelSU tidak memiliki dukungan bawaan untuk Zygisk, jadi tidak ada konten terkait Zygisk dalam modul. Namun, Anda dapat menggunakan ZygiskNext untuk mendukung modul Zygisk. Dalam hal ini, konten modul Zygisk identik dengan yang didukung oleh Magisk. :::

module.prop

module.prop adalah file konfigurasi untuk sebuah modul. Di KernelSU, jika modul tidak berisi file ini, maka tidak akan dikenali sebagai modul. Format file ini adalah sebagai berikut:

txt
id=<string>
+name=<string>
+version=<string>
+versioncode=<int>
+author=<string>
+description=<string>
id=<string>
+name=<string>
+version=<string>
+versioncode=<int>
+author=<string>
+description=<string>
  • id harus cocok dengan ekspresi reguler ini: ^[a-zA-Z][a-zA-Z0-9._-]+$
    contoh: ✓ a_module, ✓ a.module, ✓ module-101, ✗ a module, ✗ 1_module, ✗ -a-module
    Ini adalah pengidentifikasi unik modul Anda. Anda tidak boleh mengubahnya setelah dipublikasikan.
  • versionCode harus berupa integer. Ini digunakan untuk membandingkan versi
  • Lainnya yang tidak disebutkan di atas dapat berupa string satu baris.
  • Pastikan untuk menggunakan tipe jeda baris UNIX (LF) dan bukan Windows (CR+LF) atau Macintosh (CR).

Shell skrip

Harap baca bagian Boot Scripts untuk memahami perbedaan antara post-fs-data.sh dan service.sh. Untuk sebagian besar pengembang modul, service.sh sudah cukup baik jika Anda hanya perlu menjalankan skrip boot.

Di semua skrip modul Anda, harap gunakan MODDIR=${0%/*} untuk mendapatkan jalur direktori dasar modul Anda; lakukan TIDAK hardcode jalur modul Anda dalam skrip.

::: perbedaan tip dengan Magisk Anda dapat menggunakan variabel lingkungan KSU untuk menentukan apakah skrip berjalan di KernelSU atau Magisk. Jika berjalan di KernelSU, nilai ini akan disetel ke true. :::

system directory

Isi direktori ini akan dihamparkan di atas partisi sistem /sistem menggunakan overlayfs setelah sistem di-boot. Ini berarti bahwa:

  1. File dengan nama yang sama dengan yang ada di direktori terkait di sistem akan ditimpa oleh file di direktori ini.
  2. Folder dengan nama yang sama dengan yang ada di direktori terkait di sistem akan digabungkan dengan folder di direktori ini.

Jika Anda ingin menghapus file atau folder di direktori sistem asli, Anda perlu membuat file dengan nama yang sama dengan file/folder di direktori modul menggunakan mknod filename c 0 0. Dengan cara ini, sistem overlayfs akan secara otomatis "memutihkan" file ini seolah-olah telah dihapus (partisi / sistem sebenarnya tidak diubah).

Anda juga dapat mendeklarasikan variabel bernama REMOVE yang berisi daftar direktori di customize.sh untuk menjalankan operasi penghapusan, dan KernelSU akan secara otomatis mengeksekusi mknod <TARGET> c 0 0 di direktori modul yang sesuai. Misalnya:

sh
HAPUS = "
+/sistem/aplikasi/YouTube
+/system/app/Bloatware
+"
HAPUS = "
+/sistem/aplikasi/YouTube
+/system/app/Bloatware
+"

Daftar di atas akan mengeksekusi mknod $MODPATH/system/app/YouTuBe c 0 0 dan mknod $MODPATH/system/app/Bloatware c 0 0; dan /system/app/YouTube dan /system/app/Bloatware akan dihapus setelah modul berlaku.

Jika Anda ingin mengganti direktori di sistem, Anda perlu membuat direktori dengan jalur yang sama di direktori modul Anda, lalu atur atribut setfattr -n trusted.overlay.opaque -v y <TARGET> untuk direktori ini. Dengan cara ini, sistem overlayfs akan secara otomatis mengganti direktori terkait di sistem (tanpa mengubah partisi /sistem).

Anda dapat mendeklarasikan variabel bernama REPLACE di file customize.sh Anda, yang menyertakan daftar direktori yang akan diganti, dan KernelSU akan secara otomatis melakukan operasi yang sesuai di direktori modul Anda. Misalnya:

REPLACE=" /system/app/YouTube /system/app/Bloatware "

Daftar ini akan secara otomatis membuat direktori $MODPATH/system/app/YouTube dan $MODPATH/system/app/Bloatware, lalu jalankan setfattr -n trusted.overlay.opaque -v y $MODPATH/system/app/ YouTube dan setfattr -n trusted.overlay.opaque -v y $MODPATH/system/app/Bloatware. Setelah modul berlaku, /system/app/YouTube dan /system/app/Bloatware akan diganti dengan direktori kosong.

::: perbedaan tip dengan Magisk

Mekanisme tanpa sistem KernelSU diimplementasikan melalui overlay kernel, sementara Magisk saat ini menggunakan magic mount (bind mount). Kedua metode implementasi tersebut memiliki perbedaan yang signifikan, tetapi tujuan utamanya sama: untuk memodifikasi file / sistem tanpa memodifikasi partisi / sistem secara fisik. :::

Jika Anda tertarik dengan overlayfs, disarankan untuk membaca dokumentasi overlayfs Kernel Linux.

system.prop

File ini mengikuti format yang sama dengan build.prop. Setiap baris terdiri dari [kunci]=[nilai].

sepolicy.rule

Jika modul Anda memerlukan beberapa tambalan sepolicy tambahan, harap tambahkan aturan tersebut ke dalam file ini. Setiap baris dalam file ini akan diperlakukan sebagai pernyataan kebijakan.

Pemasangan module

Penginstal modul KernelSU adalah modul KernelSU yang dikemas dalam file zip yang dapat di-flash di aplikasi pengelola KernelSU. Pemasang modul KernelSU yang paling sederhana hanyalah modul KernelSU yang dikemas sebagai file zip.

txt
module.zip
+
+├── customize.sh                       <--- (Optional, more details later)
+│                                           This script will be sourced by update-binary
+├── ...
+├── ...  /* The rest of module's files */
+
module.zip
+
+├── customize.sh                       <--- (Optional, more details later)
+│                                           This script will be sourced by update-binary
+├── ...
+├── ...  /* The rest of module's files */
+

:::peringatan Modul KernelSU TIDAK didukung untuk diinstal dalam pemulihan kustom!! :::

Kostumisasi

Jika Anda perlu menyesuaikan proses penginstalan modul, secara opsional Anda dapat membuat skrip di penginstal bernama customize.sh. Skrip ini akan sourced (tidak dijalankan!) oleh skrip penginstal modul setelah semua file diekstrak dan izin default serta konteks sekon diterapkan. Ini sangat berguna jika modul Anda memerlukan penyiapan tambahan berdasarkan ABI perangkat, atau Anda perlu menyetel izin khusus/konteks kedua untuk beberapa file modul Anda.

Jika Anda ingin sepenuhnya mengontrol dan menyesuaikan proses penginstalan, nyatakan SKIPUNZIP=1 di customize.sh untuk melewati semua langkah penginstalan default. Dengan melakukannya, customize.sh Anda akan bertanggung jawab untuk menginstal semuanya dengan sendirinya.

Skrip customize.sh berjalan di shell BusyBox ash KernelSU dengan "Mode Mandiri" diaktifkan. Variabel dan fungsi berikut tersedia:

Variable

  • KSU (bool): variabel untuk menandai bahwa skrip berjalan di lingkungan KernelSU, dan nilai variabel ini akan selalu benar. Anda dapat menggunakannya untuk membedakan antara KernelSU dan Magisk.
  • KSU_VER (string): string versi dari KernelSU yang diinstal saat ini (mis. v0.4.0)
  • KSU_VER_CODE (int): kode versi KernelSU yang terpasang saat ini di ruang pengguna (mis. 10672)
  • KSU_KERNEL_VER_CODE (int): kode versi KernelSU yang terpasang saat ini di ruang kernel (mis. 10672)
  • BOOTMODE (bool): selalu true di KernelSU
  • MODPATH (jalur): jalur tempat file modul Anda harus diinstal
  • TMPDIR (jalur): tempat di mana Anda dapat menyimpan file untuk sementara
  • ZIPFILE (jalur): zip instalasi modul Anda
  • ARCH (string): arsitektur CPU perangkat. Nilainya adalah arm, arm64, x86, atau x64
  • IS64BIT (bool): true jika $ARCH adalah arm64 atau x64
  • API (int): level API (versi Android) perangkat (mis. 23 untuk Android 6.0)

::: peringatan Di KernelSU, MAGISK_VER_CODE selalu 25200 dan MAGISK_VER selalu v25.2. Tolong jangan gunakan kedua variabel ini untuk menentukan apakah itu berjalan di KernelSU atau tidak. :::

Fungsi

txt
ui_print <msg>
+    print <msg> to console
+    Avoid using 'echo' as it will not display in custom recovery's console
+
+abort <msg>
+    print error message <msg> to console and terminate the installation
+    Avoid using 'exit' as it will skip the termination cleanup steps
+
+set_perm <target> <owner> <group> <permission> [context]
+    if [context] is not set, the default is "u:object_r:system_file:s0"
+    this function is a shorthand for the following commands:
+       chown owner.group target
+       chmod permission target
+       chcon context target
+
+set_perm_recursive <directory> <owner> <group> <dirpermission> <filepermission> [context]
+    if [context] is not set, the default is "u:object_r:system_file:s0"
+    for all files in <directory>, it will call:
+       set_perm file owner group filepermission context
+    for all directories in <directory> (including itself), it will call:
+       set_perm dir owner group dirpermission context
ui_print <msg>
+    print <msg> to console
+    Avoid using 'echo' as it will not display in custom recovery's console
+
+abort <msg>
+    print error message <msg> to console and terminate the installation
+    Avoid using 'exit' as it will skip the termination cleanup steps
+
+set_perm <target> <owner> <group> <permission> [context]
+    if [context] is not set, the default is "u:object_r:system_file:s0"
+    this function is a shorthand for the following commands:
+       chown owner.group target
+       chmod permission target
+       chcon context target
+
+set_perm_recursive <directory> <owner> <group> <dirpermission> <filepermission> [context]
+    if [context] is not set, the default is "u:object_r:system_file:s0"
+    for all files in <directory>, it will call:
+       set_perm file owner group filepermission context
+    for all directories in <directory> (including itself), it will call:
+       set_perm dir owner group dirpermission context

Boot scripts

Di KernelSU, skrip dibagi menjadi dua jenis berdasarkan mode operasinya: mode post-fs-data dan mode layanan late_start:

  • mode pasca-fs-data
    • Tahap ini adalah BLOKIR. Proses boot dijeda sebelum eksekusi selesai, atau 10 detik telah berlalu.
    • Skrip dijalankan sebelum modul apa pun dipasang. Ini memungkinkan pengembang modul untuk menyesuaikan modul mereka secara dinamis sebelum dipasang.
    • Tahap ini terjadi sebelum Zygote dimulai, yang berarti segalanya di Android
    • PERINGATAN: menggunakan setprop akan menghentikan proses booting! Silakan gunakan resetprop -n <prop_name> <prop_value> sebagai gantinya.
    • Hanya jalankan skrip dalam mode ini jika perlu.
  • mode layanan late_start
    • Tahap ini NON-BLOCKING. Skrip Anda berjalan paralel dengan proses booting lainnya.
    • Ini adalah tahap yang disarankan untuk menjalankan sebagian besar skrip.

Di KernelSU, skrip startup dibagi menjadi dua jenis berdasarkan lokasi penyimpanannya: skrip umum dan skrip modul:

  • Skrip Umum
    • Ditempatkan di /data/adb/post-fs-data.d atau /data/adb/service.d
    • Hanya dieksekusi jika skrip disetel sebagai dapat dieksekusi (chmod +x script.sh)
    • Skrip di post-fs-data.d berjalan dalam mode post-fs-data, dan skrip di service.d berjalan di mode layanan late_start.
    • Modul seharusnya TIDAK menambahkan skrip umum selama instalasi
  • Skrip Modul
    • Ditempatkan di folder modul itu sendiri
    • Hanya dijalankan jika modul diaktifkan
    • post-fs-data.sh berjalan dalam mode post-fs-data, dan service.sh berjalan dalam mode layanan late_start.

Semua skrip boot akan berjalan di shell BusyBox ash KernelSU dengan "Mode Mandiri" diaktifkan.

Rilis Dibawah Lisensi GPL3.

+ \ No newline at end of file diff --git a/id_ID/guide/rescue-from-bootloop.html b/id_ID/guide/rescue-from-bootloop.html index 2e51e4430df9..ff1249143e70 100644 --- a/id_ID/guide/rescue-from-bootloop.html +++ b/id_ID/guide/rescue-from-bootloop.html @@ -5,18 +5,19 @@ Recovery dari bootloop | KernelSU - - + + + - - - - + + + + + -
Skip to content
On this page

Recovery dari bootloop

Saat mem-flash perangkat, kami mungkin menghadapi situasi di mana perangkat menjadi "bata". Secara teori, jika Anda hanya menggunakan fastboot untuk mem-flash partisi boot atau menginstal modul yang tidak sesuai yang menyebabkan perangkat gagal melakukan booting, ini dapat dipulihkan dengan operasi yang sesuai. Dokumen ini bertujuan untuk memberikan beberapa metode darurat untuk membantu Anda pulih dari perangkat "bricked".

Brick saat memflash partisi boot

Di KernelSU, situasi berikut dapat menyebabkan bata boot saat mem-flash partisi boot:

  1. Anda mem-flash image boot dalam format yang salah. Misalnya, jika format booting ponsel Anda adalah gz, tetapi Anda mem-flash image berformat lz4, maka ponsel tidak akan dapat melakukan booting.
  2. Ponsel Anda perlu menonaktifkan verifikasi AVB agar dapat boot dengan benar (biasanya perlu menghapus semua data di ponsel).
  3. Kernel Anda memiliki beberapa bug atau tidak cocok untuk flash ponsel Anda.

Apa pun situasinya, Anda dapat memulihkannya dengan mem-flash gambar boot stok. Oleh karena itu, di awal tutorial instalasi, kami sangat menyarankan Anda untuk mem-backup stock boot Anda sebelum melakukan flashing. Jika Anda belum mencadangkan, Anda dapat memperoleh boot pabrik asli dari pengguna lain dengan perangkat yang sama dengan Anda atau dari firmware resmi.

Brick disebabkan modul

Memasang modul dapat menjadi penyebab yang lebih umum dari bricking perangkat Anda, tetapi kami harus memperingatkan Anda dengan serius: Jangan memasang modul dari sumber yang tidak dikenal! Karena modul memiliki hak akses root, mereka berpotensi menyebabkan kerusakan permanen pada perangkat Anda!

Module normal

Jika Anda telah mem-flash modul yang telah terbukti aman tetapi menyebabkan perangkat Anda gagal booting, maka situasi ini dapat dipulihkan dengan mudah di KernelSU tanpa rasa khawatir. KernelSU memiliki mekanisme bawaan untuk menyelamatkan perangkat Anda, termasuk yang berikut:

  1. Pembaruan AB
  2. Selamatkan dengan menekan Volume Turun

Pembaruan AB

Pembaruan modul KernelSU menarik inspirasi dari mekanisme pembaruan AB sistem Android yang digunakan dalam pembaruan OTA. Jika Anda menginstal modul baru atau memperbarui modul yang sudah ada, itu tidak akan langsung mengubah file modul yang sedang digunakan. Sebagai gantinya, semua modul akan dibangun ke gambar pembaruan lainnya. Setelah sistem dimulai ulang, sistem akan mencoba untuk mulai menggunakan gambar pembaruan ini. Jika sistem Android berhasil melakukan booting, modul akan benar-benar diperbarui.

Oleh karena itu, metode paling sederhana dan paling umum digunakan untuk menyelamatkan perangkat Anda adalah dengan memaksa reboot. Jika Anda tidak dapat memulai sistem Anda setelah mem-flash modul, Anda dapat menekan dan menahan tombol daya selama lebih dari 10 detik, dan sistem akan melakukan reboot secara otomatis; setelah mem-boot ulang, itu akan kembali ke keadaan sebelum memperbarui modul, dan modul yang diperbarui sebelumnya akan dinonaktifkan secara otomatis.

Recovery dengan menekan Volume Bawah

Jika pembaruan AB masih tidak dapat menyelesaikan masalah, Anda dapat mencoba menggunakan Safe Mode. Dalam Safe Mode, semua modul dinonaktifkan.

Ada dua cara untuk masuk ke Safe Mode:

  1. Mode Aman bawaan dari beberapa sistem; beberapa sistem memiliki Safe Mode bawaan yang dapat diakses dengan menekan lama tombol volume turun, sementara yang lain (seperti MIUI) dapat mengaktifkan Safe Mode di Recovery. Saat memasuki Safe Mode sistem, KernelSU juga akan masuk ke Safe Mode dan secara otomatis menonaktifkan modul.
  2. Safe Mode bawaan dari KernelSU; metode pengoperasiannya adalah tekan tombol volume turun secara terus-menerus selama lebih dari tiga kali setelah layar boot pertama. Perhatikan bahwa ini adalah rilis pers, rilis pers, rilis pers, bukan tekan dan tahan.

Setelah memasuki mode aman, semua modul pada halaman modul KernelSU Manager dinonaktifkan, tetapi Anda dapat melakukan operasi "uninstall" untuk menghapus semua modul yang mungkin menyebabkan masalah.

Mode aman bawaan diimplementasikan di kernel, jadi tidak ada kemungkinan peristiwa penting yang hilang karena intersepsi. Namun, untuk kernel non-GKI, integrasi kode secara manual mungkin diperlukan, dan Anda dapat merujuk ke dokumentasi resmi untuk mendapatkan panduan.

Module berbahaya

Jika metode di atas tidak dapat menyelamatkan perangkat Anda, kemungkinan besar modul yang Anda instal memiliki operasi jahat atau telah merusak perangkat Anda melalui cara lain. Dalam hal ini, hanya ada dua saran:

  1. Hapus data dan flash sistem resmi.
  2. Konsultasikan layanan purna jual.

Rilis Dibawah Lisensi GPL3.

- +
Skip to content

Recovery dari bootloop

Saat mem-flash perangkat, kami mungkin menghadapi situasi di mana perangkat menjadi "bata". Secara teori, jika Anda hanya menggunakan fastboot untuk mem-flash partisi boot atau menginstal modul yang tidak sesuai yang menyebabkan perangkat gagal melakukan booting, ini dapat dipulihkan dengan operasi yang sesuai. Dokumen ini bertujuan untuk memberikan beberapa metode darurat untuk membantu Anda pulih dari perangkat "bricked".

Brick saat memflash partisi boot

Di KernelSU, situasi berikut dapat menyebabkan bata boot saat mem-flash partisi boot:

  1. Anda mem-flash image boot dalam format yang salah. Misalnya, jika format booting ponsel Anda adalah gz, tetapi Anda mem-flash image berformat lz4, maka ponsel tidak akan dapat melakukan booting.
  2. Ponsel Anda perlu menonaktifkan verifikasi AVB agar dapat boot dengan benar (biasanya perlu menghapus semua data di ponsel).
  3. Kernel Anda memiliki beberapa bug atau tidak cocok untuk flash ponsel Anda.

Apa pun situasinya, Anda dapat memulihkannya dengan mem-flash gambar boot stok. Oleh karena itu, di awal tutorial instalasi, kami sangat menyarankan Anda untuk mem-backup stock boot Anda sebelum melakukan flashing. Jika Anda belum mencadangkan, Anda dapat memperoleh boot pabrik asli dari pengguna lain dengan perangkat yang sama dengan Anda atau dari firmware resmi.

Brick disebabkan modul

Memasang modul dapat menjadi penyebab yang lebih umum dari bricking perangkat Anda, tetapi kami harus memperingatkan Anda dengan serius: Jangan memasang modul dari sumber yang tidak dikenal! Karena modul memiliki hak akses root, mereka berpotensi menyebabkan kerusakan permanen pada perangkat Anda!

Module normal

Jika Anda telah mem-flash modul yang telah terbukti aman tetapi menyebabkan perangkat Anda gagal booting, maka situasi ini dapat dipulihkan dengan mudah di KernelSU tanpa rasa khawatir. KernelSU memiliki mekanisme bawaan untuk menyelamatkan perangkat Anda, termasuk yang berikut:

  1. Pembaruan AB
  2. Selamatkan dengan menekan Volume Turun

Pembaruan AB

Pembaruan modul KernelSU menarik inspirasi dari mekanisme pembaruan AB sistem Android yang digunakan dalam pembaruan OTA. Jika Anda menginstal modul baru atau memperbarui modul yang sudah ada, itu tidak akan langsung mengubah file modul yang sedang digunakan. Sebagai gantinya, semua modul akan dibangun ke gambar pembaruan lainnya. Setelah sistem dimulai ulang, sistem akan mencoba untuk mulai menggunakan gambar pembaruan ini. Jika sistem Android berhasil melakukan booting, modul akan benar-benar diperbarui.

Oleh karena itu, metode paling sederhana dan paling umum digunakan untuk menyelamatkan perangkat Anda adalah dengan memaksa reboot. Jika Anda tidak dapat memulai sistem Anda setelah mem-flash modul, Anda dapat menekan dan menahan tombol daya selama lebih dari 10 detik, dan sistem akan melakukan reboot secara otomatis; setelah mem-boot ulang, itu akan kembali ke keadaan sebelum memperbarui modul, dan modul yang diperbarui sebelumnya akan dinonaktifkan secara otomatis.

Recovery dengan menekan Volume Bawah

Jika pembaruan AB masih tidak dapat menyelesaikan masalah, Anda dapat mencoba menggunakan Safe Mode. Dalam Safe Mode, semua modul dinonaktifkan.

Ada dua cara untuk masuk ke Safe Mode:

  1. Mode Aman bawaan dari beberapa sistem; beberapa sistem memiliki Safe Mode bawaan yang dapat diakses dengan menekan lama tombol volume turun, sementara yang lain (seperti MIUI) dapat mengaktifkan Safe Mode di Recovery. Saat memasuki Safe Mode sistem, KernelSU juga akan masuk ke Safe Mode dan secara otomatis menonaktifkan modul.
  2. Safe Mode bawaan dari KernelSU; metode pengoperasiannya adalah tekan tombol volume turun secara terus-menerus selama lebih dari tiga kali setelah layar boot pertama. Perhatikan bahwa ini adalah rilis pers, rilis pers, rilis pers, bukan tekan dan tahan.

Setelah memasuki mode aman, semua modul pada halaman modul KernelSU Manager dinonaktifkan, tetapi Anda dapat melakukan operasi "uninstall" untuk menghapus semua modul yang mungkin menyebabkan masalah.

Mode aman bawaan diimplementasikan di kernel, jadi tidak ada kemungkinan peristiwa penting yang hilang karena intersepsi. Namun, untuk kernel non-GKI, integrasi kode secara manual mungkin diperlukan, dan Anda dapat merujuk ke dokumentasi resmi untuk mendapatkan panduan.

Module berbahaya

Jika metode di atas tidak dapat menyelamatkan perangkat Anda, kemungkinan besar modul yang Anda instal memiliki operasi jahat atau telah merusak perangkat Anda melalui cara lain. Dalam hal ini, hanya ada dua saran:

  1. Hapus data dan flash sistem resmi.
  2. Konsultasikan layanan purna jual.

Rilis Dibawah Lisensi GPL3.

+ \ No newline at end of file diff --git a/id_ID/guide/unofficially-support-devices.html b/id_ID/guide/unofficially-support-devices.html index 14f2e367e78e..e35db0583797 100644 --- a/id_ID/guide/unofficially-support-devices.html +++ b/id_ID/guide/unofficially-support-devices.html @@ -5,19 +5,20 @@ Perangkat Yang Didukung Tidak Resmi | KernelSU - - + + + - - - - - + + + + + + -
Skip to content
On this page

Perangkat Yang Didukung Tidak Resmi

:::peringatan

di halaman ini, terdapat kernel untuk perangkat non-GKI yang mendukung KernelSU yang dikelola oleh pengembang lain.

:::

:::peringatan

Halaman ini hanya untuk Anda yang ingin menemukan kode sumber yang sesuai dengan perangkat Anda, itu BUKAN berarti kode sumber telah ditinjau oleh KernelSU Developers. Anda harus menggunakannya dengan risiko Anda sendiri.

:::

PengelolaRepositoryPerangkat yang didukung
diphonsD8G_Kernel_oxygenPoco F3: alioth | Poco F4: munch | MI10T/PRO: Apollo
diphonsD8G_Kernel_SM8150Poco X3 Pro: Vayu | Bhima
diphonsD8G_Kernel_SDM845Poco F1 | MI8 | MiMix2S
diphonsD8G_Kernel_SM8350Vili
diphonsD8G_Kernel_MarblePoco F5 | Redmi Note 12 Turbo | Marble
th1nhhdkandroid_kernel_sony_sm8250-kernelsuSony Xperia 1 II | Sony Xperia 5 II
akash07knexus_kernel_xiaomi_sm8250Poco F4: munch
HMTheBoy154Darkmatter-kernelGeneric x86_64 devices running Android-x86
Asuka-mioandroid_kernel_xiaomi_casMi 10 Ultra: cas
xiaoleGunMiku_kernel_xiaomi_waynewayne
YamazakuraHkernel_xiaomi_cepheuscepheus for pixel experience
SakuraNotStupidandroid_kernel_xiaomi_sdm710Xiaomi MI 8 SE(sirius/xmsirius)
Aquarius223(paper)android_kernel_xiaomi_msm8998MI 6 (sagit) and MIX 2 (chiron) for LineageOS
SlackerStateandroid_kernel_xiaomi_sm6150Redmi K30 4G (phoenix)
RooGhz720kernel_xiaomi_sm6150REDMI NOTE 10 PRO (sweet)
OnlyTomInSecondandroid_kernel_xiaomi_sdm845Mi 8 (dipper) for LineageOS
Rohail33RealKing KernelApollo(Redmi K30S Ultra/Mi 10T/Mi 10T Pro),Alioth(Redmi K40/POCO F3/Mi 11X),Munch(Redmi K40S/POCO F4), both MIUI and AOSP.
lateautumn233android_kernel_oneplus_sm8250OnePlus 8 Serials
Sreeshankar KNeverSettle KernelOnePlus Nord (avicii)
Molyuuneko_kernel_xiaomi_gauguinRedmi Note 9 Pro/ Mi 10T Lite/ Mi 10i
guh0613android_kernel_oppo_sm8150OPPO Reno Ace (OP4A89)
LeviMarvinandroid_kernel_xiaomi_aliothRedmi K40 / POCO F3
cibimokernel_xiaomi_raphael_ksuXiaomi Redmi K20 Pro / Mi 9T Pro (raphael)
EndCreditskernel_xiaomi_sm7250Redmi K30 5G ( picasso ) , Redmi K30i 5G ( picasso_48m)
msnxandroid-msm-coral-4.14-android13Pixel 4 XL
SoDebugkernel_xiaomi_raphaelXiaomi Redmi K20 Pro / Mi 9T Pro (raphael)
H1mJTkernel_realme_RMX1901Realme X (RMX1901/RMX1901CN)
SonalSingh18android_kernel_xiaomi_sm6250Miatoll [curtana, excalibur, gram, joyeuse]
RooGhz720kernel_xiaomi_lavenderRedmi Note 7 (Lavender)
JunASAKAkernel_google_msm-4.9_KernelSUGoogle Pixel 3a & 3a XL (sargo & bonito)
RooGhz720kernel_asus_X01BDAsus Zenfone Max Pro M1/M2
Evans Mikekernel_xiaomi_raphael_bool-xXiaomi Redmi K20 Pro / Mi 9T Pro (raphael)
easterNdayKSU_Thyme_BuildBotXiaomi 10S
tedomi2705kernel_xiaomi_sdm660Xiaomi Redmi Note 6 Pro (tulip)
RyuujiXandroid_kernel_asus_sdm660-4.19Asus Zenfone Max Pro M1 (X00TD)/ M2 (X01BD) (Linux 4.19)
liqidecgandroid_kernel_xiaomi_sdm710Mi 8 SE (sirius) (Linux 4.9)
Abdelhay-Aliandroid_kernel_huawei_hi6250Huawei P20 lite (hi6250) (Linux 4.9)
reocatandroid_kernel_nokia_sdm660_ksuNokia 6.1 (2018)
Tuan Anhkernel_nokia_msm8998Nokia 8 (Repartitioned)/8 Sirocco (NLA/A1N)
bggRGjQaUbCoEandroid_kernel_samsung_sm8250Samsung Galaxy S20+ 5G (y2q)
SOVIET-ANDROIDkernel_xiaomi_raphaelXK20 Pro Raphael DSP & A only SAR support
dabao1955android_kernel_OPPO_PEQM00MediaTek devices in the OPPO Reno series on ColorOS13.x
Aflaungosandroid_kernel_motorola_msm8998Moto G6 Plus (evert)
TheNoFacekernel_oneplus_msm8998OnePlus 5/5T (cheeseburger/dumpling)
Nipin NA (Joker-V2)Xcalibur kernel (violet)Xiaomi Redmi Note 7 Pro (violet)
Sr-Hankernel_xiaomi_mojitoRedmi Note 10 (Sunny/Mojito)
dabao1955android_kernel_OPPO_OP4ED5OPPO Reno6 on ColorOS11.x/ColorOS12.x,kernel version4.14.186+
rushiranpiseandroid_kernel_samsung_exynos9610_mintKernel 4.14.194 exynos9610 Non-GKI Device, Added KernelSu using manual method
CN-Scarspixel_experience_kernel_xiaomi_sm8250_kernelSUPixel Experience Kernel 4.19.282 for Xiaomi Redmi K40S / Xiaomi Poco F4 (munch)
exerMiyo TokuSony Tama (akari, apollo, aurora, akatsuki) (Linux 4.9)
likkaiQuicksilver KernelXiaomi 11 Lite 5G NE (lisa)
awakened1712android_kernel_samsung_exynos9820Samsung S10/N10
awakened1712android_kernel_oneplus_sm8350Oneplus 9/9Pro
siimsekLightning KernelXiaomi Redmi Note 8/8T (ginkgo/willow)
GiovanYCringekernel_a50Galaxy A50 (A505f,fn,g,gn,gt)
Asriadi Rahimandroid_kernel_google_wahooGoogle Pixel 2/2XL
Flameandroid_kernel_xiaomi_sm8250POCO F3/Redmi K40/Mi 11X (alioth)
DawfukFRkernel_oneplus_sm8250 (Stellaris-Kernel)Oneplus 8 (Instantnoodle), Oneplus 8T (Kebab), Oneplus 8 Pro (Instantnoodlep), Oneplus 9R (lemonades) [msm-4.19]
Tejas Singhkernel_xiaomi_ginkgo (Cuh Kernel)Xiaomi Redmi Note 8/8T (ginkgo/willow)
Coconutatandroid_kernel_xiaomi_ruby_expRedmi Note 12 Pro / Pro+ For MIUI 14
Coconutatandroid_kernel_xiaomi_sdm845_expXiaomi Mi 8 Pro(UD)[equuleus]/Explorer Edition[ursa]
Abdul Wahid Khankernel_xiaomi_begoniaRedmi Note 8 Pro (Begonia/Begoniain)
Abdul Wahid Khankernel_xiaomi_marbleXiaomi Poco F5 / Redmi Note 12 Turbo (Marble)
zfdx123kernel_xiaomi_aliothRedmi K40 (alioth)
wxt1221android_kernel_oneplus_sdm845Oneplus 6 (enchilada) | Oneplus 6T (fajita) with no Retrofit Dynamic Partitions
zharzinhooKernel-Oriente-GuampMoto g9 play
hc841NovaCore_kernelMoto g9 play
zlm324android_kernel_xiaomi_msm8998_ksua lineage kernel forked from LineageOS official repository, version 4.4.302, added ksu.
zlm324android_kernel_xiaomi_msm8953_ksuXiaomi 5X (tiffany)
Vincent4440android_kernel_xiaomi_sm8250Poco F4: munch
zharzinhooandroid_kernel_motorola_cebuMoto g9 power
tiandoufayaleandroid_kernel_samsung_sm8250Samsung Tab S7 WIFI(T870)
starmoeandroid_kernel_oneplus_msm8998Oneplus 5/5T, kernel version 4.4.302
supechickenkernel_xiaomi_sm8350 Xiaomi 11T Pro (vili)
Vaz15kkernelSU-A70qSamsung Galaxy A70 ( A705MN / A705FN )
whyakariandroid_kernel_xiaomi_ginkgonon-GKI ginkgo
wulan17android_kernel_xiaomi_mt6765Redmi 6/6A (cereus/cactus)
officialputuidandroid_kernel_realme_mt6785Realme 6/6i(Indian)/6s/7/Narzo/Narzo 20 Pro/Narzo 30 4G (RM6785)

Rilis Dibawah Lisensi GPL3.

- +
Skip to content

Perangkat Yang Didukung Tidak Resmi

:::peringatan

di halaman ini, terdapat kernel untuk perangkat non-GKI yang mendukung KernelSU yang dikelola oleh pengembang lain.

:::

:::peringatan

Halaman ini hanya untuk Anda yang ingin menemukan kode sumber yang sesuai dengan perangkat Anda, itu BUKAN berarti kode sumber telah ditinjau oleh KernelSU Developers. Anda harus menggunakannya dengan risiko Anda sendiri.

:::

PengelolaRepositoryPerangkat yang didukung
diphonsD8G_Kernel_oxygenPoco F3: alioth | Poco F4: munch | MI10T/PRO: Apollo
diphonsD8G_Kernel_SM8150Poco X3 Pro: Vayu | Bhima
diphonsD8G_Kernel_SDM845Poco F1 | MI8 | MiMix2S
diphonsD8G_Kernel_SM8350Vili
diphonsD8G_Kernel_MarblePoco F5 | Redmi Note 12 Turbo | Marble
th1nhhdkandroid_kernel_sony_sm8250-kernelsuSony Xperia 1 II | Sony Xperia 5 II
akash07knexus_kernel_xiaomi_sm8250Poco F4: munch
HMTheBoy154Darkmatter-kernelGeneric x86_64 devices running Android-x86
Asuka-mioandroid_kernel_xiaomi_casMi 10 Ultra: cas
xiaoleGunMiku_kernel_xiaomi_waynewayne
YamazakuraHkernel_xiaomi_cepheuscepheus for pixel experience
SakuraNotStupidandroid_kernel_xiaomi_sdm710Xiaomi MI 8 SE(sirius/xmsirius)
Aquarius223(paper)android_kernel_xiaomi_msm8998MI 6 (sagit) and MIX 2 (chiron) for LineageOS
SlackerStateandroid_kernel_xiaomi_sm6150Redmi K30 4G (phoenix)
RooGhz720kernel_xiaomi_sm6150REDMI NOTE 10 PRO (sweet)
OnlyTomInSecondandroid_kernel_xiaomi_sdm845Mi 8 (dipper) for LineageOS
Rohail33RealKing KernelApollo(Redmi K30S Ultra/Mi 10T/Mi 10T Pro),Alioth(Redmi K40/POCO F3/Mi 11X),Munch(Redmi K40S/POCO F4), both MIUI and AOSP.
lateautumn233android_kernel_oneplus_sm8250OnePlus 8 Serials
Sreeshankar KNeverSettle KernelOnePlus Nord (avicii)
Molyuuneko_kernel_xiaomi_gauguinRedmi Note 9 Pro/ Mi 10T Lite/ Mi 10i
guh0613android_kernel_oppo_sm8150OPPO Reno Ace (OP4A89)
LeviMarvinandroid_kernel_xiaomi_aliothRedmi K40 / POCO F3
cibimokernel_xiaomi_raphael_ksuXiaomi Redmi K20 Pro / Mi 9T Pro (raphael)
EndCreditskernel_xiaomi_sm7250Redmi K30 5G ( picasso ) , Redmi K30i 5G ( picasso_48m)
msnxandroid-msm-coral-4.14-android13Pixel 4 XL
SoDebugkernel_xiaomi_raphaelXiaomi Redmi K20 Pro / Mi 9T Pro (raphael)
H1mJTkernel_realme_RMX1901Realme X (RMX1901/RMX1901CN)
SonalSingh18android_kernel_xiaomi_sm6250Miatoll [curtana, excalibur, gram, joyeuse]
RooGhz720kernel_xiaomi_lavenderRedmi Note 7 (Lavender)
JunASAKAkernel_google_msm-4.9_KernelSUGoogle Pixel 3a & 3a XL (sargo & bonito)
RooGhz720kernel_asus_X01BDAsus Zenfone Max Pro M1/M2
Evans Mikekernel_xiaomi_raphael_bool-xXiaomi Redmi K20 Pro / Mi 9T Pro (raphael)
easterNdayKSU_Thyme_BuildBotXiaomi 10S
tedomi2705kernel_xiaomi_sdm660Xiaomi Redmi Note 6 Pro (tulip)
RyuujiXandroid_kernel_asus_sdm660-4.19Asus Zenfone Max Pro M1 (X00TD)/ M2 (X01BD) (Linux 4.19)
liqidecgandroid_kernel_xiaomi_sdm710Mi 8 SE (sirius) (Linux 4.9)
Abdelhay-Aliandroid_kernel_huawei_hi6250Huawei P20 lite (hi6250) (Linux 4.9)
reocatandroid_kernel_nokia_sdm660_ksuNokia 6.1 (2018)
Tuan Anhkernel_nokia_msm8998Nokia 8 (Repartitioned)/8 Sirocco (NLA/A1N)
bggRGjQaUbCoEandroid_kernel_samsung_sm8250Samsung Galaxy S20+ 5G (y2q)
SOVIET-ANDROIDkernel_xiaomi_raphaelXK20 Pro Raphael DSP & A only SAR support
dabao1955android_kernel_OPPO_PEQM00MediaTek devices in the OPPO Reno series on ColorOS13.x
Aflaungosandroid_kernel_motorola_msm8998Moto G6 Plus (evert)
TheNoFacekernel_oneplus_msm8998OnePlus 5/5T (cheeseburger/dumpling)
Nipin NA (Joker-V2)Xcalibur kernel (violet)Xiaomi Redmi Note 7 Pro (violet)
Sr-Hankernel_xiaomi_mojitoRedmi Note 10 (Sunny/Mojito)
dabao1955android_kernel_OPPO_OP4ED5OPPO Reno6 on ColorOS11.x/ColorOS12.x,kernel version4.14.186+
rushiranpiseandroid_kernel_samsung_exynos9610_mintKernel 4.14.194 exynos9610 Non-GKI Device, Added KernelSu using manual method
CN-Scarspixel_experience_kernel_xiaomi_sm8250_kernelSUPixel Experience Kernel 4.19.282 for Xiaomi Redmi K40S / Xiaomi Poco F4 (munch)
exerMiyo TokuSony Tama (akari, apollo, aurora, akatsuki) (Linux 4.9)
likkaiQuicksilver KernelXiaomi 11 Lite 5G NE (lisa)
awakened1712android_kernel_samsung_exynos9820Samsung S10/N10
awakened1712android_kernel_oneplus_sm8350Oneplus 9/9Pro
siimsekLightning KernelXiaomi Redmi Note 8/8T (ginkgo/willow)
GiovanYCringekernel_a50Galaxy A50 (A505f,fn,g,gn,gt)
Asriadi Rahimandroid_kernel_google_wahooGoogle Pixel 2/2XL
Flameandroid_kernel_xiaomi_sm8250POCO F3/Redmi K40/Mi 11X (alioth)
DawfukFRkernel_oneplus_sm8250 (Stellaris-Kernel)Oneplus 8 (Instantnoodle), Oneplus 8T (Kebab), Oneplus 8 Pro (Instantnoodlep), Oneplus 9R (lemonades) [msm-4.19]
Tejas Singhkernel_xiaomi_ginkgo (Cuh Kernel)Xiaomi Redmi Note 8/8T (ginkgo/willow)
Coconutatandroid_kernel_xiaomi_ruby_expRedmi Note 12 Pro / Pro+ For MIUI 14
Coconutatandroid_kernel_xiaomi_sdm845_expXiaomi Mi 8 Pro(UD)[equuleus]/Explorer Edition[ursa]
Abdul Wahid Khankernel_xiaomi_begoniaRedmi Note 8 Pro (Begonia/Begoniain)
Abdul Wahid Khankernel_xiaomi_marbleXiaomi Poco F5 / Redmi Note 12 Turbo (Marble)
zfdx123kernel_xiaomi_aliothRedmi K40 (alioth)
wxt1221android_kernel_oneplus_sdm845Oneplus 6 (enchilada) | Oneplus 6T (fajita) with no Retrofit Dynamic Partitions
zharzinhooKernel-Oriente-GuampMoto g9 play
hc841NovaCore_kernelMoto g9 play
zlm324android_kernel_xiaomi_msm8998_ksua lineage kernel forked from LineageOS official repository, version 4.4.302, added ksu.
zlm324android_kernel_xiaomi_msm8953_ksuXiaomi 5X (tiffany)
Vincent4440android_kernel_xiaomi_sm8250Poco F4: munch
zharzinhooandroid_kernel_motorola_cebuMoto g9 power
tiandoufayaleandroid_kernel_samsung_sm8250Samsung Tab S7 WIFI(T870)
starmoeandroid_kernel_oneplus_msm8998Oneplus 5/5T, kernel version 4.4.302
supechickenkernel_xiaomi_sm8350 Xiaomi 11T Pro (vili)
Vaz15kkernelSU-A70qSamsung Galaxy A70 ( A705MN / A705FN )
whyakariandroid_kernel_xiaomi_ginkgonon-GKI ginkgo
wulan17android_kernel_xiaomi_mt6765Redmi 6/6A (cereus/cactus)
officialputuidandroid_kernel_realme_mt6785Realme 6/6i(Indian)/6s/7/Narzo/Narzo 20 Pro/Narzo 30 4G (RM6785)

Rilis Dibawah Lisensi GPL3.

+ \ No newline at end of file diff --git a/id_ID/guide/what-is-kernelsu.html b/id_ID/guide/what-is-kernelsu.html index 7cb4d831b4e4..9eaf0573dbc8 100644 --- a/id_ID/guide/what-is-kernelsu.html +++ b/id_ID/guide/what-is-kernelsu.html @@ -5,18 +5,19 @@ Apa itu KernelSU? | KernelSU - - + + + - - - - + + + + + -
Skip to content
On this page

Apa itu KernelSU?

KernelSU adalah solusi root untuk perangkat GKI Android, ia bekerja dalam mode kernel dan memberikan izin root ke aplikasi userspace secara langsung di ruang kernel.

Fitur

Fitur utama dari KernelSU adalah berbasis kernel. KernelSU bekerja dalam mode kernel, sehingga dapat menyediakan antarmuka kernel yang belum pernah kita miliki sebelumnya. Sebagai contoh, kita dapat menambahkan breakpoint perangkat keras ke proses apa pun dalam mode kernel; Kita dapat mengakses memori fisik dari proses apa pun tanpa diketahui oleh siapa pun; Kita dapat mencegat syscall apa pun di ruang kernel; dll.

Dan juga, KernelSU menyediakan sistem modul melalui overlayfs, yang memungkinkan Anda untuk memuat plugin kustom Anda ke dalam sistem. KernelSU juga menyediakan mekanisme untuk memodifikasi berkas-berkas pada partisi /system.

Bagaimana cara menggunakannya

Silakan merujuk ke: Installation

Bagaimana cara men-buildnya

How to build

Diskusi

Rilis Dibawah Lisensi GPL3.

- +
Skip to content

Apa itu KernelSU?

KernelSU adalah solusi root untuk perangkat GKI Android, ia bekerja dalam mode kernel dan memberikan izin root ke aplikasi userspace secara langsung di ruang kernel.

Fitur

Fitur utama dari KernelSU adalah berbasis kernel. KernelSU bekerja dalam mode kernel, sehingga dapat menyediakan antarmuka kernel yang belum pernah kita miliki sebelumnya. Sebagai contoh, kita dapat menambahkan breakpoint perangkat keras ke proses apa pun dalam mode kernel; Kita dapat mengakses memori fisik dari proses apa pun tanpa diketahui oleh siapa pun; Kita dapat mencegat syscall apa pun di ruang kernel; dll.

Dan juga, KernelSU menyediakan sistem modul melalui overlayfs, yang memungkinkan Anda untuk memuat plugin kustom Anda ke dalam sistem. KernelSU juga menyediakan mekanisme untuk memodifikasi berkas-berkas pada partisi /system.

Bagaimana cara menggunakannya

Silakan merujuk ke: Installation

Bagaimana cara men-buildnya

How to build

Diskusi

Rilis Dibawah Lisensi GPL3.

+ \ No newline at end of file diff --git a/id_ID/index.html b/id_ID/index.html index 3a5b2c32e3f7..c258ce681c97 100644 --- a/id_ID/index.html +++ b/id_ID/index.html @@ -5,18 +5,19 @@ Sebuah solusi root kernel-based untuk Android | KernelSU - - + + + - - - - + + + + + -
Skip to content

KernelSU

Sebuah solusi root kernel-based untuk Android

KernelSU

Kernel-based

KernelSU bekerja dalam mode Linux kernel, dan mempunyai kelebihan diatas aplikasi userspace.

Kontrol akses daftar putih

Hanya aplikasi yang diberikan izin root yang bisa mengakses `su`, aplikasi lain tidak bisa mengakses su.

Dukungan modul

KernelSU mendukung modifikasi /system tanpa-sistem dalam overlayfs, bahkan bisa membuat system dapat ditulis.

Sumber terbuka

KernelSU adalah projek sumber terbuka dibawah lisensi GPL-3.

Rilis Dibawah Lisensi GPL3.

- +
Skip to content

KernelSU

Sebuah solusi root kernel-based untuk Android

KernelSU

Rilis Dibawah Lisensi GPL3.

+ \ No newline at end of file diff --git a/index.html b/index.html index b80163462847..b5ce263dac03 100644 --- a/index.html +++ b/index.html @@ -5,18 +5,19 @@ A kernel-based root solution for Android | KernelSU - - + + + - - - - + + + + + -
Skip to content

KernelSU

A kernel-based root solution for Android

KernelSU

Kernel-based

As the name suggests, KernelSU works under the Linux kernel giving it more control over userspace applications.

Root Access Control

Only permitted apps may access or see `su`, all other apps are not aware of this.

Customizable Root Privileges

KernelSU allows customization of `su`'s uid, gid, groups, capabilities, and SELinux rules, locking up root privileges.

Modules

Modules may modify /system systemlessly using overlayfs enabling great power.

Released under the GPL3 License.

- +
Skip to content

KernelSU

A kernel-based root solution for Android

KernelSU

Released under the GPL3 License.

+ \ No newline at end of file diff --git a/ja_JP/guide/difference-with-magisk.html b/ja_JP/guide/difference-with-magisk.html index cc8a41f74bee..533e0b1d044a 100644 --- a/ja_JP/guide/difference-with-magisk.html +++ b/ja_JP/guide/difference-with-magisk.html @@ -5,18 +5,19 @@ Magisk との違い | KernelSU - - + + + - - - - + + + + + -
Skip to content
On this page

Magisk との違い

KernelSU モジュールと Magisk モジュールには多くの共通点がありますが、実装の仕組みが全く異なるため、必然的にいくつかの相違点が存在します。Magisk と KernelSU の両方でモジュールを動作させたい場合、これらの違いを理解する必要があります。

似ているところ

  • モジュールファイルの形式:どちらもzip形式でモジュールを整理しており、モジュールの形式はほぼ同じです。
  • モジュールのインストールディレクトリ:どちらも /data/adb/modules に配置されます。
  • システムレス:どちらもモジュールによるシステムレスな方法で /system を変更できます。
  • post-fs-data.sh: 実行時間と意味は全く同じです。
  • service.sh: 実行時間と意味は全く同じです。
  • system.prop:全く同じです。
  • sepolicy.rule:全く同じです。
  • BusyBox:スクリプトは BusyBox で実行され、どちらの場合も「スタンドアロンモード」が有効です。

違うところ

違いを理解する前に、モジュールが KernelSU で動作しているか Magisk で動作しているかを区別する方法を知っておく必要があります。環境変数 KSU を使うとモジュールスクリプトを実行できるすべての場所 (customize.sh, post-fs-data.sh, service.sh) で区別できます。KernelSU では、この環境変数に true が設定されます。

以下は違いです:

  • KernelSU モジュールは、リカバリーモードではインストールできません。
  • KernelSU モジュールには Zygisk のサポートが組み込まれていません(ただしZygiskNextを使うと Zygisk モジュールを使用できます)。
  • KernelSU モジュールにおけるファイルの置換や削除の方法は、Magisk とは全く異なります。KernelSU は .replace メソッドをサポートしていません。その代わり、mknod filename c 0 0 で同名のファイルを作成し、対応するファイルを削除する必要があります。
  • BusyBox 用のディレクトリが違います。KernelSU の組み込み BusyBox は /data/adb/ksu/bin/busybox に、Magisk では /data/adb/magisk/busybox に配置されます。これは KernelSU の内部動作であり、将来的に変更される可能性があることに注意してください!
  • KernelSU は .replace ファイルをサポートしていません。しかし、KernelSU はファイルやフォルダを削除したり置き換えたりするための REMOVEREPLACE 変数をサポートしています。

GPL3 ライセンスでリリースされています。

- +
Skip to content

Magisk との違い

KernelSU モジュールと Magisk モジュールには多くの共通点がありますが、実装の仕組みが全く異なるため、必然的にいくつかの相違点が存在します。Magisk と KernelSU の両方でモジュールを動作させたい場合、これらの違いを理解する必要があります。

似ているところ

  • モジュールファイルの形式:どちらもzip形式でモジュールを整理しており、モジュールの形式はほぼ同じです。
  • モジュールのインストールディレクトリ:どちらも /data/adb/modules に配置されます。
  • システムレス:どちらもモジュールによるシステムレスな方法で /system を変更できます。
  • post-fs-data.sh: 実行時間と意味は全く同じです。
  • service.sh: 実行時間と意味は全く同じです。
  • system.prop:全く同じです。
  • sepolicy.rule:全く同じです。
  • BusyBox:スクリプトは BusyBox で実行され、どちらの場合も「スタンドアロンモード」が有効です。

違うところ

違いを理解する前に、モジュールが KernelSU で動作しているか Magisk で動作しているかを区別する方法を知っておく必要があります。環境変数 KSU を使うとモジュールスクリプトを実行できるすべての場所 (customize.sh, post-fs-data.sh, service.sh) で区別できます。KernelSU では、この環境変数に true が設定されます。

以下は違いです:

  • KernelSU モジュールは、リカバリーモードではインストールできません。
  • KernelSU モジュールには Zygisk のサポートが組み込まれていません(ただしZygiskNextを使うと Zygisk モジュールを使用できます)。
  • KernelSU モジュールにおけるファイルの置換や削除の方法は、Magisk とは全く異なります。KernelSU は .replace メソッドをサポートしていません。その代わり、mknod filename c 0 0 で同名のファイルを作成し、対応するファイルを削除する必要があります。
  • BusyBox 用のディレクトリが違います。KernelSU の組み込み BusyBox は /data/adb/ksu/bin/busybox に、Magisk では /data/adb/magisk/busybox に配置されます。これは KernelSU の内部動作であり、将来的に変更される可能性があることに注意してください!
  • KernelSU は .replace ファイルをサポートしていません。しかし、KernelSU はファイルやフォルダを削除したり置き換えたりするための REMOVEREPLACE 変数をサポートしています。

GPL3 ライセンスでリリースされています。

+ \ No newline at end of file diff --git a/ja_JP/guide/faq.html b/ja_JP/guide/faq.html index 748130558aaf..503841dde905 100644 --- a/ja_JP/guide/faq.html +++ b/ja_JP/guide/faq.html @@ -5,18 +5,19 @@ よくある質問 | KernelSU - - + + + - - - - + + + + + -
Skip to content
On this page

よくある質問

私のデバイスは KernelSU に対応していますか?

まず、お使いのデバイスがブートローダーのロックを解除できる必要があります。もしできないのであれば、サポート外です。

もし KernelSU アプリで「非対応」と表示されたら、そのデバイスは最初からサポートされていないことになりますが、カーネルソースをビルドして KernelSU を組み込むか、非公式の対応デバイスで動作させることが可能です。

KernelSU を使うにはブートローダーのロックを解除する必要がありますか?

はい。

KernelSU はモジュールに対応していますか?

はい。ただし初期バージョンであるためバグがある可能性があります。安定するのをお待ちください。

KernelSU は Xposed に対応していますか?

はい。DreamlandTaiChi が動作します。LSPosed については、ZygiskNext を使うと動作するようにできます。

KernelSU は Zygisk に対応していますか?

KernelSU は Zygisk サポートを内蔵していません。ZygiskNext を使ってください。

KernelSU は Magisk と互換性がありますか?

KernelSU のモジュールシステムは Magisk のマジックマウントと競合しており、KernelSU で有効になっているモジュールがある場合、Magisk 全体が動作しなくなります。

しかし、KernelSU の su だけを使うのであれば、Magisk とうまく連携することができます。KernelSU は kernel を、Magisk は ramdisk を修正するため、両者は共存できます。

KernelSU は Magisk の代わりになりますか?

私たちはそうは思っていませんし、それが目標でもありません。Magisk はユーザ空間の root ソリューションとして十分であり、長く使われ続けるでしょう。KernelSU の目標は、ユーザーにカーネルインターフェースを提供することであり、Magisk の代用ではありません。

KernelSU は GKI 以外のデバイスに対応できますか?

可能です。ただしカーネルソースをダウンロードし、KernelSU をソースツリーに統合して、自分でカーネルをビルドする必要があります。

KernelSU は Android 12 以下のデバイスに対応できますか?

KernelSU の互換性に影響を与えるのはデバイスのカーネルであり、Android のバージョンとは無関係です。唯一の制限は、Android 12 で発売されたデバイスはカーネル5.10以上(GKI デバイス)でなければならないことです:

  1. Android 12 をプリインストールして発売された端末は対応しているはずです。
  2. カーネルが古い端末(一部の Android 12 端末はカーネルも古い)は対応可能ですが、カーネルは自分でビルドする必要があります。

KernelSU は古いカーネルに対応できますか?

KernelSU は現在カーネル4.14にバックポートされていますが、それ以前のカーネルについては手動でバックポートする必要があります。プルリクエスト歓迎です!

古いカーネルに KernelSU を組み込むには?

ガイド を参考にしてください。

Android のバージョンが13なのに、カーネルは「android12-5.10」と表示されるのはなぜ?

カーネルのバージョンは Android のバージョンと関係ありません。カーネルを書き込む必要がある場合は、常にカーネルのバージョンを使用してください。Android のバージョンはそれほど重要ではありません。

KernelSU に-mount-master/global のマウント名前空間はありますか?

今はまだありませんが(将来的にはあるかもしれません)、グローバルマウントの名前空間に手動で切り替える方法は、以下のようにたくさんあります:

  1. nsenter -t 1 -m sh でシェルをグローバル名前空間にします。
  2. nsenter --mount=/proc/1/ns/mnt を実行したいコマンドに追加すればグローバル名前空間で実行されます。 KernelSU は このような使い方 もできます。

GKI 1.0 なのですが、使えますか?

GKI1 は GKI2 と全く異なるため、カーネルは自分でビルドする必要があります。

GPL3 ライセンスでリリースされています。

- +
Skip to content

よくある質問

私のデバイスは KernelSU に対応していますか?

まず、お使いのデバイスがブートローダーのロックを解除できる必要があります。もしできないのであれば、サポート外です。

もし KernelSU アプリで「非対応」と表示されたら、そのデバイスは最初からサポートされていないことになりますが、カーネルソースをビルドして KernelSU を組み込むか、非公式の対応デバイスで動作させることが可能です。

KernelSU を使うにはブートローダーのロックを解除する必要がありますか?

はい。

KernelSU はモジュールに対応していますか?

はい。ただし初期バージョンであるためバグがある可能性があります。安定するのをお待ちください。

KernelSU は Xposed に対応していますか?

はい。DreamlandTaiChi が動作します。LSPosed については、ZygiskNext を使うと動作するようにできます。

KernelSU は Zygisk に対応していますか?

KernelSU は Zygisk サポートを内蔵していません。ZygiskNext を使ってください。

KernelSU は Magisk と互換性がありますか?

KernelSU のモジュールシステムは Magisk のマジックマウントと競合しており、KernelSU で有効になっているモジュールがある場合、Magisk 全体が動作しなくなります。

しかし、KernelSU の su だけを使うのであれば、Magisk とうまく連携することができます。KernelSU は kernel を、Magisk は ramdisk を修正するため、両者は共存できます。

KernelSU は Magisk の代わりになりますか?

私たちはそうは思っていませんし、それが目標でもありません。Magisk はユーザ空間の root ソリューションとして十分であり、長く使われ続けるでしょう。KernelSU の目標は、ユーザーにカーネルインターフェースを提供することであり、Magisk の代用ではありません。

KernelSU は GKI 以外のデバイスに対応できますか?

可能です。ただしカーネルソースをダウンロードし、KernelSU をソースツリーに統合して、自分でカーネルをビルドする必要があります。

KernelSU は Android 12 以下のデバイスに対応できますか?

KernelSU の互換性に影響を与えるのはデバイスのカーネルであり、Android のバージョンとは無関係です。唯一の制限は、Android 12 で発売されたデバイスはカーネル5.10以上(GKI デバイス)でなければならないことです:

  1. Android 12 をプリインストールして発売された端末は対応しているはずです。
  2. カーネルが古い端末(一部の Android 12 端末はカーネルも古い)は対応可能ですが、カーネルは自分でビルドする必要があります。

KernelSU は古いカーネルに対応できますか?

KernelSU は現在カーネル4.14にバックポートされていますが、それ以前のカーネルについては手動でバックポートする必要があります。プルリクエスト歓迎です!

古いカーネルに KernelSU を組み込むには?

ガイド を参考にしてください。

Android のバージョンが13なのに、カーネルは「android12-5.10」と表示されるのはなぜ?

カーネルのバージョンは Android のバージョンと関係ありません。カーネルを書き込む必要がある場合は、常にカーネルのバージョンを使用してください。Android のバージョンはそれほど重要ではありません。

KernelSU に-mount-master/global のマウント名前空間はありますか?

今はまだありませんが(将来的にはあるかもしれません)、グローバルマウントの名前空間に手動で切り替える方法は、以下のようにたくさんあります:

  1. nsenter -t 1 -m sh でシェルをグローバル名前空間にします。
  2. nsenter --mount=/proc/1/ns/mnt を実行したいコマンドに追加すればグローバル名前空間で実行されます。 KernelSU は このような使い方 もできます。

GKI 1.0 なのですが、使えますか?

GKI1 は GKI2 と全く異なるため、カーネルは自分でビルドする必要があります。

GPL3 ライセンスでリリースされています。

+ \ No newline at end of file diff --git a/ja_JP/guide/hidden-features.html b/ja_JP/guide/hidden-features.html index 93c5d29d484d..1980d7250817 100644 --- a/ja_JP/guide/hidden-features.html +++ b/ja_JP/guide/hidden-features.html @@ -5,18 +5,19 @@ 隠し機能 | KernelSU - - + + + - - - - + + + + + -
Skip to content
On this page

隠し機能

.ksurc

デフォルトでは /system/bin/sh/system/etc/mkshrc を読み込みます。

/data/adb/ksu/.ksurc ファイルを作成することで、カスタマイズした rc ファイルを su に読み込ませられます。

GPL3 ライセンスでリリースされています。

- +
Skip to content

隠し機能

.ksurc

デフォルトでは /system/bin/sh/system/etc/mkshrc を読み込みます。

/data/adb/ksu/.ksurc ファイルを作成することで、カスタマイズした rc ファイルを su に読み込ませられます。

GPL3 ライセンスでリリースされています。

+ \ No newline at end of file diff --git a/ja_JP/guide/installation.html b/ja_JP/guide/installation.html index 20356c327f27..96c601f92454 100644 --- a/ja_JP/guide/installation.html +++ b/ja_JP/guide/installation.html @@ -5,20 +5,23 @@ インストール | KernelSU - - + + + - - - - + + + + + -
Skip to content
On this page

インストール

デバイスが対応しているか確認する

GitHub Releases または Coolapk market から KernelSU Manager アプリをダウンロードし、お使いのデバイスにインストールしてください。

  • アプリが「非対応」と表示した場合は、自分でカーネルをコンパイルする必要があるという意味です。KernelSU は書き込むためのブートイメージを提供しません。
  • アプリが「未インストール」と表示した場合、お使いのデバイスは KernelSU に対応しています。

ヒント

非対応と表示されているデバイスについては、非公式の対応デバイスであればご自身でカーネルをビルドできます。

純正の boot.img をバックアップ

書き込む前に、まず純正の boot.img をバックアップする必要があります。ブートループが発生した場合は、fastboot を使用して純正のブートイメージを書き込むことでいつでもシステムを復旧できます。

警告

書き込みによりデータ損失を引き起こす可能性があります。次のステップに進む前に、このステップを必ず行うようにしてください!また、可能であればすべてのデータをバックアップしてください。

必要な知識

ADB と fastboot

このチュートリアルでは、デフォルトで ADB と fastboot のツールを使用します。ご存じない方は、まず検索エンジンを使って勉強されることをおすすめします。

KMI

同じ Kernel Module Interface (KMI) のカーネルバージョンは互換性があります。これが GKI の「汎用」という意味です。逆に言えば KMI が異なればカーネルには互換性がなく、お使いのデバイスと異なる KMI のカーネルイメージを書き込むと、ブートループが発生する場合があります。

具体的には GKI デバイスの場合、カーネルバージョンの形式は以下のようになります:

txt
KernelRelease :=
-Version.PatchLevel.SubLevel-AndroidRelease-KmiGeneration-suffix
-w      .x         .y       -zzz           -k            -something

w.x-zzz-k は KMI のバージョンです。例えば、デバイスのカーネルバージョンが 5.10.101-android12-9-g30979850fc20 である場合、その KMIは 5.10-android12-9 であり、理論的には他の KMI カーネルでも正常に起動できます。

ヒント

カーネルバージョンの SubLevel は、KMI の一部ではないことに注意してください。5.10.101-android12-9-g30979850fc205.10.137-android12-9-g30979850fc20 と同じ KMI を持っているということになります。

Kernel バージョンと Android バージョンの違い

注意: カーネルバージョンと Android バージョンは必ずしも同じではありません

カーネルのバージョンは「android12-5.10.101」なのに、Android システムのバージョンは「Android 13」などとなっている場合、驚かないでください。Linux カーネルのバージョン番号は、必ずしもデバイスの出荷時にプリインストールされている Android システムのバージョンと一致していません。Android システムが後でアップグレードされた場合、一般的にはカーネルのバージョンは変更されません。書き込む際は、必ずカーネルバージョンを参照してください!!!

インストール方法

KernelSU のインストール方法はいくつかあり、それぞれ適したシーンが異なりますので、必要に応じて選択してください。

  1. カスタムリカバリー(TWRPなど)でインストールする
  2. Franco Kernel Manager などのカーネル管理アプリでインストールする
  3. KernelSU が提供する boot.img を使用し、fastboot でインストールする
  4. boot.img を手動でパッチしてインストールする

カスタムリカバリーでインストール

前提条件:デバイスに TWRP などのカスタムリカバリーがあること。ない場合、または公式リカバリーしかない場合は他の方法を使用してください。

手順:

  1. KernelSUのリリースページから、お使いのデバイスのバージョンにあった AnyKernel3 で始まる ZIP パッケージをダウンロードします。例えば、デバイスのカーネルのバージョンがandroid12-5.10. 66の場合、AnyKernel3-android12-5.10.66_yyyy-MM.zip(yyyyは年、MMは月)のファイルをダウンロードします。
  2. デバイスを TWRP へ再起動します。
  3. adb を使用して AnyKernel3-*.zip をデバイスの /sdcard に入れ、TWRP GUI でインストールを選択します。または直接adb sideload AnyKernel-*.zip でインストールできます。

この方法は TWRP を使用できるならどのようなインストール(初期インストールやその後のアップグレード)にも適しています。

カーネル管理アプリでインストール

前提条件:お使いのデバイスが root 化されている必要があります。例えば、Magisk をインストールして root を取得した場合、または古いバージョンの KernelSU をインストールしており、別のバージョンの KernelSU にアップグレードする必要がある場合などです。お使いのデバイスが root 化されていない場合、他の方法をお試しください。

手順:

  1. AnyKernel3 ZIP をダウンロードします。ダウンロード方法は、「カスタムリカバリーでインストール」を参照してください。
  2. カーネル管理アプリを開き、AnyKernel3 の ZIP をインストールします。

カーネル管理アプリは以下のようなものが人気です:

  1. Kernel Flasher
  2. Franco Kernel Manager
  3. Ex Kernel Manager

この方法は KernelSU をアップグレードするときに便利で、パソコンがなくてもできます。(まずはバックアップしてください!)

KernelSU が提供する boot.img を使用してインストール

この方法は TWRP や root 権限を必要としないので、KernelSU を初めてインストールする場合に適しています。

正しい boot.img を見つける

KernelSU では、GKI デバイス用の汎用 boot.img を提供しています。デバイスの boot パーティションに boot.img をフラッシュする必要があります。

boot.img は、GitHub Release からダウンロードできます。例えば、あなたのデバイスがカーネル android12-5.10.101 の場合、android-5.10.101_yyyy-MM.boot-<format>.imgをダウンロードする必要があります。(KMI を同じにしてください!)。

<format>は純正 boot.img のカーネル圧縮形式を指します。純正の boot.img のカーネル圧縮形式を確認してください。間違った圧縮形式を使うと、ブートループするかもしれません。

情報

  1. magiskboot を使えば、元のブートの圧縮形式を知ることができます。もちろん、あなたのデバイスと同じモデルを持つ、より経験豊富な他の人にも聞くこともできます。また、カーネルの圧縮形式は通常変更されないので、ある圧縮形式でうまく起動した場合、後でその形式を試すことも可能です。
  2. Xiaomi デバイスでは通常 gz無圧縮が使われます。
  3. Pixel デバイスでは以下の手順に従ってください。

boot.img をデバイスに書き込む

adb でデバイスを接続し、adb reboot bootloader で fastboot モードにし、このコマンドで KernelSU を書き込んでください:

sh
fastboot flash boot boot.img

情報

デバイスが fastboot boot をサポートしている場合、まず fastboot boot.img を使えば書き込みせずにシステムを起動できます。予期せぬことが起こった場合は、もう一度再起動して起動してください。

再起動

書き込みが完了したら、デバイスを再起動します:

sh
fastboot reboot

boot.img を手動でパッチ

デバイスによっては、boot.img のフォーマットが lz4 でない、gz である、無圧縮であるなど、あまり一般的でないことがあります。最も典型的なのは Pixel で、boot.img フォーマットは lz4_legacy 圧縮、RAM ディスクは gzlz4_legacy 圧縮です。この時、KernelSU が提供した boot.img を直接書き込むとデバイスが起動できなくなる場合があります。その場合は手動で boot.img に対してパッチしてください。

パッチ方式は一般的に2種類あります:

  1. Android-Image-Kitchen
  2. magiskboot

このうち、Android-Image-Kitchen は PC での操作に適しており、magiskboot はデバイスとの連携が必要です。

準備

  1. お使いのデバイスの純正 boot.img を入手します。デバイスメーカーから入手できます。payload-dumper-goが必要かもしれません。
  2. お使いのデバイスの KMI バージョンに合った、KernelSU が提供する AnyKernel3 の ZIP ファイルをダウンロードします(カスタムリカバリーでインストールを参照してください)。
  3. AnyKernel3 パッケージを展開し、KernelSU のカーネルファイルである Image ファイルを取得します。

Android-Image-Kitchen を使う

  1. Android-Image-Kitchen を PC にダウンロードします。
  2. 純正 boot.img を Android-Image-Kitchen のルートフォルダに入れます。
  3. Android-Image-Kitchen のルートディレクトリで ./unpackimg.sh boot.img を実行して、boot.imgを展開します。
  4. split_img ディレクトリの boot.img-kernel を AnyKernel3 から展開した Image に置き換えます(boot.img-kernelに名前が変わっていることに注意してください)。
  5. Android-Image-Kitchen のルートディレクトリで ./repackimg.sh を実行すると、 image-new.img というファイルが生成されます。

magiskboot を使う

  1. 最新の Magisk をリリースページからダウンロードしてください。
  2. Magisk-*.apk を Magisk-vesion.zip に名前を変更して展開してください。
  3. Magisk-v25.2/lib/arm64-v8a/libmagiskboot.soを adb でデバイスに転送します:adb push Magisk-v25.2/lib/arm64-v8a/libmagiskboot.so /data/local/tmp/magiskboot
  4. 純正 boot.img と AnyKernel3 の中の Image をデバイスに転送します。
  5. adb shell に入り、cd /data/local/tmp/ し、chmod +x magiskboot を実行します。
  6. adb shell に入り、cd /data/local/tmp/ し、./magiskboot unpack boot.img を実行して boot.img を抽出します。kernel ファイルが純正カーネルです。
  7. kernelImage で置き換えます: mv -f Image kernel
  8. ./magiskboot repack boot.img を実行してブートイメージをリパックします。出来上がった new-boot.img を fastboot でデバイスに書き込んでください。

その他の方法

実はこれらのインストール方法はすべて、元のカーネルを KernelSU が提供するカーネルに置き換えるという主旨でしかなく、これが実現できれば他の方法でもインストール可能です:

  1. まず Magisk をインストールし、Magisk を通じて root 権限を取得し、カーネル管理アプリで KernelSU の AnyKernel ZIPをインストールする
  2. PC 上で何らかの書き込みツールを使用し、KernelSU が提供するカーネルを書き込む

GPL3 ライセンスでリリースされています。

- +
Skip to content

インストール

デバイスが対応しているか確認する

GitHub Releases または Coolapk market から KernelSU Manager アプリをダウンロードし、お使いのデバイスにインストールしてください。

  • アプリが「非対応」と表示した場合は、自分でカーネルをコンパイルする必要があるという意味です。KernelSU は書き込むためのブートイメージを提供しません。
  • アプリが「未インストール」と表示した場合、お使いのデバイスは KernelSU に対応しています。

ヒント

非対応と表示されているデバイスについては、非公式の対応デバイスであればご自身でカーネルをビルドできます。

純正の boot.img をバックアップ

書き込む前に、まず純正の boot.img をバックアップする必要があります。ブートループが発生した場合は、fastboot を使用して純正のブートイメージを書き込むことでいつでもシステムを復旧できます。

警告

書き込みによりデータ損失を引き起こす可能性があります。次のステップに進む前に、このステップを必ず行うようにしてください!また、可能であればすべてのデータをバックアップしてください。

必要な知識

ADB と fastboot

このチュートリアルでは、デフォルトで ADB と fastboot のツールを使用します。ご存じない方は、まず検索エンジンを使って勉強されることをおすすめします。

KMI

同じ Kernel Module Interface (KMI) のカーネルバージョンは互換性があります。これが GKI の「汎用」という意味です。逆に言えば KMI が異なればカーネルには互換性がなく、お使いのデバイスと異なる KMI のカーネルイメージを書き込むと、ブートループが発生する場合があります。

具体的には GKI デバイスの場合、カーネルバージョンの形式は以下のようになります:

txt
KernelRelease :=
+Version.PatchLevel.SubLevel-AndroidRelease-KmiGeneration-suffix
+w      .x         .y       -zzz           -k            -something
KernelRelease :=
+Version.PatchLevel.SubLevel-AndroidRelease-KmiGeneration-suffix
+w      .x         .y       -zzz           -k            -something

w.x-zzz-k は KMI のバージョンです。例えば、デバイスのカーネルバージョンが 5.10.101-android12-9-g30979850fc20 である場合、その KMIは 5.10-android12-9 であり、理論的には他の KMI カーネルでも正常に起動できます。

ヒント

カーネルバージョンの SubLevel は、KMI の一部ではないことに注意してください。5.10.101-android12-9-g30979850fc205.10.137-android12-9-g30979850fc20 と同じ KMI を持っているということになります。

Kernel バージョンと Android バージョンの違い

注意: カーネルバージョンと Android バージョンは必ずしも同じではありません

カーネルのバージョンは「android12-5.10.101」なのに、Android システムのバージョンは「Android 13」などとなっている場合、驚かないでください。Linux カーネルのバージョン番号は、必ずしもデバイスの出荷時にプリインストールされている Android システムのバージョンと一致していません。Android システムが後でアップグレードされた場合、一般的にはカーネルのバージョンは変更されません。書き込む際は、必ずカーネルバージョンを参照してください!!!

インストール方法

KernelSU のインストール方法はいくつかあり、それぞれ適したシーンが異なりますので、必要に応じて選択してください。

  1. カスタムリカバリー(TWRPなど)でインストールする
  2. Franco Kernel Manager などのカーネル管理アプリでインストールする
  3. KernelSU が提供する boot.img を使用し、fastboot でインストールする
  4. boot.img を手動でパッチしてインストールする

カスタムリカバリーでインストール

前提条件:デバイスに TWRP などのカスタムリカバリーがあること。ない場合、または公式リカバリーしかない場合は他の方法を使用してください。

手順:

  1. KernelSUのリリースページから、お使いのデバイスのバージョンにあった AnyKernel3 で始まる ZIP パッケージをダウンロードします。例えば、デバイスのカーネルのバージョンがandroid12-5.10. 66の場合、AnyKernel3-android12-5.10.66_yyyy-MM.zip(yyyyは年、MMは月)のファイルをダウンロードします。
  2. デバイスを TWRP へ再起動します。
  3. adb を使用して AnyKernel3-*.zip をデバイスの /sdcard に入れ、TWRP GUI でインストールを選択します。または直接adb sideload AnyKernel-*.zip でインストールできます。

この方法は TWRP を使用できるならどのようなインストール(初期インストールやその後のアップグレード)にも適しています。

カーネル管理アプリでインストール

前提条件:お使いのデバイスが root 化されている必要があります。例えば、Magisk をインストールして root を取得した場合、または古いバージョンの KernelSU をインストールしており、別のバージョンの KernelSU にアップグレードする必要がある場合などです。お使いのデバイスが root 化されていない場合、他の方法をお試しください。

手順:

  1. AnyKernel3 ZIP をダウンロードします。ダウンロード方法は、「カスタムリカバリーでインストール」を参照してください。
  2. カーネル管理アプリを開き、AnyKernel3 の ZIP をインストールします。

カーネル管理アプリは以下のようなものが人気です:

  1. Kernel Flasher
  2. Franco Kernel Manager
  3. Ex Kernel Manager

この方法は KernelSU をアップグレードするときに便利で、パソコンがなくてもできます。(まずはバックアップしてください!)

KernelSU が提供する boot.img を使用してインストール

この方法は TWRP や root 権限を必要としないので、KernelSU を初めてインストールする場合に適しています。

正しい boot.img を見つける

KernelSU では、GKI デバイス用の汎用 boot.img を提供しています。デバイスの boot パーティションに boot.img をフラッシュする必要があります。

boot.img は、GitHub Release からダウンロードできます。例えば、あなたのデバイスがカーネル android12-5.10.101 の場合、android-5.10.101_yyyy-MM.boot-<format>.imgをダウンロードする必要があります。(KMI を同じにしてください!)。

<format>は純正 boot.img のカーネル圧縮形式を指します。純正の boot.img のカーネル圧縮形式を確認してください。間違った圧縮形式を使うと、ブートループするかもしれません。

情報

  1. magiskboot を使えば、元のブートの圧縮形式を知ることができます。もちろん、あなたのデバイスと同じモデルを持つ、より経験豊富な他の人にも聞くこともできます。また、カーネルの圧縮形式は通常変更されないので、ある圧縮形式でうまく起動した場合、後でその形式を試すことも可能です。
  2. Xiaomi デバイスでは通常 gz無圧縮が使われます。
  3. Pixel デバイスでは以下の手順に従ってください。

boot.img をデバイスに書き込む

adb でデバイスを接続し、adb reboot bootloader で fastboot モードにし、このコマンドで KernelSU を書き込んでください:

sh
fastboot flash boot boot.img
fastboot flash boot boot.img

情報

デバイスが fastboot boot をサポートしている場合、まず fastboot boot.img を使えば書き込みせずにシステムを起動できます。予期せぬことが起こった場合は、もう一度再起動して起動してください。

再起動

書き込みが完了したら、デバイスを再起動します:

sh
fastboot reboot
fastboot reboot

boot.img を手動でパッチ

デバイスによっては、boot.img のフォーマットが lz4 でない、gz である、無圧縮であるなど、あまり一般的でないことがあります。最も典型的なのは Pixel で、boot.img フォーマットは lz4_legacy 圧縮、RAM ディスクは gzlz4_legacy 圧縮です。この時、KernelSU が提供した boot.img を直接書き込むとデバイスが起動できなくなる場合があります。その場合は手動で boot.img に対してパッチしてください。

パッチ方式は一般的に2種類あります:

  1. Android-Image-Kitchen
  2. magiskboot

このうち、Android-Image-Kitchen は PC での操作に適しており、magiskboot はデバイスとの連携が必要です。

準備

  1. お使いのデバイスの純正 boot.img を入手します。デバイスメーカーから入手できます。payload-dumper-goが必要かもしれません。
  2. お使いのデバイスの KMI バージョンに合った、KernelSU が提供する AnyKernel3 の ZIP ファイルをダウンロードします(カスタムリカバリーでインストールを参照してください)。
  3. AnyKernel3 パッケージを展開し、KernelSU のカーネルファイルである Image ファイルを取得します。

Android-Image-Kitchen を使う

  1. Android-Image-Kitchen を PC にダウンロードします。
  2. 純正 boot.img を Android-Image-Kitchen のルートフォルダに入れます。
  3. Android-Image-Kitchen のルートディレクトリで ./unpackimg.sh boot.img を実行して、boot.imgを展開します。
  4. split_img ディレクトリの boot.img-kernel を AnyKernel3 から展開した Image に置き換えます(boot.img-kernelに名前が変わっていることに注意してください)。
  5. Android-Image-Kitchen のルートディレクトリで ./repackimg.sh を実行すると、 image-new.img というファイルが生成されます。

magiskboot を使う

  1. 最新の Magisk をリリースページからダウンロードしてください。
  2. Magisk-*.apk を Magisk-vesion.zip に名前を変更して展開してください。
  3. Magisk-v25.2/lib/arm64-v8a/libmagiskboot.soを adb でデバイスに転送します:adb push Magisk-v25.2/lib/arm64-v8a/libmagiskboot.so /data/local/tmp/magiskboot
  4. 純正 boot.img と AnyKernel3 の中の Image をデバイスに転送します。
  5. adb shell に入り、cd /data/local/tmp/ し、chmod +x magiskboot を実行します。
  6. adb shell に入り、cd /data/local/tmp/ し、./magiskboot unpack boot.img を実行して boot.img を抽出します。kernel ファイルが純正カーネルです。
  7. kernelImage で置き換えます: mv -f Image kernel
  8. ./magiskboot repack boot.img を実行してブートイメージをリパックします。出来上がった new-boot.img を fastboot でデバイスに書き込んでください。

その他の方法

実はこれらのインストール方法はすべて、元のカーネルを KernelSU が提供するカーネルに置き換えるという主旨でしかなく、これが実現できれば他の方法でもインストール可能です:

  1. まず Magisk をインストールし、Magisk を通じて root 権限を取得し、カーネル管理アプリで KernelSU の AnyKernel ZIPをインストールする
  2. PC 上で何らかの書き込みツールを使用し、KernelSU が提供するカーネルを書き込む

GPL3 ライセンスでリリースされています。

+ \ No newline at end of file diff --git a/ja_JP/guide/module.html b/ja_JP/guide/module.html index f3b59d92f1c7..98627cf678dd 100644 --- a/ja_JP/guide/module.html +++ b/ja_JP/guide/module.html @@ -5,98 +5,179 @@ モジュールのガイド | KernelSU - - + + + - - - - + + + + + -
Skip to content
On this page

モジュールのガイド

KernelSU はシステムパーティションの整合性を維持しながら、システムディレクトリを変更する効果を実現するモジュール機構を提供します。この機構は一般に「システムレス」と呼ばれています。

KernelSU のモジュール機構は、Magisk とほぼ同じです。Magisk のモジュール開発に慣れている方であれば、KernelSU のモジュール開発も簡単でしょう。その場合は以下のモジュールの紹介は読み飛ばして、Magisk との違いの内容だけ読めばOKです。

Busybox

KernelSU には、機能的に完全な Busybox バイナリ (SELinux の完全サポートを含む) が同梱されています。実行ファイルは /data/adb/ksu/bin/busybox に配置されています。KernelSU の Busybox はランタイムに切り替え可能な「ASH スタンドアローンシェルモード」をサポートしています。このスタンドアロンモードとは、Busybox の ash シェルで実行する場合 PATH として設定されているものに関係なく、すべてのコマンドが Busybox 内のアプレットを直接使用するというものです。たとえば、lsrmchmod などのコマンドは、PATH にあるもの(Android の場合、デフォルトではそれぞれ /system/bin/ls, /system/bin/rm, /system/bin/chmod)ではなく、直接 Busybox 内部のアプレットを呼び出すことになります。これにより、スクリプトは常に予測可能な環境で実行され、どの Android バージョンで実行されていても常にコマンドを利用できます。Busybox を使用しないコマンドを強制的に実行するには、フルパスで実行ファイルを呼び出す必要があります。

KernelSU のコンテキストで実行されるすべてのシェルスクリプトは、Busybox の ash シェルでスタンドアロンモードが有効な状態で実行されます。サードパーティの開発者に関係するものとしては、すべてのブートスクリプトとモジュールのインストールスクリプトが含まれます。

この「スタンドアロンモード」機能を KernelSU 以外で使用したい場合、2つの方法で有効にできます:

  1. 環境変数 ASH_STANDALONE1 にする
    例: ASH_STANDALONE=1 /data/adb/ksu/bin/busybox sh <script>
  2. コマンドラインのオプションで変更する:
    /data/adb/ksu/bin/busybox sh -o standalone <script>

環境変数が子プロセスに継承されるため、その後に実行されるすべての sh シェルもスタンドアロンモードで実行されるようにするにはオプション 1 が望ましい方法です(KernelSU と KernelSU Managerが内部的に使用しているのもこちらです)。

Magisk との違い

KernelSU の Busybox は、Magisk プロジェクトから直接コンパイルされたバイナリファイルを使用するようになりました。Magisk と KernelSU の Busybox スクリプトはまったく同じものなので、互換性の問題を心配する必要はありません!

KernelSU モジュール

KernelSU モジュールは、/data/adb/modules に配置された以下の構造を持つフォルダーです:

txt
/data/adb/modules
-├── .
-├── .
-|
-├── $MODID                  <--- フォルダの名前はモジュールの ID で付けます
-│   │
-│   │      *** モジュールの ID ***
-│   │
-│   ├── module.prop         <--- このファイルにモジュールのメタデータを保存します
-│   │
-│   │      *** メインコンテンツ ***
-│   │
-│   ├── system              <--- skip_mount が存在しない場合、このフォルダがマウントされます
-│   │   ├── ...
-│   │   ├── ...
-│   │   └── ...
-│   │
-│   │      *** ステータスフラグ ***
-│   │
-│   ├── skip_mount          <--- 存在する場合、KernelSU はシステムフォルダをマウントしません
-│   ├── disable             <--- 存在する場合、モジュールは無効化されます
-│   ├── remove              <--- 存在する場合、次の再起動時にモジュールが削除されます
-│   │
-│   │      *** 任意のファイル ***
-│   │
-│   ├── post-fs-data.sh     <--- このスクリプトは post-fs-data で実行されます
-│   ├── service.sh          <--- このスクリプトは late_start サービスで実行されます
-|   ├── uninstall.sh        <--- このスクリプトは KernelSU がモジュールを削除するときに実行されます
-│   ├── system.prop         <--- このファイルのプロパティは resetprop によってシステムプロパティとして読み込まれます
-│   ├── sepolicy.rule       <--- カスタム SEPolicy ルールを追加します
-│   │
-│   │      *** 自動生成されるため、手動で作成または変更しないでください ***
-│   │
-│   ├── vendor              <--- $MODID/system/vendor へのシンボリックリンク
-│   ├── product             <--- $MODID/system/product へのシンボリックリンク
-│   ├── system_ext          <--- $MODID/system/system_ext へのシンボリックリンク
-│   │
-│   │      *** その他のファイル/フォルダの追加も可能です ***
-│   │
-│   ├── ...
-│   └── ...
-|
-├── another_module
-│   ├── .
-│   └── .
-├── .
-├── .

Magisk との違い

KernelSU は Zygisk をビルトインでサポートしていないため、モジュール内に Zygisk に関連するコンテンツは存在しません。 しかし、ZygiskNext をインストールすれば Zygisk モジュールを使えます。その場合の Zygisk モジュールのコンテンツは Magisk と同じです。

module.prop

module.prop はモジュールの設定ファイルです。KernelSU ではこのファイルを含まないモジュールは、モジュールとして認識されません。このファイルの形式は以下の通りです:

txt
id=<string>
-name=<string>
-version=<string>
-versionCode=<int>
-author=<string>
-description=<string>
  • id はこの正規表現に一致していなければいけません: ^[a-zA-Z][a-zA-Z0-9._-]+$
    例: ✓ a_module, ✓ a.module, ✓ module-101, ✗ a module, ✗ 1_module, ✗ -a-module
    これはモジュールのユニークな ID です。公開後は変更しないでください。
  • versionCodeinteger です。バージョンの比較に使います。
  • 他のものには単一行 の文字であれば何でも使えます。
  • 改行文字は UNIX (LF) を使ってください。Windows (CR+LF)Macintosh (CR) は使ってはいけません。

シェルスクリプト

post-fs-data.shservice.sh の違いについては、ブートスクリプトのセクションを読んでください。ほとんどのモジュール開発者にとって、ブートスクリプトを実行するだけなら service.sh で十分なはずです。

モジュールのすべてのスクリプトでは、MODDIR=${0%/*}を使えばモジュールのベースディレクトリのパスを取得できます。スクリプト内でモジュールのパスをハードコードしないでください。

Magisk との違い

環境変数 KSU を使用すると、スクリプトが KernelSU と Magisk どちらで実行されているかを判断できます。KernelSU で実行されている場合、この値は true に設定されます。

system ディレクトリ

このディレクトリの内容は、システムの起動後に OverlayFS を使用してシステムの /system パーティションの上にオーバーレイされます:

  1. システム内の対応するディレクトリにあるファイルと同名のファイルは、このディレクトリにあるファイルで上書きされます。
  2. システム内の対応するディレクトリにあるフォルダと同じ名前のフォルダは、このディレクトリにあるフォルダと統合されます。

元のシステムディレクトリにあるファイルやフォルダを削除したい場合は、mknod filename c 0 0 を使ってモジュールディレクトリにそのファイル/フォルダと同じ名前のファイルを作成する必要があります。こうすることで、OverlayFS システムはこのファイルを削除したかのように自動的に「ホワイトアウト」します(/system パーティションは実際には変更されません)。

また、customize.sh 内で REMOVE という変数に削除操作を実行するディレクトリのリストを宣言すると、KernelSU は自動的にそのモジュールの対応するディレクトリで mknod <TARGET> c 0 0 を実行します。例えば

sh
REMOVE="
-/system/app/YouTube
-/system/app/Bloatware
-"

上記の場合は、mknod $MODPATH/system/app/YouTuBe c 0 0mknod $MODPATH/system/app/Bloatware c 0 0を実行し、/system/app/YouTube/system/app/Bloatwareはモジュール有効化後に削除されます。

システム内のディレクトリを置き換えたい場合は、モジュールディレクトリに同じパスのディレクトリを作成し、このディレクトリに setfattr -n trusted.overlay.opaque -v y <TARGET> という属性を設定する必要があります。こうすることで、OverlayFS システムは(/system パーティションを変更することなく)システム内の対応するディレクトリを自動的に置き換えることができます。

customize.sh ファイル内に REPLACE という変数を宣言し、その中に置換するディレクトリのリストを入れておけば、KernelSU は自動的にモジュールディレクトリに対応した処理を行います。例えば:

REPLACE=" /system/app/YouTube /system/app/Bloatware "

このリストは、自動的に $MODPATH/system/app/YouTube$MODPATH/system/app/Bloatware というディレクトリを作成し、 setfattr -n trusted.overlay.opaque -v y $MODPATH/system/app/YouTubesetfattr -n trusted.overlay.opaque -v y $MODPATH/system/app/Bloatware を実行します。モジュールが有効になると、/system/app/YouTube/system/app/Bloatware は空のディレクトリに置き換えられます。

Magisk との違い

KernelSU のシステムレスメカニズムはカーネルの OverlayFS によって実装され、Magisk は現在マジックマウント(bind mount)を使用しています。この2つの実装方法には大きな違いがありますが最終的な目的は同じで、/system パーティションを物理的に変更することなく、/system のファイルを変更できます。

OverlayFS に興味があれば、Linux カーネルの OverlayFS のドキュメンテーション を読んでみてください。

system.prop

このファイルは build.prop と同じ形式をとっています。各行は [key]=[value] で構成されます。

sepolicy.rule

もしあなたのモジュールが追加の SEPolicy パッチを必要とする場合は、それらのルールをこのファイルに追加してください。このファイルの各行は、ポリシーステートメントとして扱われます。

モジュールのインストーラー

KernelSU モジュールインストーラーは、KernelSU Manager アプリでインストールできる、ZIP ファイルにパッケージされた KernelSU モジュールです。最もシンプルな KernelSU モジュールインストーラーは、KernelSU モジュールを ZIP ファイルとしてパックしただけのものです。

txt
module.zip
-
-├── customize.sh                       <--- (任意、詳細は後述)
-│                                           このスクリプトは update-binary から読み込まれます
-├── ...
-├── ...  /* 残りのモジュールのファイル */
-

警告

KernelSU モジュールは、カスタムリカバリーからのインストールには非対応です!

カスタマイズ

モジュールのインストールプロセスをカスタマイズする必要がある場合、customize.sh という名前のスクリプトを作成してください。このスクリプトは、すべてのファイルが抽出され、デフォルトのパーミッションと secontext が適用された後、モジュールインストーラースクリプトによって読み込み (実行ではなく) されます。これは、モジュールがデバイスの ABI に基づいて追加設定を必要とする場合や、モジュールファイルの一部に特別なパーミッション/コンテキストを設定する必要がある場合に、非常に便利です。

インストールプロセスを完全に制御しカスタマイズしたい場合は、customize.shSKIPUNZIP=1 と宣言すればデフォルトのインストールステップをすべてスキップできます。そうすることで、customize.sh が責任をもってすべてをインストールするようになります。

customize.shスクリプトは、KernelSU の Busybox ash シェルで、「スタンドアロンモード」を有効にして実行します。以下の変数と関数が利用可能です:

変数

  • KSU (bool): スクリプトが KernelSU 環境で実行されていることを示すための変数で、この変数の値は常に true になります。KernelSU と Magisk を区別するために使用できます。
  • KSU_VER (string): 現在インストールされている KernelSU のバージョン文字列 (例: v0.4.0)
  • KSU_VER_CODE (int): ユーザー空間に現在インストールされているKernelSUのバージョンコード (例: 10672)
  • KSU_KERNEL_VER_CODE (int): 現在インストールされている KernelSU のカーネル空間でのバージョンコード(例:10672
  • BOOTMODE (bool): KernelSU では常に true
  • MODPATH (path): モジュールファイルがインストールされるパス
  • TMPDIR (path): ファイルを一時的に保存しておく場所
  • ZIPFILE (path): あなたのモジュールのインストールZIP
  • ARCH (string): デバイスの CPU アーキテクチャ。値は armarm64x86x64 のいずれか
  • IS64BIT (bool): ARCHarm64 または x64 のときは true
  • API (int): 端末の API レベル・Android のバージョン(例:Android 6.0 なら23

警告

KernelSU では、MAGISK_VER_CODE は常に25200、MAGISK_VER は常にv25.2です。この2つの変数で KernelSU 上で動作しているかどうかを判断するのはやめてください。

機能

txt
ui_print <msg>
-    コンソールに <msg> を表示します
-    カスタムリカバリーのコンソールでは表示されないため、「echo」の使用は避けてください
-
-abort <msg>
-    エラーメッセージ<msg>をコンソールに出力し、インストールを終了させます
-    終了時のクリーンアップがスキップされてしまうため、「exit」の使用は避けてください
-
-set_perm <target> <owner> <group> <permission> [context]
-    [context] が設定されていない場合、デフォルトは "u:object_r:system_file:s0" です。
-    この機能は、次のコマンドの略記です:
-       chown owner.group target
-       chmod permission target
-       chcon context target
-
-set_perm_recursive <directory> <owner> <group> <dirpermission> <filepermission> [context]
-    [context] が設定されていない場合、デフォルトは "u:object_r:system_file:s0" です。
-    <directory> 内のすべてのファイルに対しては以下が実行されます:
-       set_perm file owner group filepermission context
-    <directory> 内のすべてのディレクトリ(自身を含む)に対しては以下が実行されます:
-       set_perm dir owner group dirpermission context

ブートスクリプト

KernelSU では、スクリプトは実行モードによって post-fs-data モードと late_start サービスモードの2種類に分けられます:

  • post-fs-data モード
    • 同期処理です。実行が終わるか、10秒が経過するまでブートプロセスが一時停止されます。
    • スクリプトはモジュールがマウントされる前に実行されます。モジュール開発者はモジュールがマウントされる前に、動的にモジュールを調整できます。
    • このステージは Zygote が始まる前に起こるので、Android のほぼすべての処理の前に割り込めます
    • 警告: setprop を使うとブートプロセスのデッドロックを引き起こします! resetprop -n <prop_name> <prop_value> を使ってください。
    • 本当に必要な場合だけこのモードでコマンド実行してください
  • late_start サービスモード
    • 非同期処理です。スクリプトは、起動プロセスの残りの部分と並行して実行されます。
    • ほとんどのスクリプトにはこちらがおすすめです

KernelSU では、起動スクリプトは保存場所によって一般スクリプトとモジュールスクリプトの2種類に分けられます:

  • 一般スクリプト
    • /data/adb/post-fs-data.d/data/adb/service.d に配置されます
    • スクリプトが実行可能な状態に設定されている場合にのみ実行されます (chmod +x script.sh)
    • post-fs-data.d のスクリプトは post-fs-data モードで実行され、service.d のスクリプトは late_start サービスモードで実行されます
    • モジュールはインストール時に一般スクリプトを追加するべきではありません
  • モジュールスクリプト
    • モジュール独自のフォルダに配置されます
    • モジュールが有効な場合のみ実行されます
    • post-fs-data.sh は post-fs-data モードで実行され、service.sh は late_start サービスモードで実行されます

すべてのブートスクリプトは、KernelSU の Busybox ash シェルで「スタンドアロンモード」を有効にした状態で実行されます。

GPL3 ライセンスでリリースされています。

- +
Skip to content

モジュールのガイド

KernelSU はシステムパーティションの整合性を維持しながら、システムディレクトリを変更する効果を実現するモジュール機構を提供します。この機構は一般に「システムレス」と呼ばれています。

KernelSU のモジュール機構は、Magisk とほぼ同じです。Magisk のモジュール開発に慣れている方であれば、KernelSU のモジュール開発も簡単でしょう。その場合は以下のモジュールの紹介は読み飛ばして、Magisk との違いの内容だけ読めばOKです。

Busybox

KernelSU には、機能的に完全な Busybox バイナリ (SELinux の完全サポートを含む) が同梱されています。実行ファイルは /data/adb/ksu/bin/busybox に配置されています。KernelSU の Busybox はランタイムに切り替え可能な「ASH スタンドアローンシェルモード」をサポートしています。このスタンドアロンモードとは、Busybox の ash シェルで実行する場合 PATH として設定されているものに関係なく、すべてのコマンドが Busybox 内のアプレットを直接使用するというものです。たとえば、lsrmchmod などのコマンドは、PATH にあるもの(Android の場合、デフォルトではそれぞれ /system/bin/ls, /system/bin/rm, /system/bin/chmod)ではなく、直接 Busybox 内部のアプレットを呼び出すことになります。これにより、スクリプトは常に予測可能な環境で実行され、どの Android バージョンで実行されていても常にコマンドを利用できます。Busybox を使用しないコマンドを強制的に実行するには、フルパスで実行ファイルを呼び出す必要があります。

KernelSU のコンテキストで実行されるすべてのシェルスクリプトは、Busybox の ash シェルでスタンドアロンモードが有効な状態で実行されます。サードパーティの開発者に関係するものとしては、すべてのブートスクリプトとモジュールのインストールスクリプトが含まれます。

この「スタンドアロンモード」機能を KernelSU 以外で使用したい場合、2つの方法で有効にできます:

  1. 環境変数 ASH_STANDALONE1 にする
    例: ASH_STANDALONE=1 /data/adb/ksu/bin/busybox sh <script>
  2. コマンドラインのオプションで変更する:
    /data/adb/ksu/bin/busybox sh -o standalone <script>

環境変数が子プロセスに継承されるため、その後に実行されるすべての sh シェルもスタンドアロンモードで実行されるようにするにはオプション 1 が望ましい方法です(KernelSU と KernelSU Managerが内部的に使用しているのもこちらです)。

Magisk との違い

KernelSU の Busybox は、Magisk プロジェクトから直接コンパイルされたバイナリファイルを使用するようになりました。Magisk と KernelSU の Busybox スクリプトはまったく同じものなので、互換性の問題を心配する必要はありません!

KernelSU モジュール

KernelSU モジュールは、/data/adb/modules に配置された以下の構造を持つフォルダーです:

txt
/data/adb/modules
+├── .
+├── .
+|
+├── $MODID                  <--- フォルダの名前はモジュールの ID で付けます
+│   │
+│   │      *** モジュールの ID ***
+│   │
+│   ├── module.prop         <--- このファイルにモジュールのメタデータを保存します
+│   │
+│   │      *** メインコンテンツ ***
+│   │
+│   ├── system              <--- skip_mount が存在しない場合、このフォルダがマウントされます
+│   │   ├── ...
+│   │   ├── ...
+│   │   └── ...
+│   │
+│   │      *** ステータスフラグ ***
+│   │
+│   ├── skip_mount          <--- 存在する場合、KernelSU はシステムフォルダをマウントしません
+│   ├── disable             <--- 存在する場合、モジュールは無効化されます
+│   ├── remove              <--- 存在する場合、次の再起動時にモジュールが削除されます
+│   │
+│   │      *** 任意のファイル ***
+│   │
+│   ├── post-fs-data.sh     <--- このスクリプトは post-fs-data で実行されます
+│   ├── service.sh          <--- このスクリプトは late_start サービスで実行されます
+|   ├── uninstall.sh        <--- このスクリプトは KernelSU がモジュールを削除するときに実行されます
+│   ├── system.prop         <--- このファイルのプロパティは resetprop によってシステムプロパティとして読み込まれます
+│   ├── sepolicy.rule       <--- カスタム SEPolicy ルールを追加します
+│   │
+│   │      *** 自動生成されるため、手動で作成または変更しないでください ***
+│   │
+│   ├── vendor              <--- $MODID/system/vendor へのシンボリックリンク
+│   ├── product             <--- $MODID/system/product へのシンボリックリンク
+│   ├── system_ext          <--- $MODID/system/system_ext へのシンボリックリンク
+│   │
+│   │      *** その他のファイル/フォルダの追加も可能です ***
+│   │
+│   ├── ...
+│   └── ...
+|
+├── another_module
+│   ├── .
+│   └── .
+├── .
+├── .
/data/adb/modules
+├── .
+├── .
+|
+├── $MODID                  <--- フォルダの名前はモジュールの ID で付けます
+│   │
+│   │      *** モジュールの ID ***
+│   │
+│   ├── module.prop         <--- このファイルにモジュールのメタデータを保存します
+│   │
+│   │      *** メインコンテンツ ***
+│   │
+│   ├── system              <--- skip_mount が存在しない場合、このフォルダがマウントされます
+│   │   ├── ...
+│   │   ├── ...
+│   │   └── ...
+│   │
+│   │      *** ステータスフラグ ***
+│   │
+│   ├── skip_mount          <--- 存在する場合、KernelSU はシステムフォルダをマウントしません
+│   ├── disable             <--- 存在する場合、モジュールは無効化されます
+│   ├── remove              <--- 存在する場合、次の再起動時にモジュールが削除されます
+│   │
+│   │      *** 任意のファイル ***
+│   │
+│   ├── post-fs-data.sh     <--- このスクリプトは post-fs-data で実行されます
+│   ├── service.sh          <--- このスクリプトは late_start サービスで実行されます
+|   ├── uninstall.sh        <--- このスクリプトは KernelSU がモジュールを削除するときに実行されます
+│   ├── system.prop         <--- このファイルのプロパティは resetprop によってシステムプロパティとして読み込まれます
+│   ├── sepolicy.rule       <--- カスタム SEPolicy ルールを追加します
+│   │
+│   │      *** 自動生成されるため、手動で作成または変更しないでください ***
+│   │
+│   ├── vendor              <--- $MODID/system/vendor へのシンボリックリンク
+│   ├── product             <--- $MODID/system/product へのシンボリックリンク
+│   ├── system_ext          <--- $MODID/system/system_ext へのシンボリックリンク
+│   │
+│   │      *** その他のファイル/フォルダの追加も可能です ***
+│   │
+│   ├── ...
+│   └── ...
+|
+├── another_module
+│   ├── .
+│   └── .
+├── .
+├── .

Magisk との違い

KernelSU は Zygisk をビルトインでサポートしていないため、モジュール内に Zygisk に関連するコンテンツは存在しません。 しかし、ZygiskNext をインストールすれば Zygisk モジュールを使えます。その場合の Zygisk モジュールのコンテンツは Magisk と同じです。

module.prop

module.prop はモジュールの設定ファイルです。KernelSU ではこのファイルを含まないモジュールは、モジュールとして認識されません。このファイルの形式は以下の通りです:

txt
id=<string>
+name=<string>
+version=<string>
+versionCode=<int>
+author=<string>
+description=<string>
id=<string>
+name=<string>
+version=<string>
+versionCode=<int>
+author=<string>
+description=<string>
  • id はこの正規表現に一致していなければいけません: ^[a-zA-Z][a-zA-Z0-9._-]+$
    例: ✓ a_module, ✓ a.module, ✓ module-101, ✗ a module, ✗ 1_module, ✗ -a-module
    これはモジュールのユニークな ID です。公開後は変更しないでください。
  • versionCodeinteger です。バージョンの比較に使います。
  • 他のものには単一行 の文字であれば何でも使えます。
  • 改行文字は UNIX (LF) を使ってください。Windows (CR+LF)Macintosh (CR) は使ってはいけません。

シェルスクリプト

post-fs-data.shservice.sh の違いについては、ブートスクリプトのセクションを読んでください。ほとんどのモジュール開発者にとって、ブートスクリプトを実行するだけなら service.sh で十分なはずです。

モジュールのすべてのスクリプトでは、MODDIR=${0%/*}を使えばモジュールのベースディレクトリのパスを取得できます。スクリプト内でモジュールのパスをハードコードしないでください。

Magisk との違い

環境変数 KSU を使用すると、スクリプトが KernelSU と Magisk どちらで実行されているかを判断できます。KernelSU で実行されている場合、この値は true に設定されます。

system ディレクトリ

このディレクトリの内容は、システムの起動後に OverlayFS を使用してシステムの /system パーティションの上にオーバーレイされます:

  1. システム内の対応するディレクトリにあるファイルと同名のファイルは、このディレクトリにあるファイルで上書きされます。
  2. システム内の対応するディレクトリにあるフォルダと同じ名前のフォルダは、このディレクトリにあるフォルダと統合されます。

元のシステムディレクトリにあるファイルやフォルダを削除したい場合は、mknod filename c 0 0 を使ってモジュールディレクトリにそのファイル/フォルダと同じ名前のファイルを作成する必要があります。こうすることで、OverlayFS システムはこのファイルを削除したかのように自動的に「ホワイトアウト」します(/system パーティションは実際には変更されません)。

また、customize.sh 内で REMOVE という変数に削除操作を実行するディレクトリのリストを宣言すると、KernelSU は自動的にそのモジュールの対応するディレクトリで mknod <TARGET> c 0 0 を実行します。例えば

sh
REMOVE="
+/system/app/YouTube
+/system/app/Bloatware
+"
REMOVE="
+/system/app/YouTube
+/system/app/Bloatware
+"

上記の場合は、mknod $MODPATH/system/app/YouTuBe c 0 0mknod $MODPATH/system/app/Bloatware c 0 0を実行し、/system/app/YouTube/system/app/Bloatwareはモジュール有効化後に削除されます。

システム内のディレクトリを置き換えたい場合は、モジュールディレクトリに同じパスのディレクトリを作成し、このディレクトリに setfattr -n trusted.overlay.opaque -v y <TARGET> という属性を設定する必要があります。こうすることで、OverlayFS システムは(/system パーティションを変更することなく)システム内の対応するディレクトリを自動的に置き換えることができます。

customize.sh ファイル内に REPLACE という変数を宣言し、その中に置換するディレクトリのリストを入れておけば、KernelSU は自動的にモジュールディレクトリに対応した処理を行います。例えば:

REPLACE=" /system/app/YouTube /system/app/Bloatware "

このリストは、自動的に $MODPATH/system/app/YouTube$MODPATH/system/app/Bloatware というディレクトリを作成し、 setfattr -n trusted.overlay.opaque -v y $MODPATH/system/app/YouTubesetfattr -n trusted.overlay.opaque -v y $MODPATH/system/app/Bloatware を実行します。モジュールが有効になると、/system/app/YouTube/system/app/Bloatware は空のディレクトリに置き換えられます。

Magisk との違い

KernelSU のシステムレスメカニズムはカーネルの OverlayFS によって実装され、Magisk は現在マジックマウント(bind mount)を使用しています。この2つの実装方法には大きな違いがありますが最終的な目的は同じで、/system パーティションを物理的に変更することなく、/system のファイルを変更できます。

OverlayFS に興味があれば、Linux カーネルの OverlayFS のドキュメンテーション を読んでみてください。

system.prop

このファイルは build.prop と同じ形式をとっています。各行は [key]=[value] で構成されます。

sepolicy.rule

もしあなたのモジュールが追加の SEPolicy パッチを必要とする場合は、それらのルールをこのファイルに追加してください。このファイルの各行は、ポリシーステートメントとして扱われます。

モジュールのインストーラー

KernelSU モジュールインストーラーは、KernelSU Manager アプリでインストールできる、ZIP ファイルにパッケージされた KernelSU モジュールです。最もシンプルな KernelSU モジュールインストーラーは、KernelSU モジュールを ZIP ファイルとしてパックしただけのものです。

txt
module.zip
+
+├── customize.sh                       <--- (任意、詳細は後述)
+│                                           このスクリプトは update-binary から読み込まれます
+├── ...
+├── ...  /* 残りのモジュールのファイル */
+
module.zip
+
+├── customize.sh                       <--- (任意、詳細は後述)
+│                                           このスクリプトは update-binary から読み込まれます
+├── ...
+├── ...  /* 残りのモジュールのファイル */
+

警告

KernelSU モジュールは、カスタムリカバリーからのインストールには非対応です!

カスタマイズ

モジュールのインストールプロセスをカスタマイズする必要がある場合、customize.sh という名前のスクリプトを作成してください。このスクリプトは、すべてのファイルが抽出され、デフォルトのパーミッションと secontext が適用された後、モジュールインストーラースクリプトによって読み込み (実行ではなく) されます。これは、モジュールがデバイスの ABI に基づいて追加設定を必要とする場合や、モジュールファイルの一部に特別なパーミッション/コンテキストを設定する必要がある場合に、非常に便利です。

インストールプロセスを完全に制御しカスタマイズしたい場合は、customize.shSKIPUNZIP=1 と宣言すればデフォルトのインストールステップをすべてスキップできます。そうすることで、customize.sh が責任をもってすべてをインストールするようになります。

customize.shスクリプトは、KernelSU の Busybox ash シェルで、「スタンドアロンモード」を有効にして実行します。以下の変数と関数が利用可能です:

変数

  • KSU (bool): スクリプトが KernelSU 環境で実行されていることを示すための変数で、この変数の値は常に true になります。KernelSU と Magisk を区別するために使用できます。
  • KSU_VER (string): 現在インストールされている KernelSU のバージョン文字列 (例: v0.4.0)
  • KSU_VER_CODE (int): ユーザー空間に現在インストールされているKernelSUのバージョンコード (例: 10672)
  • KSU_KERNEL_VER_CODE (int): 現在インストールされている KernelSU のカーネル空間でのバージョンコード(例:10672
  • BOOTMODE (bool): KernelSU では常に true
  • MODPATH (path): モジュールファイルがインストールされるパス
  • TMPDIR (path): ファイルを一時的に保存しておく場所
  • ZIPFILE (path): あなたのモジュールのインストールZIP
  • ARCH (string): デバイスの CPU アーキテクチャ。値は armarm64x86x64 のいずれか
  • IS64BIT (bool): ARCHarm64 または x64 のときは true
  • API (int): 端末の API レベル・Android のバージョン(例:Android 6.0 なら23

警告

KernelSU では、MAGISK_VER_CODE は常に25200、MAGISK_VER は常にv25.2です。この2つの変数で KernelSU 上で動作しているかどうかを判断するのはやめてください。

機能

txt
ui_print <msg>
+    コンソールに <msg> を表示します
+    カスタムリカバリーのコンソールでは表示されないため、「echo」の使用は避けてください
+
+abort <msg>
+    エラーメッセージ<msg>をコンソールに出力し、インストールを終了させます
+    終了時のクリーンアップがスキップされてしまうため、「exit」の使用は避けてください
+
+set_perm <target> <owner> <group> <permission> [context]
+    [context] が設定されていない場合、デフォルトは "u:object_r:system_file:s0" です。
+    この機能は、次のコマンドの略記です:
+       chown owner.group target
+       chmod permission target
+       chcon context target
+
+set_perm_recursive <directory> <owner> <group> <dirpermission> <filepermission> [context]
+    [context] が設定されていない場合、デフォルトは "u:object_r:system_file:s0" です。
+    <directory> 内のすべてのファイルに対しては以下が実行されます:
+       set_perm file owner group filepermission context
+    <directory> 内のすべてのディレクトリ(自身を含む)に対しては以下が実行されます:
+       set_perm dir owner group dirpermission context
ui_print <msg>
+    コンソールに <msg> を表示します
+    カスタムリカバリーのコンソールでは表示されないため、「echo」の使用は避けてください
+
+abort <msg>
+    エラーメッセージ<msg>をコンソールに出力し、インストールを終了させます
+    終了時のクリーンアップがスキップされてしまうため、「exit」の使用は避けてください
+
+set_perm <target> <owner> <group> <permission> [context]
+    [context] が設定されていない場合、デフォルトは "u:object_r:system_file:s0" です。
+    この機能は、次のコマンドの略記です:
+       chown owner.group target
+       chmod permission target
+       chcon context target
+
+set_perm_recursive <directory> <owner> <group> <dirpermission> <filepermission> [context]
+    [context] が設定されていない場合、デフォルトは "u:object_r:system_file:s0" です。
+    <directory> 内のすべてのファイルに対しては以下が実行されます:
+       set_perm file owner group filepermission context
+    <directory> 内のすべてのディレクトリ(自身を含む)に対しては以下が実行されます:
+       set_perm dir owner group dirpermission context

ブートスクリプト

KernelSU では、スクリプトは実行モードによって post-fs-data モードと late_start サービスモードの2種類に分けられます:

  • post-fs-data モード
    • 同期処理です。実行が終わるか、10秒が経過するまでブートプロセスが一時停止されます。
    • スクリプトはモジュールがマウントされる前に実行されます。モジュール開発者はモジュールがマウントされる前に、動的にモジュールを調整できます。
    • このステージは Zygote が始まる前に起こるので、Android のほぼすべての処理の前に割り込めます
    • 警告: setprop を使うとブートプロセスのデッドロックを引き起こします! resetprop -n <prop_name> <prop_value> を使ってください。
    • 本当に必要な場合だけこのモードでコマンド実行してください
  • late_start サービスモード
    • 非同期処理です。スクリプトは、起動プロセスの残りの部分と並行して実行されます。
    • ほとんどのスクリプトにはこちらがおすすめです

KernelSU では、起動スクリプトは保存場所によって一般スクリプトとモジュールスクリプトの2種類に分けられます:

  • 一般スクリプト
    • /data/adb/post-fs-data.d/data/adb/service.d に配置されます
    • スクリプトが実行可能な状態に設定されている場合にのみ実行されます (chmod +x script.sh)
    • post-fs-data.d のスクリプトは post-fs-data モードで実行され、service.d のスクリプトは late_start サービスモードで実行されます
    • モジュールはインストール時に一般スクリプトを追加するべきではありません
  • モジュールスクリプト
    • モジュール独自のフォルダに配置されます
    • モジュールが有効な場合のみ実行されます
    • post-fs-data.sh は post-fs-data モードで実行され、service.sh は late_start サービスモードで実行されます

すべてのブートスクリプトは、KernelSU の Busybox ash シェルで「スタンドアロンモード」を有効にした状態で実行されます。

GPL3 ライセンスでリリースされています。

+ \ No newline at end of file diff --git a/ja_JP/guide/rescue-from-bootloop.html b/ja_JP/guide/rescue-from-bootloop.html index 0196cfc936d1..3136f5974b1c 100644 --- a/ja_JP/guide/rescue-from-bootloop.html +++ b/ja_JP/guide/rescue-from-bootloop.html @@ -5,18 +5,19 @@ ブートループからの復旧 | KernelSU - - + + + - - - - + + + + + -
Skip to content
On this page

ブートループからの復旧

デバイスに書き込む際、デバイスが「文鎮化」状態になる場面に遭遇することがあります。理論的には、fastboot で boot パーティションを書き込んだだけだったり、不適切なモジュールをインストールしてデバイスが起動しなくなったりした場合なら、適切な操作で復旧できます。このページでは、「文鎮化」状態になったデバイスを復旧させるための緊急手段を紹介します。

boot パーティションの書き込みによる文鎮化

KernelSU では、以下のような状況で boot パーティションを書き込んだときに文鎮化する場合があります:

  1. 間違った形式の boot イメージを書き込んでしまった場合。例えばお使いのデバイスのパーティション形式が gz なのに lz4 形式のイメージを書き込んでしまうと、起動しなくなります。
  2. お使いのデバイスが起動するために AVB 検証を無効にする必要がある場合(通常、無効にするにはすべてのデータを消去する必要があります)。
  3. カーネルにバグがある、または書き込みに適していない場合。

どのような状況であっても、純正の boot イメージを書き込むことで復旧できます。したがって、インストールする前にまずは純正の boot パーティションをバックアップすることを強くおすすめします。バックアップしていない場合は、あなたと同じデバイスを持つ他のユーザー、または公式ファームウェアから純正の boot イメージを入手できます。

モジュールによる文鎮化

モジュールのインストールはデバイスを文鎮化させる一般的な原因です。モジュールを未知のソースからインストールしないでください!モジュールは root 権限を持つため、あなたのデバイスに不可逆的なダメージを与える可能性があります!

通常のモジュール

安全であることが確認されているモジュールをインストールしてデバイスが起動しなくなった場合、KernelSU では心配することなく簡単に復旧できます。KernelSU には、以下のようなデバイスを救出するための仕組みが組み込まれています:

  1. AB アップデート
  2. 音量下ボタンでの復旧

AB アップデート

KernelSU のモジュール更新は、OTA アップデートで使用される Android システムの AB アップデート機構からヒントを得ています。新しいモジュールをインストールしたり、既存のモジュールを更新したりする場合、現在使用されているモジュールファイルを直接変更することはありません。代わりに、すべてのモジュールが別のアップデートイメージに組み込まれます。システムが再起動された後、このアップデートイメージの使用を開始しようとします。Android システムが正常に起動した場合、モジュールは本当に更新されます。

そのため、デバイスを復旧する最もシンプルで一般的な方法は、強制的に再起動することです。モジュールをインストールした後にシステムを起動できなくなった場合、電源ボタンを10秒以上長押しするとシステムが自動的に再起動します。再起動後はモジュールを更新する前の状態にロールバックされ、以前に更新したモジュールは自動的に無効化されます。

音量下ボタンでの復旧

AB アップデートでも解決しない場合は、セーフモードを使用してみてください。セーフモードでは、すべてのモジュールが無効化されます。

セーフモードに入るには、2つの方法があります:

  1. 一部のシステムの内蔵セーフモード:音量下ボタンの長押しでセーフモードに入れるシステムもあれば、リカバリーでセーフモードに入れるシステム(MIUI など)もあります。システムのセーフモードに入ると KernelSU もセーフモードに入り、自動的にモジュールを無効化します。
  2. KernelSU の内蔵セーフモード:最初の起動画面の後、音量下キーを3回以上連続して押すと入れます。なお、押す→離すを三回繰り返すのであって、長押しではありません。

セーフモードに入ると、KernelSU Manager のモジュールページにあるすべてのモジュールが無効になります。「アンインストール」操作を行うことで、問題を起こしている可能性のあるモジュールをアンインストールできます。

内蔵のセーフモードはカーネルに実装されているため、キーイベントを見逃す可能性はありません。ただし、GKI 以外のカーネルでは手動によるコードの統合が必要な場合があるため、公式ドキュメントを参考にしてください。

悪意のあるモジュール

上記の方法でデバイスを救出できない場合、インストールしたモジュールが悪意のある操作をしているか、他の手段でデバイスを損傷している可能性が高いです。この場合、2つの方法しかありません:

  1. データを消去して純正システムをインストールし直す
  2. アフターセールスサービスに問い合わせする

GPL3 ライセンスでリリースされています。

- +
Skip to content

ブートループからの復旧

デバイスに書き込む際、デバイスが「文鎮化」状態になる場面に遭遇することがあります。理論的には、fastboot で boot パーティションを書き込んだだけだったり、不適切なモジュールをインストールしてデバイスが起動しなくなったりした場合なら、適切な操作で復旧できます。このページでは、「文鎮化」状態になったデバイスを復旧させるための緊急手段を紹介します。

boot パーティションの書き込みによる文鎮化

KernelSU では、以下のような状況で boot パーティションを書き込んだときに文鎮化する場合があります:

  1. 間違った形式の boot イメージを書き込んでしまった場合。例えばお使いのデバイスのパーティション形式が gz なのに lz4 形式のイメージを書き込んでしまうと、起動しなくなります。
  2. お使いのデバイスが起動するために AVB 検証を無効にする必要がある場合(通常、無効にするにはすべてのデータを消去する必要があります)。
  3. カーネルにバグがある、または書き込みに適していない場合。

どのような状況であっても、純正の boot イメージを書き込むことで復旧できます。したがって、インストールする前にまずは純正の boot パーティションをバックアップすることを強くおすすめします。バックアップしていない場合は、あなたと同じデバイスを持つ他のユーザー、または公式ファームウェアから純正の boot イメージを入手できます。

モジュールによる文鎮化

モジュールのインストールはデバイスを文鎮化させる一般的な原因です。モジュールを未知のソースからインストールしないでください!モジュールは root 権限を持つため、あなたのデバイスに不可逆的なダメージを与える可能性があります!

通常のモジュール

安全であることが確認されているモジュールをインストールしてデバイスが起動しなくなった場合、KernelSU では心配することなく簡単に復旧できます。KernelSU には、以下のようなデバイスを救出するための仕組みが組み込まれています:

  1. AB アップデート
  2. 音量下ボタンでの復旧

AB アップデート

KernelSU のモジュール更新は、OTA アップデートで使用される Android システムの AB アップデート機構からヒントを得ています。新しいモジュールをインストールしたり、既存のモジュールを更新したりする場合、現在使用されているモジュールファイルを直接変更することはありません。代わりに、すべてのモジュールが別のアップデートイメージに組み込まれます。システムが再起動された後、このアップデートイメージの使用を開始しようとします。Android システムが正常に起動した場合、モジュールは本当に更新されます。

そのため、デバイスを復旧する最もシンプルで一般的な方法は、強制的に再起動することです。モジュールをインストールした後にシステムを起動できなくなった場合、電源ボタンを10秒以上長押しするとシステムが自動的に再起動します。再起動後はモジュールを更新する前の状態にロールバックされ、以前に更新したモジュールは自動的に無効化されます。

音量下ボタンでの復旧

AB アップデートでも解決しない場合は、セーフモードを使用してみてください。セーフモードでは、すべてのモジュールが無効化されます。

セーフモードに入るには、2つの方法があります:

  1. 一部のシステムの内蔵セーフモード:音量下ボタンの長押しでセーフモードに入れるシステムもあれば、リカバリーでセーフモードに入れるシステム(MIUI など)もあります。システムのセーフモードに入ると KernelSU もセーフモードに入り、自動的にモジュールを無効化します。
  2. KernelSU の内蔵セーフモード:最初の起動画面の後、音量下キーを3回以上連続して押すと入れます。なお、押す→離すを三回繰り返すのであって、長押しではありません。

セーフモードに入ると、KernelSU Manager のモジュールページにあるすべてのモジュールが無効になります。「アンインストール」操作を行うことで、問題を起こしている可能性のあるモジュールをアンインストールできます。

内蔵のセーフモードはカーネルに実装されているため、キーイベントを見逃す可能性はありません。ただし、GKI 以外のカーネルでは手動によるコードの統合が必要な場合があるため、公式ドキュメントを参考にしてください。

悪意のあるモジュール

上記の方法でデバイスを救出できない場合、インストールしたモジュールが悪意のある操作をしているか、他の手段でデバイスを損傷している可能性が高いです。この場合、2つの方法しかありません:

  1. データを消去して純正システムをインストールし直す
  2. アフターセールスサービスに問い合わせする

GPL3 ライセンスでリリースされています。

+ \ No newline at end of file diff --git a/ja_JP/guide/unofficially-support-devices.html b/ja_JP/guide/unofficially-support-devices.html index 691ced229680..18c814dfe780 100644 --- a/ja_JP/guide/unofficially-support-devices.html +++ b/ja_JP/guide/unofficially-support-devices.html @@ -5,19 +5,20 @@ 非公式の対応デバイス | KernelSU - - + + + - - - - - + + + + + + -
Skip to content
On this page

非公式の対応デバイス

警告

このページでは他の開発者が管理している、KernelSU をサポートする GKI 以外のデバイス用のカーネルを紹介しています。

警告

このページはあなたのデバイスに対応するソースコードを見つけるためのものであり、そのソースコードが KernelSU 開発者 によってレビューされたことを意味するものではありません。ご自身の責任においてご利用ください。

メンテナーリポジトリ対応デバイス
diphonsD8G_Kernel_oxygenPoco F3: alioth | Poco F4: munch | MI10T/PRO: Apollo
diphonsD8G_Kernel_SM8150Poco X3 Pro: Vayu | Bhima
diphonsD8G_Kernel_SDM845Poco F1 | MI8 | MiMix2S
diphonsD8G_Kernel_SM8350Vili
diphonsD8G_Kernel_MarblePoco F5 | Redmi Note 12 Turbo | Marble
th1nhhdkandroid_kernel_sony_sm8250-kernelsuSony Xperia 1 II | Sony Xperia 5 II
akash07knexus_kernel_xiaomi_sm8250Poco F4: munch
HMTheBoy154Darkmatter-kernelGeneric x86_64 devices running Android-x86
Asuka-mioandroid_kernel_xiaomi_casMi 10 Ultra: cas
xiaoleGunMiku_kernel_xiaomi_waynewayne
YamazakuraHkernel_xiaomi_cepheuscepheus for pixel experience
SakuraNotStupidandroid_kernel_xiaomi_sdm710Xiaomi MI 8 SE(sirius/xmsirius)
Aquarius223(paper)android_kernel_xiaomi_msm8998MI 6 (sagit) and MIX 2 (chiron) for LineageOS
SlackerStateandroid_kernel_xiaomi_sm6150Redmi K30 4G (phoenix)
RooGhz720kernel_xiaomi_sm6150REDMI NOTE 10 PRO (sweet)
OnlyTomInSecondandroid_kernel_xiaomi_sdm845Mi 8 (dipper) for LineageOS
Rohail33RealKing KernelApollo(Redmi K30S Ultra/Mi 10T/Mi 10T Pro),Alioth(Redmi K40/POCO F3/Mi 11X),Munch(Redmi K40S/POCO F4), both MIUI and AOSP.
lateautumn233android_kernel_oneplus_sm8250OnePlus 8 Serials
Sreeshankar KNeverSettle KernelOnePlus Nord (avicii)
Molyuuneko_kernel_xiaomi_gauguinRedmi Note 9 Pro/ Mi 10T Lite/ Mi 10i
guh0613android_kernel_oppo_sm8150OPPO Reno Ace (OP4A89)
LeviMarvinandroid_kernel_xiaomi_aliothRedmi K40 / POCO F3
cibimokernel_xiaomi_raphael_ksuXiaomi Redmi K20 Pro / Mi 9T Pro (raphael)
EndCreditskernel_xiaomi_sm7250Redmi K30 5G ( picasso ) , Redmi K30i 5G ( picasso_48m)
msnxandroid-msm-coral-4.14-android13Pixel 4 XL
SoDebugkernel_xiaomi_raphaelXiaomi Redmi K20 Pro / Mi 9T Pro (raphael)
H1mJTkernel_realme_RMX1901Realme X (RMX1901/RMX1901CN)
SonalSingh18android_kernel_xiaomi_sm6250Miatoll [curtana, excalibur, gram, joyeuse]
RooGhz720kernel_xiaomi_lavenderRedmi Note 7 (Lavender)
JunASAKAkernel_google_msm-4.9_KernelSUGoogle Pixel 3a & 3a XL (sargo & bonito)
RooGhz720kernel_asus_X01BDAsus Zenfone Max Pro M1/M2
Evans Mikekernel_xiaomi_raphael_bool-xXiaomi Redmi K20 Pro / Mi 9T Pro (raphael)
easterNdayKSU_Thyme_BuildBotXiaomi 10S
tedomi2705kernel_xiaomi_sdm660Xiaomi Redmi Note 6 Pro (tulip)
RyuujiXandroid_kernel_asus_sdm660-4.19Asus Zenfone Max Pro M1 (X00TD)/ M2 (X01BD) (Linux 4.19)
liqidecgandroid_kernel_xiaomi_sdm710Mi 8 SE (sirius) (Linux 4.9)
Abdelhay-Aliandroid_kernel_huawei_hi6250Huawei P20 lite (hi6250) (Linux 4.9)
reocatandroid_kernel_nokia_sdm660_ksuNokia 6.1 (2018)
Tuan Anhkernel_nokia_msm8998Nokia 8 (Repartitioned)/8 Sirocco (NLA/A1N)
bggRGjQaUbCoEandroid_kernel_samsung_sm8250Samsung Galaxy S20+ 5G (y2q)
SOVIET-ANDROIDkernel_xiaomi_raphaelXK20 Pro Raphael DSP & A only SAR support
dabao1955android_kernel_OPPO_PEQM00MediaTek devices in the OPPO Reno series on ColorOS13.x
Aflaungosandroid_kernel_motorola_msm8998Moto G6 Plus (evert)
TheNoFacekernel_oneplus_msm8998OnePlus 5/5T (cheeseburger/dumpling)
Nipin NA (Joker-V2)Xcalibur kernel (violet)Xiaomi Redmi Note 7 Pro (violet)
Sr-Hankernel_xiaomi_mojitoRedmi Note 10 (Sunny/Mojito)
dabao1955android_kernel_OPPO_OP4ED5OPPO Reno6 on ColorOS11.x/ColorOS12.x,kernel version4.14.186+
rushiranpiseandroid_kernel_samsung_exynos9610_mintKernel 4.14.194 exynos9610 Non-GKI Device, Added KernelSu using manual method
CN-Scarspixel_experience_kernel_xiaomi_sm8250_kernelSUPixel Experience Kernel 4.19.282 for Xiaomi Redmi K40S / Xiaomi Poco F4 (munch)
exerMiyo TokuSony Tama (akari, apollo, aurora, akatsuki) (Linux 4.9)
likkaiQuicksilver KernelXiaomi 11 Lite 5G NE (lisa)
awakened1712android_kernel_samsung_exynos9820Samsung S10/N10
awakened1712android_kernel_oneplus_sm8350Oneplus 9/9Pro
siimsekLightning KernelXiaomi Redmi Note 8/8T (ginkgo/willow)
GiovanYCringekernel_a50Galaxy A50 (A505f,fn,g,gn,gt)
Asriadi Rahimandroid_kernel_google_wahooGoogle Pixel 2/2XL
Flameandroid_kernel_xiaomi_sm8250POCO F3/Redmi K40/Mi 11X (alioth)
DawfukFRkernel_oneplus_sm8250 (Stellaris-Kernel)Oneplus 8 (Instantnoodle), Oneplus 8T (Kebab), Oneplus 8 Pro (Instantnoodlep), Oneplus 9R (lemonades) [msm-4.19]
Tejas Singhkernel_xiaomi_ginkgo (Cuh Kernel)Xiaomi Redmi Note 8/8T (ginkgo/willow)
Coconutatandroid_kernel_xiaomi_ruby_expRedmi Note 12 Pro / Pro+ For MIUI 14
Coconutatandroid_kernel_xiaomi_sdm845_expXiaomi Mi 8 Pro(UD)[equuleus]/Explorer Edition[ursa]
Abdul Wahid Khankernel_xiaomi_begoniaRedmi Note 8 Pro (Begonia/Begoniain)
Abdul Wahid Khankernel_xiaomi_marbleXiaomi Poco F5 / Redmi Note 12 Turbo (Marble)
zfdx123kernel_xiaomi_aliothRedmi K40 (alioth)
wxt1221android_kernel_oneplus_sdm845Oneplus 6 (enchilada) | Oneplus 6T (fajita) with no Retrofit Dynamic Partitions
zharzinhooKernel-Oriente-GuampMoto g9 play
hc841NovaCore_kernelMoto g9 play
zlm324android_kernel_xiaomi_msm8998_ksua lineage kernel forked from LineageOS official repository, version 4.4.302, added ksu.
zlm324android_kernel_xiaomi_msm8953_ksuXiaomi 5X (tiffany)
Vincent4440android_kernel_xiaomi_sm8250Poco F4: munch
zharzinhooandroid_kernel_motorola_cebuMoto g9 power
tiandoufayaleandroid_kernel_samsung_sm8250Samsung Tab S7 WIFI(T870)
starmoeandroid_kernel_oneplus_msm8998Oneplus 5/5T, kernel version 4.4.302
supechickenkernel_xiaomi_sm8350 Xiaomi 11T Pro (vili)
Vaz15kkernelSU-A70qSamsung Galaxy A70 ( A705MN / A705FN )
whyakariandroid_kernel_xiaomi_ginkgonon-GKI ginkgo
wulan17android_kernel_xiaomi_mt6765Redmi 6/6A (cereus/cactus)
officialputuidandroid_kernel_realme_mt6785Realme 6/6i(Indian)/6s/7/Narzo/Narzo 20 Pro/Narzo 30 4G (RM6785)

GPL3 ライセンスでリリースされています。

- +
Skip to content

非公式の対応デバイス

警告

このページでは他の開発者が管理している、KernelSU をサポートする GKI 以外のデバイス用のカーネルを紹介しています。

警告

このページはあなたのデバイスに対応するソースコードを見つけるためのものであり、そのソースコードが KernelSU 開発者 によってレビューされたことを意味するものではありません。ご自身の責任においてご利用ください。

メンテナーリポジトリ対応デバイス
diphonsD8G_Kernel_oxygenPoco F3: alioth | Poco F4: munch | MI10T/PRO: Apollo
diphonsD8G_Kernel_SM8150Poco X3 Pro: Vayu | Bhima
diphonsD8G_Kernel_SDM845Poco F1 | MI8 | MiMix2S
diphonsD8G_Kernel_SM8350Vili
diphonsD8G_Kernel_MarblePoco F5 | Redmi Note 12 Turbo | Marble
th1nhhdkandroid_kernel_sony_sm8250-kernelsuSony Xperia 1 II | Sony Xperia 5 II
akash07knexus_kernel_xiaomi_sm8250Poco F4: munch
HMTheBoy154Darkmatter-kernelGeneric x86_64 devices running Android-x86
Asuka-mioandroid_kernel_xiaomi_casMi 10 Ultra: cas
xiaoleGunMiku_kernel_xiaomi_waynewayne
YamazakuraHkernel_xiaomi_cepheuscepheus for pixel experience
SakuraNotStupidandroid_kernel_xiaomi_sdm710Xiaomi MI 8 SE(sirius/xmsirius)
Aquarius223(paper)android_kernel_xiaomi_msm8998MI 6 (sagit) and MIX 2 (chiron) for LineageOS
SlackerStateandroid_kernel_xiaomi_sm6150Redmi K30 4G (phoenix)
RooGhz720kernel_xiaomi_sm6150REDMI NOTE 10 PRO (sweet)
OnlyTomInSecondandroid_kernel_xiaomi_sdm845Mi 8 (dipper) for LineageOS
Rohail33RealKing KernelApollo(Redmi K30S Ultra/Mi 10T/Mi 10T Pro),Alioth(Redmi K40/POCO F3/Mi 11X),Munch(Redmi K40S/POCO F4), both MIUI and AOSP.
lateautumn233android_kernel_oneplus_sm8250OnePlus 8 Serials
Sreeshankar KNeverSettle KernelOnePlus Nord (avicii)
Molyuuneko_kernel_xiaomi_gauguinRedmi Note 9 Pro/ Mi 10T Lite/ Mi 10i
guh0613android_kernel_oppo_sm8150OPPO Reno Ace (OP4A89)
LeviMarvinandroid_kernel_xiaomi_aliothRedmi K40 / POCO F3
cibimokernel_xiaomi_raphael_ksuXiaomi Redmi K20 Pro / Mi 9T Pro (raphael)
EndCreditskernel_xiaomi_sm7250Redmi K30 5G ( picasso ) , Redmi K30i 5G ( picasso_48m)
msnxandroid-msm-coral-4.14-android13Pixel 4 XL
SoDebugkernel_xiaomi_raphaelXiaomi Redmi K20 Pro / Mi 9T Pro (raphael)
H1mJTkernel_realme_RMX1901Realme X (RMX1901/RMX1901CN)
SonalSingh18android_kernel_xiaomi_sm6250Miatoll [curtana, excalibur, gram, joyeuse]
RooGhz720kernel_xiaomi_lavenderRedmi Note 7 (Lavender)
JunASAKAkernel_google_msm-4.9_KernelSUGoogle Pixel 3a & 3a XL (sargo & bonito)
RooGhz720kernel_asus_X01BDAsus Zenfone Max Pro M1/M2
Evans Mikekernel_xiaomi_raphael_bool-xXiaomi Redmi K20 Pro / Mi 9T Pro (raphael)
easterNdayKSU_Thyme_BuildBotXiaomi 10S
tedomi2705kernel_xiaomi_sdm660Xiaomi Redmi Note 6 Pro (tulip)
RyuujiXandroid_kernel_asus_sdm660-4.19Asus Zenfone Max Pro M1 (X00TD)/ M2 (X01BD) (Linux 4.19)
liqidecgandroid_kernel_xiaomi_sdm710Mi 8 SE (sirius) (Linux 4.9)
Abdelhay-Aliandroid_kernel_huawei_hi6250Huawei P20 lite (hi6250) (Linux 4.9)
reocatandroid_kernel_nokia_sdm660_ksuNokia 6.1 (2018)
Tuan Anhkernel_nokia_msm8998Nokia 8 (Repartitioned)/8 Sirocco (NLA/A1N)
bggRGjQaUbCoEandroid_kernel_samsung_sm8250Samsung Galaxy S20+ 5G (y2q)
SOVIET-ANDROIDkernel_xiaomi_raphaelXK20 Pro Raphael DSP & A only SAR support
dabao1955android_kernel_OPPO_PEQM00MediaTek devices in the OPPO Reno series on ColorOS13.x
Aflaungosandroid_kernel_motorola_msm8998Moto G6 Plus (evert)
TheNoFacekernel_oneplus_msm8998OnePlus 5/5T (cheeseburger/dumpling)
Nipin NA (Joker-V2)Xcalibur kernel (violet)Xiaomi Redmi Note 7 Pro (violet)
Sr-Hankernel_xiaomi_mojitoRedmi Note 10 (Sunny/Mojito)
dabao1955android_kernel_OPPO_OP4ED5OPPO Reno6 on ColorOS11.x/ColorOS12.x,kernel version4.14.186+
rushiranpiseandroid_kernel_samsung_exynos9610_mintKernel 4.14.194 exynos9610 Non-GKI Device, Added KernelSu using manual method
CN-Scarspixel_experience_kernel_xiaomi_sm8250_kernelSUPixel Experience Kernel 4.19.282 for Xiaomi Redmi K40S / Xiaomi Poco F4 (munch)
exerMiyo TokuSony Tama (akari, apollo, aurora, akatsuki) (Linux 4.9)
likkaiQuicksilver KernelXiaomi 11 Lite 5G NE (lisa)
awakened1712android_kernel_samsung_exynos9820Samsung S10/N10
awakened1712android_kernel_oneplus_sm8350Oneplus 9/9Pro
siimsekLightning KernelXiaomi Redmi Note 8/8T (ginkgo/willow)
GiovanYCringekernel_a50Galaxy A50 (A505f,fn,g,gn,gt)
Asriadi Rahimandroid_kernel_google_wahooGoogle Pixel 2/2XL
Flameandroid_kernel_xiaomi_sm8250POCO F3/Redmi K40/Mi 11X (alioth)
DawfukFRkernel_oneplus_sm8250 (Stellaris-Kernel)Oneplus 8 (Instantnoodle), Oneplus 8T (Kebab), Oneplus 8 Pro (Instantnoodlep), Oneplus 9R (lemonades) [msm-4.19]
Tejas Singhkernel_xiaomi_ginkgo (Cuh Kernel)Xiaomi Redmi Note 8/8T (ginkgo/willow)
Coconutatandroid_kernel_xiaomi_ruby_expRedmi Note 12 Pro / Pro+ For MIUI 14
Coconutatandroid_kernel_xiaomi_sdm845_expXiaomi Mi 8 Pro(UD)[equuleus]/Explorer Edition[ursa]
Abdul Wahid Khankernel_xiaomi_begoniaRedmi Note 8 Pro (Begonia/Begoniain)
Abdul Wahid Khankernel_xiaomi_marbleXiaomi Poco F5 / Redmi Note 12 Turbo (Marble)
zfdx123kernel_xiaomi_aliothRedmi K40 (alioth)
wxt1221android_kernel_oneplus_sdm845Oneplus 6 (enchilada) | Oneplus 6T (fajita) with no Retrofit Dynamic Partitions
zharzinhooKernel-Oriente-GuampMoto g9 play
hc841NovaCore_kernelMoto g9 play
zlm324android_kernel_xiaomi_msm8998_ksua lineage kernel forked from LineageOS official repository, version 4.4.302, added ksu.
zlm324android_kernel_xiaomi_msm8953_ksuXiaomi 5X (tiffany)
Vincent4440android_kernel_xiaomi_sm8250Poco F4: munch
zharzinhooandroid_kernel_motorola_cebuMoto g9 power
tiandoufayaleandroid_kernel_samsung_sm8250Samsung Tab S7 WIFI(T870)
starmoeandroid_kernel_oneplus_msm8998Oneplus 5/5T, kernel version 4.4.302
supechickenkernel_xiaomi_sm8350 Xiaomi 11T Pro (vili)
Vaz15kkernelSU-A70qSamsung Galaxy A70 ( A705MN / A705FN )
whyakariandroid_kernel_xiaomi_ginkgonon-GKI ginkgo
wulan17android_kernel_xiaomi_mt6765Redmi 6/6A (cereus/cactus)
officialputuidandroid_kernel_realme_mt6785Realme 6/6i(Indian)/6s/7/Narzo/Narzo 20 Pro/Narzo 30 4G (RM6785)

GPL3 ライセンスでリリースされています。

+ \ No newline at end of file diff --git a/ja_JP/guide/what-is-kernelsu.html b/ja_JP/guide/what-is-kernelsu.html index 1cf7a1df39d3..7796d1528fdc 100644 --- a/ja_JP/guide/what-is-kernelsu.html +++ b/ja_JP/guide/what-is-kernelsu.html @@ -5,18 +5,19 @@ KernelSU とは? | KernelSU - - + + + - - - - + + + + + -
Skip to content
On this page

KernelSU とは?

KernelSU は Android GKI デバイスのための root ソリューションです。カーネルモードで動作し、カーネル空間で直接ユーザー空間アプリに root 権限を付与します。

機能

KernelSU の最大の特徴は、カーネルベースであることです。KernelSU はカーネルモードで動作するため、今までにないカーネルインターフェイスを提供できます。例えば、カーネルモードで任意のプロセスにハードウェアブレークポイントを追加できる、誰にも気づかれずに任意のプロセスの物理メモリにアクセスできる、カーネル空間で任意のシステムコールを傍受できる、などです。

また、KernelSU は OverlayFS によるモジュールシステムを提供しており、カスタムプラグインをシステムに読み込めます。/system パーティションを変更する仕組みも提供しています。

使用方法

こちらをご覧ください: インストール方法

ビルド方法

ビルドするには

ディスカッション

GPL3 ライセンスでリリースされています。

- +
Skip to content

KernelSU とは?

KernelSU は Android GKI デバイスのための root ソリューションです。カーネルモードで動作し、カーネル空間で直接ユーザー空間アプリに root 権限を付与します。

機能

KernelSU の最大の特徴は、カーネルベースであることです。KernelSU はカーネルモードで動作するため、今までにないカーネルインターフェイスを提供できます。例えば、カーネルモードで任意のプロセスにハードウェアブレークポイントを追加できる、誰にも気づかれずに任意のプロセスの物理メモリにアクセスできる、カーネル空間で任意のシステムコールを傍受できる、などです。

また、KernelSU は OverlayFS によるモジュールシステムを提供しており、カスタムプラグインをシステムに読み込めます。/system パーティションを変更する仕組みも提供しています。

使用方法

こちらをご覧ください: インストール方法

ビルド方法

ビルドするには

ディスカッション

GPL3 ライセンスでリリースされています。

+ \ No newline at end of file diff --git a/ja_JP/index.html b/ja_JP/index.html index 72c3748db6c3..0d7bfea372f8 100644 --- a/ja_JP/index.html +++ b/ja_JP/index.html @@ -5,18 +5,19 @@ Android 向けのカーネルベース root ソリューション | KernelSU - - + + + - - - - + + + + + -
Skip to content

KernelSU

Android 向けのカーネルベース root ソリューション

KernelSU

カーネルベース

KernelSU は Linux カーネルモードで動作し、ユーザー空間よりも高度な制御が可能です。

ホワイトリストの権限管理

root 権限を許可したアプリのみが su にアクセスでき、他のアプリは su を見つけられません。

モジュール対応

KernelSU は OverlayFS により実際のシステムを改変せずに /system を変更できます。書き込み可能にすることさえできます。

オープンソース

KernelSU は GPL-3 でライセンスされたオープンソースプロジェクトです。

GPL3 ライセンスでリリースされています。

- +
Skip to content

KernelSU

Android 向けのカーネルベース root ソリューション

KernelSU

GPL3 ライセンスでリリースされています。

+ \ No newline at end of file diff --git a/pt_BR/guide/app-profile.html b/pt_BR/guide/app-profile.html index 896ad697bb60..663fbcc3ef07 100644 --- a/pt_BR/guide/app-profile.html +++ b/pt_BR/guide/app-profile.html @@ -5,22 +5,27 @@ Perfil do Aplicativo | KernelSU - - + + + - - - - + + + + + -
Skip to content
On this page

Perfil do Aplicativo

O Perfil do Aplicativo é um mecanismo fornecido pelo KernelSU para personalizar a configuração de vários apps.

Para apps com permissões de root (ou seja, capazes de usar su), o Perfil do Aplicativo também pode ser chamado de Perfil Root. Ele permite a customização das regras uid, gid, groups, capabilities e SELinux do comando su, restringindo assim os privilégios do usuário root. Por exemplo, ele pode conceder permissões de rede apenas para apps de firewall enquanto nega permissões de acesso a arquivos, ou pode conceder permissões de shell em vez de acesso root completo para apps congelados: mantendo o poder confinado com o princípio do menor privilégio.

Para apps comuns sem permissões de root, o Perfil do Aplicativo pode controlar o comportamento do kernel e do sistema de módulos em relação a esses apps. Por exemplo, pode determinar se as modificações resultantes dos módulos devem ser abordadas. O kernel e o sistema de módulos podem tomar decisões com base nesta configuração, como realizar operações semelhantes a "ocultar"

Perfil Root

UID, GID e Grupos

Os sistemas Linux possuem dois conceitos: usuários e grupos. Cada usuário possui um ID de usuário (UID) e um usuário pode pertencer a vários grupos, cada um com seu próprio ID de grupo (GID). Esses IDs são usados ​​para identificar usuários no sistema e determinar quais recursos do sistema eles podem acessar.

Os usuários com UID 0 são conhecidos como usuários root e os grupos com GID 0 são conhecidos como grupos root. O grupo de usuários root normalmente possui os privilégios de sistema mais altos.

No caso do sistema Android, cada app é um usuário separado (excluindo cenários de UID compartilhados) com um UID exclusivo. Por exemplo, 0 representa o usuário root, 1000 representa system, 2000 representa o ADB shell e UIDs variando de 10.000 a 19.999 representam apps comuns.

INFORMAÇÕES

Aqui, o UID mencionado não é o mesmo que o conceito de múltiplos usuários ou perfis de trabalho no sistema Android. Os perfis de trabalho são, na verdade, implementados particionando o intervalo UID. Por exemplo, 10000-19999 representa o usuário principal, enquanto 110000-119999 representa um perfil de trabalho. Cada app comum entre eles possui seu próprio UID exclusivo.

Cada app pode ter vários grupos, com o GID representando o grupo principal, que geralmente corresponde ao UID. Outros grupos são conhecidos como grupos suplementares. Certas permissões são controladas por meio de grupos, como permissões de acesso à rede ou acesso Bluetooth.

Por exemplo, se executarmos o comando id no ADB shell, a saída pode ser semelhante a esta:

sh
oriole:/ $ id
-uid=2000(shell) gid=2000(shell) groups=2000(shell),1004(input),1007(log),1011(adb),1015(sdcard_rw),1028(sdcard_r),1078(ext_data_rw),1079(ext_obb_rw),3001(net_bt_admin),3002(net_bt),3003(inet),3006(net_bw_stats),3009(readproc),3011(uhid),3012(readtracefs) context=u:r:shell:s0

Aqui, o UID é 2000 e o GID (ID do grupo primário) também é 2000. Além disso, pertence a vários grupos suplementares, como inet (indicando a capacidade de criar soquetes AF_INET e AF_INET6) e sdcard_rw (indicando permissões de leitura/gravação para o cartão SD).

O Perfil Root do KernelSU permite a personalização do UID, GID e grupos para o processo root após a execução de su. Por exemplo, o Perfil Root de um app root pode definir seu UID como 2000, que significa que ao usar su, as permissões reais do app estão no nível do ADB shell. O grupo inet pode ser removido, evitando que o comando su acesse a rede.

OBSERVAÇÃO

O Perfil do Aplicativo controla apenas as permissões do processo root após usar su, e ele não controla as permissões do próprio app. Se um app solicitou permissão de acesso à rede, ele ainda poderá acessar a rede mesmo sem usar su. Remover o grupo inet de su apenas impede que su acesse a rede.

O Perfil Root é aplicado no kernel e não depende do comportamento voluntário de apps root, ao contrário da troca de usuários ou grupos por meio de su, a concessão da permissão su depende inteiramente do usuário e não do desenvolvedor.

Capacidades

As capacidades são um mecanismo para separação de privilégios no Linux.

Para realizar verificações de permissão, as implementações tradicionais do UNIX distinguem duas categorias de processos: processos privilegiados (cujo ID de usuário efetivo é 0, referido como superusuário ou root) e processos sem privilégios (cujo UID efetivo é diferente de zero). Os processos privilegiados ignoram todas as verificações de permissão do kernel, enquanto os processos não privilegiados estão sujeitos à verificação completa de permissão com base nas credenciais do processo (geralmente: UID efetivo, GID efetivo e lista de grupos suplementares).

A partir do Linux 2.2, o Linux divide os privilégios tradicionalmente associados ao superusuário em unidades distintas, conhecidas como capacidades, que podem ser ativadas e desativadas de forma independente.

Cada capacidade representa um ou mais privilégios. Por exemplo, CAP_DAC_READ_SEARCH representa a capacidade de ignorar verificações de permissão para leitura de arquivos, bem como permissões de leitura e execução de diretório. Se um usuário com um UID efetivo 0 (usuário root) não tiver recursos CAP_DAC_READ_SEARCH ou superiores, isso significa que mesmo sendo root, ele não pode ler arquivos à vontade.

O Perfil Root do KernelSU permite a personalização das capacidades do processo root após a execução de su, conseguindo assim conceder parcialmente "permissões de root". Ao contrário do UID e GID mencionados acima, certos apps root exigem um UID de 0 após usar su. Nesses casos, limitar as capacidades deste usuário root com UID 0 pode restringir suas operações permitidas.

FORTE RECOMENDAÇÃO

A documentação oficial da Capacidade do Linux fornece explicações detalhadas das habilidades representadas por cada Capacidade. Se você pretende customizar Capacidades, é altamente recomendável que você leia este documento primeiro.

SELinux

SELinux é um poderoso mecanismo do Controle de Acesso Obrigatório (MAC). Ele opera com base no princípio de negação padrão. Qualquer ação não explicitamente permitida é negada.

O SELinux pode ser executado em dois modos globais:

  1. Modo permissivo: Os eventos de negação são registrados, mas não aplicados.
  2. Modo de aplicação: Os eventos de negação são registrados e aplicados.

AVISO

Os sistemas Android modernos dependem fortemente do SELinux para garantir a segurança geral do sistema. É altamente recomendável não usar nenhum sistema personalizado executado em "Modo permissivo", pois não oferece vantagens significativas em relação a um sistema completamente aberto.

Explicar o conceito completo do SELinux é complexo e está além do objetivo deste documento. Recomenda-se primeiro entender seu funcionamento através dos seguintes recursos:

  1. Wikipédia
  2. Red Hat: O que é SELinux?
  3. ArchLinux: SELinux

O Perfil Root do KernelSU permite a personalização do contexto SELinux do processo root após a execução de su. Regras específicas de controle de acesso podem ser definidas para este contexto para permitir um controle refinado sobre as permissões de root.

Em cenários típicos, quando um app executa su, ele alterna o processo para um domínio SELinux com acesso irrestrito, como u:r:su:s0. Através do Perfil Root, este domínio pode ser mudado para um domínio personalizado, como u:r:app1:s0, e uma série de regras podem ser definidas para este domínio:

sh
type app1
-enforce app1
-typeattribute app1 mlstrustedsubject
-allow app1 * * *

Observe que a regra allow app1 * * * é usada apenas para fins de demonstração. Na prática, esta regra não deve ser utilizada extensivamente, pois não difere muito do Modo permissivo.

Escalação

Se a configuração do Perfil Root não estiver definida corretamente, poderá ocorrer um cenário de escalação. As restrições impostas pelo Perfil Root poderão falhar involuntariamente.

Por exemplo, se você conceder permissão root a um usuário ADB shell (que é um caso comum) e, em seguida, conceder permissão root a um app normal, mas configurar seu Perfil Root com UID 2000 (que é o UID do usuário ADB shell), o app pode obter acesso root completo executando o comando su duas vezes:

  1. A primeira execução su está sujeita à aplicação do Perfil do Aplicativo e mudará para UID 2000 (adb shell) em vez de 0 (root).
  2. A segunda execução su, como o UID é 2000 e você concedeu acesso root ao UID 2000 (adb shell) na configuração, o app obterá privilégio de root completo.

OBSERVAÇÃO

Este comportamento é totalmente esperado e não é um bug. Portanto, recomendamos o seguinte:

Se você realmente precisa conceder permissões de root ao ADB (por exemplo, como desenvolvedor), não é aconselhável alterar o UID para 2000 ao configurar o Perfil Root. Usar 1000 (system) seria uma melhor escolha.

Perfil não Root

Desmontar módulos

O KernelSU fornece um mecanismo sem sistema para modificar partições do sistema, obtido através da montagem de overlayfs. No entanto, alguns apps podem ser sensíveis a esse comportamento. Assim, podemos descarregar módulos montados nesses apps configurando a opção “desmontar módulos”.

Além disso, a interface de configurações do gerenciador KernelSU fornece uma opção para "desmontar módulos por padrão". Por padrão, essa opção está ativada, o que significa que o KernelSU ou alguns módulos descarregarão módulos para este app, a menos que configurações adicionais sejam aplicadas. Se você não preferir esta configuração ou se ela afetar determinados apps, você terá as seguintes opções:

  1. Mantenha a opção "desmontar módulos por padrão" e desative individualmente a opção "desmontar módulos" no Perfil do Aplicativo para apps que exigem carregamento do módulo (agindo como uma "lista de permissões").
  2. Desative a opção "desmontar módulos por padrão" e ative individualmente a opção "desmontar módulos" no Perfil do Aplicativo para apps que exigem descarregamento do módulo (agindo como uma "lista negra").

INFORMAÇÕES

Em dispositivos que utilizam kernel versão 5.10 e superior, o kernel realiza o descarregamento dos módulos. No entanto, para dispositivos que executam versões de kernel abaixo de 5.10, essa opção é apenas uma opção de configuração e o próprio KernelSU não executa nenhuma ação. Alguns módulos, como Zygisksu, podem usar essa opção para determinar se o descarregamento do módulo é necessário.

Lançado sob a Licença GPL3.

- +
Skip to content

Perfil do Aplicativo

O Perfil do Aplicativo é um mecanismo fornecido pelo KernelSU para personalizar a configuração de vários apps.

Para apps com permissões de root (ou seja, capazes de usar su), o Perfil do Aplicativo também pode ser chamado de Perfil Root. Ele permite a customização das regras uid, gid, groups, capabilities e SELinux do comando su, restringindo assim os privilégios do usuário root. Por exemplo, ele pode conceder permissões de rede apenas para apps de firewall enquanto nega permissões de acesso a arquivos, ou pode conceder permissões de shell em vez de acesso root completo para apps congelados: mantendo o poder confinado com o princípio do menor privilégio.

Para apps comuns sem permissões de root, o Perfil do Aplicativo pode controlar o comportamento do kernel e do sistema de módulos em relação a esses apps. Por exemplo, pode determinar se as modificações resultantes dos módulos devem ser abordadas. O kernel e o sistema de módulos podem tomar decisões com base nesta configuração, como realizar operações semelhantes a "ocultar"

Perfil Root

UID, GID e Grupos

Os sistemas Linux possuem dois conceitos: usuários e grupos. Cada usuário possui um ID de usuário (UID) e um usuário pode pertencer a vários grupos, cada um com seu próprio ID de grupo (GID). Esses IDs são usados ​​para identificar usuários no sistema e determinar quais recursos do sistema eles podem acessar.

Os usuários com UID 0 são conhecidos como usuários root e os grupos com GID 0 são conhecidos como grupos root. O grupo de usuários root normalmente possui os privilégios de sistema mais altos.

No caso do sistema Android, cada app é um usuário separado (excluindo cenários de UID compartilhados) com um UID exclusivo. Por exemplo, 0 representa o usuário root, 1000 representa system, 2000 representa o ADB shell e UIDs variando de 10.000 a 19.999 representam apps comuns.

INFORMAÇÕES

Aqui, o UID mencionado não é o mesmo que o conceito de múltiplos usuários ou perfis de trabalho no sistema Android. Os perfis de trabalho são, na verdade, implementados particionando o intervalo UID. Por exemplo, 10000-19999 representa o usuário principal, enquanto 110000-119999 representa um perfil de trabalho. Cada app comum entre eles possui seu próprio UID exclusivo.

Cada app pode ter vários grupos, com o GID representando o grupo principal, que geralmente corresponde ao UID. Outros grupos são conhecidos como grupos suplementares. Certas permissões são controladas por meio de grupos, como permissões de acesso à rede ou acesso Bluetooth.

Por exemplo, se executarmos o comando id no ADB shell, a saída pode ser semelhante a esta:

sh
oriole:/ $ id
+uid=2000(shell) gid=2000(shell) groups=2000(shell),1004(input),1007(log),1011(adb),1015(sdcard_rw),1028(sdcard_r),1078(ext_data_rw),1079(ext_obb_rw),3001(net_bt_admin),3002(net_bt),3003(inet),3006(net_bw_stats),3009(readproc),3011(uhid),3012(readtracefs) context=u:r:shell:s0
oriole:/ $ id
+uid=2000(shell) gid=2000(shell) groups=2000(shell),1004(input),1007(log),1011(adb),1015(sdcard_rw),1028(sdcard_r),1078(ext_data_rw),1079(ext_obb_rw),3001(net_bt_admin),3002(net_bt),3003(inet),3006(net_bw_stats),3009(readproc),3011(uhid),3012(readtracefs) context=u:r:shell:s0

Aqui, o UID é 2000 e o GID (ID do grupo primário) também é 2000. Além disso, pertence a vários grupos suplementares, como inet (indicando a capacidade de criar soquetes AF_INET e AF_INET6) e sdcard_rw (indicando permissões de leitura/gravação para o cartão SD).

O Perfil Root do KernelSU permite a personalização do UID, GID e grupos para o processo root após a execução de su. Por exemplo, o Perfil Root de um app root pode definir seu UID como 2000, que significa que ao usar su, as permissões reais do app estão no nível do ADB shell. O grupo inet pode ser removido, evitando que o comando su acesse a rede.

OBSERVAÇÃO

O Perfil do Aplicativo controla apenas as permissões do processo root após usar su, e ele não controla as permissões do próprio app. Se um app solicitou permissão de acesso à rede, ele ainda poderá acessar a rede mesmo sem usar su. Remover o grupo inet de su apenas impede que su acesse a rede.

O Perfil Root é aplicado no kernel e não depende do comportamento voluntário de apps root, ao contrário da troca de usuários ou grupos por meio de su, a concessão da permissão su depende inteiramente do usuário e não do desenvolvedor.

Capacidades

As capacidades são um mecanismo para separação de privilégios no Linux.

Para realizar verificações de permissão, as implementações tradicionais do UNIX distinguem duas categorias de processos: processos privilegiados (cujo ID de usuário efetivo é 0, referido como superusuário ou root) e processos sem privilégios (cujo UID efetivo é diferente de zero). Os processos privilegiados ignoram todas as verificações de permissão do kernel, enquanto os processos não privilegiados estão sujeitos à verificação completa de permissão com base nas credenciais do processo (geralmente: UID efetivo, GID efetivo e lista de grupos suplementares).

A partir do Linux 2.2, o Linux divide os privilégios tradicionalmente associados ao superusuário em unidades distintas, conhecidas como capacidades, que podem ser ativadas e desativadas de forma independente.

Cada capacidade representa um ou mais privilégios. Por exemplo, CAP_DAC_READ_SEARCH representa a capacidade de ignorar verificações de permissão para leitura de arquivos, bem como permissões de leitura e execução de diretório. Se um usuário com um UID efetivo 0 (usuário root) não tiver recursos CAP_DAC_READ_SEARCH ou superiores, isso significa que mesmo sendo root, ele não pode ler arquivos à vontade.

O Perfil Root do KernelSU permite a personalização das capacidades do processo root após a execução de su, conseguindo assim conceder parcialmente "permissões de root". Ao contrário do UID e GID mencionados acima, certos apps root exigem um UID de 0 após usar su. Nesses casos, limitar as capacidades deste usuário root com UID 0 pode restringir suas operações permitidas.

FORTE RECOMENDAÇÃO

A documentação oficial da Capacidade do Linux fornece explicações detalhadas das habilidades representadas por cada Capacidade. Se você pretende customizar Capacidades, é altamente recomendável que você leia este documento primeiro.

SELinux

SELinux é um poderoso mecanismo do Controle de Acesso Obrigatório (MAC). Ele opera com base no princípio de negação padrão. Qualquer ação não explicitamente permitida é negada.

O SELinux pode ser executado em dois modos globais:

  1. Modo permissivo: Os eventos de negação são registrados, mas não aplicados.
  2. Modo de aplicação: Os eventos de negação são registrados e aplicados.

AVISO

Os sistemas Android modernos dependem fortemente do SELinux para garantir a segurança geral do sistema. É altamente recomendável não usar nenhum sistema personalizado executado em "Modo permissivo", pois não oferece vantagens significativas em relação a um sistema completamente aberto.

Explicar o conceito completo do SELinux é complexo e está além do objetivo deste documento. Recomenda-se primeiro entender seu funcionamento através dos seguintes recursos:

  1. Wikipédia
  2. Red Hat: O que é SELinux?
  3. ArchLinux: SELinux

O Perfil Root do KernelSU permite a personalização do contexto SELinux do processo root após a execução de su. Regras específicas de controle de acesso podem ser definidas para este contexto para permitir um controle refinado sobre as permissões de root.

Em cenários típicos, quando um app executa su, ele alterna o processo para um domínio SELinux com acesso irrestrito, como u:r:su:s0. Através do Perfil Root, este domínio pode ser mudado para um domínio personalizado, como u:r:app1:s0, e uma série de regras podem ser definidas para este domínio:

sh
type app1
+enforce app1
+typeattribute app1 mlstrustedsubject
+allow app1 * * *
type app1
+enforce app1
+typeattribute app1 mlstrustedsubject
+allow app1 * * *

Observe que a regra allow app1 * * * é usada apenas para fins de demonstração. Na prática, esta regra não deve ser utilizada extensivamente, pois não difere muito do Modo permissivo.

Escalação

Se a configuração do Perfil Root não estiver definida corretamente, poderá ocorrer um cenário de escalação. As restrições impostas pelo Perfil Root poderão falhar involuntariamente.

Por exemplo, se você conceder permissão root a um usuário ADB shell (que é um caso comum) e, em seguida, conceder permissão root a um app normal, mas configurar seu Perfil Root com UID 2000 (que é o UID do usuário ADB shell), o app pode obter acesso root completo executando o comando su duas vezes:

  1. A primeira execução su está sujeita à aplicação do Perfil do Aplicativo e mudará para UID 2000 (adb shell) em vez de 0 (root).
  2. A segunda execução su, como o UID é 2000 e você concedeu acesso root ao UID 2000 (adb shell) na configuração, o app obterá privilégio de root completo.

OBSERVAÇÃO

Este comportamento é totalmente esperado e não é um bug. Portanto, recomendamos o seguinte:

Se você realmente precisa conceder permissões de root ao ADB (por exemplo, como desenvolvedor), não é aconselhável alterar o UID para 2000 ao configurar o Perfil Root. Usar 1000 (system) seria uma melhor escolha.

Perfil não Root

Desmontar módulos

O KernelSU fornece um mecanismo sem sistema para modificar partições do sistema, obtido através da montagem de overlayfs. No entanto, alguns apps podem ser sensíveis a esse comportamento. Assim, podemos descarregar módulos montados nesses apps configurando a opção “desmontar módulos”.

Além disso, a interface de configurações do gerenciador KernelSU fornece uma opção para "desmontar módulos por padrão". Por padrão, essa opção está ativada, o que significa que o KernelSU ou alguns módulos descarregarão módulos para este app, a menos que configurações adicionais sejam aplicadas. Se você não preferir esta configuração ou se ela afetar determinados apps, você terá as seguintes opções:

  1. Mantenha a opção "desmontar módulos por padrão" e desative individualmente a opção "desmontar módulos" no Perfil do Aplicativo para apps que exigem carregamento do módulo (agindo como uma "lista de permissões").
  2. Desative a opção "desmontar módulos por padrão" e ative individualmente a opção "desmontar módulos" no Perfil do Aplicativo para apps que exigem descarregamento do módulo (agindo como uma "lista negra").

INFORMAÇÕES

Em dispositivos que utilizam kernel versão 5.10 e superior, o kernel realiza o descarregamento dos módulos. No entanto, para dispositivos que executam versões de kernel abaixo de 5.10, essa opção é apenas uma opção de configuração e o próprio KernelSU não executa nenhuma ação. Alguns módulos, como Zygisksu, podem usar essa opção para determinar se o descarregamento do módulo é necessário.

Lançado sob a Licença GPL3.

+ \ No newline at end of file diff --git a/pt_BR/guide/difference-with-magisk.html b/pt_BR/guide/difference-with-magisk.html index 3a042531154d..c5baeb47f815 100644 --- a/pt_BR/guide/difference-with-magisk.html +++ b/pt_BR/guide/difference-with-magisk.html @@ -5,18 +5,19 @@ Diferença com Magisk | KernelSU - - + + + - - - - + + + + + -
Skip to content
On this page

Diferença com Magisk

Embora existam muitas semelhanças entre os módulos KernelSU e os módulos Magisk, existem inevitavelmente algumas diferenças devido aos seus mecanismos de implementação completamente diferentes. Se você deseja que seu módulo seja executado no Magisk e no KernelSU, você deve entender essas diferenças.

Semelhanças

  • Formato de arquivo do módulo: ambos usam o formato zip para organizar os módulos, e o formato dos módulos é quase o mesmo.
  • Diretório de instalação do módulo: ambos localizados em /data/adb/modules.
  • Sem sistema: ambos suportam a modificação de /system de maneira sem sistema por meio de módulos.
  • post-fs-data.sh: o tempo de execução e a semântica são exatamente os mesmos.
  • service.sh: o tempo de execução e a semântica são exatamente os mesmos.
  • system.prop: completamente o mesmo.
  • sepolicy.rule: completamente o mesmo.
  • BusyBox: os scripts são executados no BusyBox com o "Modo Autônomo" ativado em ambos os casos.

Diferenças

Antes de entender as diferenças, você precisa saber diferenciar se o seu módulo está rodando no KernelSU ou Magisk. Você pode usar a variável de ambiente KSU para diferenciá-la em todos os locais onde você pode executar os scripts do módulo (customize.sh, post-fs-data.sh, service.sh). No KernelSU, esta variável de ambiente será definida como true.

Aqui estão algumas diferenças:

  • Os módulos KernelSU não podem ser instalados no modo Recovery.
  • Os módulos KernelSU não têm suporte integrado para Zygisk (mas você pode usar módulos Zygisk através do ZygiskNext.
  • O método para substituir ou excluir arquivos nos módulos KernelSU é completamente diferente do Magisk. O KernelSU não suporta o método .replace. Em vez disso, você precisa criar um arquivo com o mesmo nome mknod filename c 0 0 para excluir o arquivo correspondente.
  • Os diretórios do BusyBox são diferentes. O BusyBox integrado no KernelSU está localizado em /data/adb/ksu/bin/busybox, enquanto no Magisk está em /data/adb/magisk/busybox. Observe que este é um comportamento interno do KernelSU e pode mudar no futuro!
  • O KernelSU não suporta arquivos .replace, entretanto, o KernelSU suporta as variáveis ​​REMOVE e REPLACE para remover ou substituir arquivos e pastas.
  • O KernelSU adiciona o estágio boot-completed para executar alguns scripts na inicialização concluída.
  • O KernelSU adiciona o estágio post-mount para executar alguns scripts após montar overlayfs.

Lançado sob a Licença GPL3.

- +
Skip to content

Diferença com Magisk

Embora existam muitas semelhanças entre os módulos KernelSU e os módulos Magisk, existem inevitavelmente algumas diferenças devido aos seus mecanismos de implementação completamente diferentes. Se você deseja que seu módulo seja executado no Magisk e no KernelSU, você deve entender essas diferenças.

Semelhanças

  • Formato de arquivo do módulo: ambos usam o formato zip para organizar os módulos, e o formato dos módulos é quase o mesmo.
  • Diretório de instalação do módulo: ambos localizados em /data/adb/modules.
  • Sem sistema: ambos suportam a modificação de /system de maneira sem sistema por meio de módulos.
  • post-fs-data.sh: o tempo de execução e a semântica são exatamente os mesmos.
  • service.sh: o tempo de execução e a semântica são exatamente os mesmos.
  • system.prop: completamente o mesmo.
  • sepolicy.rule: completamente o mesmo.
  • BusyBox: os scripts são executados no BusyBox com o "Modo Autônomo" ativado em ambos os casos.

Diferenças

Antes de entender as diferenças, você precisa saber diferenciar se o seu módulo está rodando no KernelSU ou Magisk. Você pode usar a variável de ambiente KSU para diferenciá-la em todos os locais onde você pode executar os scripts do módulo (customize.sh, post-fs-data.sh, service.sh). No KernelSU, esta variável de ambiente será definida como true.

Aqui estão algumas diferenças:

  • Os módulos KernelSU não podem ser instalados no modo Recovery.
  • Os módulos KernelSU não têm suporte integrado para Zygisk (mas você pode usar módulos Zygisk através do ZygiskNext.
  • O método para substituir ou excluir arquivos nos módulos KernelSU é completamente diferente do Magisk. O KernelSU não suporta o método .replace. Em vez disso, você precisa criar um arquivo com o mesmo nome mknod filename c 0 0 para excluir o arquivo correspondente.
  • Os diretórios do BusyBox são diferentes. O BusyBox integrado no KernelSU está localizado em /data/adb/ksu/bin/busybox, enquanto no Magisk está em /data/adb/magisk/busybox. Observe que este é um comportamento interno do KernelSU e pode mudar no futuro!
  • O KernelSU não suporta arquivos .replace, entretanto, o KernelSU suporta as variáveis ​​REMOVE e REPLACE para remover ou substituir arquivos e pastas.
  • O KernelSU adiciona o estágio boot-completed para executar alguns scripts na inicialização concluída.
  • O KernelSU adiciona o estágio post-mount para executar alguns scripts após montar overlayfs.

Lançado sob a Licença GPL3.

+ \ No newline at end of file diff --git a/pt_BR/guide/faq.html b/pt_BR/guide/faq.html index 8118139be5ad..d3084f5732d7 100644 --- a/pt_BR/guide/faq.html +++ b/pt_BR/guide/faq.html @@ -5,18 +5,19 @@ FAQ | KernelSU - - + + + - - - - + + + + + -
Skip to content
On this page

FAQ

KernelSU oferece suporte ao meu dispositivo?

Primeiro, seu dispositivo deve ser capaz de desbloquear o bootloader. Se não, então não há suporte.

Em seguida, instale o app gerenciador KernelSU em seu dispositivo e abra-o, se mostrar Sem suporte então seu dispositivo não pode ser suportado imediatamente, mas você pode construir a fonte do kernel e integrar o KernelSU para fazê-lo funcionar ou usar dispositivos com suporte não oficial.

KernelSU precisa desbloquear o Bootloader?

Certamente, sim.

KernelSU suporta módulos?

Sim, mas está na versão inicial, pode apresentar bugs. Por favor, aguarde até que fique estável.

KernelSU suporta Xposed?

Sim, Dreamland e TaiChi funcionam agora. Para o LSPosed, você pode fazer funcionar usando ZygiskNext.

KernelSU suporta Zygisk?

KernelSU não tem suporte integrado ao Zygisk, mas você pode usar ZygiskNext.

KernelSU é compatível com Magisk?

O sistema de módulos do KernelSU está em conflito com a montagem mágica do Magisk, se houver algum módulo habilitado no KernelSU, então todo o Magisk não funcionaria.

Mas se você usar apenas o su do KernelSU, então funcionará bem com o Magisk. KernelSU modifica o kernel e o Magisk modifica o ramdisk, eles podem trabalhar juntos.

KernelSU substituirá o Magisk?

Achamos que não e esse não é o nosso objetivo. O Magisk é bom o suficiente para solução root do espaço do usuário e terá uma longa vida. O objetivo do KernelSU é fornecer uma interface de kernel aos usuários, não substituindo o Magisk.

KernelSU pode oferecer suporte a dispositivos não GKI?

É possível. Mas você deve baixar o código-fonte do kernel e integrar o KernelSU à árvore do código-fonte e compilar o kernel você mesmo.

KernelSU pode oferecer suporte a dispositivos abaixo do Android 12?

É o kernel do dispositivo que afeta a compatibilidade do KernelSU e não tem nada a ver com a versão do Android. A única restrição é que os dispositivos lançados com Android 12 devem ser kernel 5.10+ (dispositivos GKI). Então:

  1. Os dispositivos lançados com Android 12 devem ser compatíveis.
  2. Dispositivos com kernel antigo (alguns dispositivos Android 12 também têm o kernel antigo) são compatíveis (você mesmo deve construir o kernel).

KernelSU pode suportar kernel antigo?

É possível, o KernelSU é portado para o kernel 4.14 agora, para o kernel mais antigo, você precisa fazer o backport manualmente e PRs são bem-vindos!

Como integrar o KernelSU para o kernel antigo?

Por favor, consulte a guia Como integrar o KernelSU para kernels não GKI

Por que minha versão do Android é 13 e o kernel mostra “android12-5.10”?

A versão do Kernel não tem nada a ver com a versão do Android, se você precisar fazer o flash do kernel, use sempre a versão do kernel, a versão do Android não é tão importante.

Existe algum namespace de montagem --mount-master/global no KernelSU?

Não existe agora (talvez no futuro), mas há muitas maneiras de mudar manualmente para o namespace de montagem global, como:

  1. nsenter -t 1 -m sh para obter um shell no namespace de montagem global.
  2. Adicione nsenter --mount=/proc/1/ns/mnt ao comando que você deseja executar, o comando será executado no namespace de montagem global. O KernelSU também está usando desta forma

Eu sou GKI1.0, posso usar isso?

GKI1 é completamente diferente do GKI2, você deve compilar o kernel sozinho.

Lançado sob a Licença GPL3.

- +
Skip to content

FAQ

KernelSU oferece suporte ao meu dispositivo?

Primeiro, seu dispositivo deve ser capaz de desbloquear o bootloader. Se não, então não há suporte.

Em seguida, instale o app gerenciador KernelSU em seu dispositivo e abra-o, se mostrar Sem suporte então seu dispositivo não pode ser suportado imediatamente, mas você pode construir a fonte do kernel e integrar o KernelSU para fazê-lo funcionar ou usar dispositivos com suporte não oficial.

KernelSU precisa desbloquear o Bootloader?

Certamente, sim.

KernelSU suporta módulos?

Sim, mas está na versão inicial, pode apresentar bugs. Por favor, aguarde até que fique estável.

KernelSU suporta Xposed?

Sim, Dreamland e TaiChi funcionam agora. Para o LSPosed, você pode fazer funcionar usando ZygiskNext.

KernelSU suporta Zygisk?

KernelSU não tem suporte integrado ao Zygisk, mas você pode usar ZygiskNext.

KernelSU é compatível com Magisk?

O sistema de módulos do KernelSU está em conflito com a montagem mágica do Magisk, se houver algum módulo habilitado no KernelSU, então todo o Magisk não funcionaria.

Mas se você usar apenas o su do KernelSU, então funcionará bem com o Magisk. KernelSU modifica o kernel e o Magisk modifica o ramdisk, eles podem trabalhar juntos.

KernelSU substituirá o Magisk?

Achamos que não e esse não é o nosso objetivo. O Magisk é bom o suficiente para solução root do espaço do usuário e terá uma longa vida. O objetivo do KernelSU é fornecer uma interface de kernel aos usuários, não substituindo o Magisk.

KernelSU pode oferecer suporte a dispositivos não GKI?

É possível. Mas você deve baixar o código-fonte do kernel e integrar o KernelSU à árvore do código-fonte e compilar o kernel você mesmo.

KernelSU pode oferecer suporte a dispositivos abaixo do Android 12?

É o kernel do dispositivo que afeta a compatibilidade do KernelSU e não tem nada a ver com a versão do Android. A única restrição é que os dispositivos lançados com Android 12 devem ser kernel 5.10+ (dispositivos GKI). Então:

  1. Os dispositivos lançados com Android 12 devem ser compatíveis.
  2. Dispositivos com kernel antigo (alguns dispositivos Android 12 também têm o kernel antigo) são compatíveis (você mesmo deve construir o kernel).

KernelSU pode suportar kernel antigo?

É possível, o KernelSU é portado para o kernel 4.14 agora, para o kernel mais antigo, você precisa fazer o backport manualmente e PRs são bem-vindos!

Como integrar o KernelSU para o kernel antigo?

Por favor, consulte a guia Como integrar o KernelSU para kernels não GKI

Por que minha versão do Android é 13 e o kernel mostra “android12-5.10”?

A versão do Kernel não tem nada a ver com a versão do Android, se você precisar fazer o flash do kernel, use sempre a versão do kernel, a versão do Android não é tão importante.

Existe algum namespace de montagem --mount-master/global no KernelSU?

Não existe agora (talvez no futuro), mas há muitas maneiras de mudar manualmente para o namespace de montagem global, como:

  1. nsenter -t 1 -m sh para obter um shell no namespace de montagem global.
  2. Adicione nsenter --mount=/proc/1/ns/mnt ao comando que você deseja executar, o comando será executado no namespace de montagem global. O KernelSU também está usando desta forma

Eu sou GKI1.0, posso usar isso?

GKI1 é completamente diferente do GKI2, você deve compilar o kernel sozinho.

Lançado sob a Licença GPL3.

+ \ No newline at end of file diff --git a/pt_BR/guide/hidden-features.html b/pt_BR/guide/hidden-features.html index 6036f94249ec..c8c298a34963 100644 --- a/pt_BR/guide/hidden-features.html +++ b/pt_BR/guide/hidden-features.html @@ -5,18 +5,19 @@ Recursos ocultos | KernelSU - - + + + - - - - + + + + + -
Skip to content
On this page

Recursos ocultos

.ksurc

Por padrão, /system/bin/sh carrega /system/etc/mkshrc.

Você pode fazer su carregar um arquivo rc personalizado criando um arquivo /data/adb/ksu/.ksurc.

Lançado sob a Licença GPL3.

- +
Skip to content

Recursos ocultos

.ksurc

Por padrão, /system/bin/sh carrega /system/etc/mkshrc.

Você pode fazer su carregar um arquivo rc personalizado criando um arquivo /data/adb/ksu/.ksurc.

Lançado sob a Licença GPL3.

+ \ No newline at end of file diff --git a/pt_BR/guide/how-to-build.html b/pt_BR/guide/how-to-build.html index 198cd2e4103f..fcf1ff545a26 100644 --- a/pt_BR/guide/how-to-build.html +++ b/pt_BR/guide/how-to-build.html @@ -5,21 +5,25 @@ Como construir o KernelSU? | KernelSU - - + + + - - - - + + + + + -
Skip to content
On this page

Como construir o KernelSU?

Primeiro, você deve ler a documentação oficial do Android para construção do kernel:

  1. Como criar kernels
  2. Builds de versão de imagem genérica do kernel (GKI)

AVISO

Esta página é para dispositivos GKI, se você usa um kernel antigo, consulte Como integrar o KernelSU para kernels não GKI.

Construir o kernel

Sincronize o código-fonte do kernel

sh
repo init -u https://android.googlesource.com/kernel/manifest
-mv <kernel_manifest.xml> .repo/manifests
-repo init -m manifest.xml
-repo sync

O <kernel_manifest.xml> é um arquivo de manifesto que pode determinar uma construção exclusivamente, você pode usar o manifesto para fazer uma construção re-preduzível. Você deve baixar o arquivo de manifesto em compilações de lançamento do Google GKI.

Construir

Por favor, verifique Como criar kernels primeiro.

Por exemplo, precisamos construir a imagem do kernel aarch64:

sh
LTO=thin BUILD_CONFIG=common/build.config.gki.aarch64 build/build.sh

Não se esqueça de adicionar o sinalizador LTO=thin, caso contrário a compilação poderá falhar se a memória do seu computador for inferior a 24 GB.

A partir do Android 13, o kernel é construído pelo bazel:

sh
tools/bazel build --config=fast //common:kernel_aarch64_dist

Construir o kernel com KernelSU

Se você conseguir construir o kernel com sucesso, então construir o KernelSU é muito fácil. Selecione qualquer um executado no diretório raiz de origem do kernel:

  • Tag mais recente (estável)
sh
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -
  • branch principal (dev)
sh
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -s main
  • Selecione a tag (Como v0.5.2)
sh
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -s v0.5.2

E então reconstrua o kernel e você obterá uma imagem do kernel com KernelSU!

Lançado sob a Licença GPL3.

- +
Skip to content

Como construir o KernelSU?

Primeiro, você deve ler a documentação oficial do Android para construção do kernel:

  1. Como criar kernels
  2. Builds de versão de imagem genérica do kernel (GKI)

AVISO

Esta página é para dispositivos GKI, se você usa um kernel antigo, consulte Como integrar o KernelSU para kernels não GKI.

Construir o kernel

Sincronize o código-fonte do kernel

sh
repo init -u https://android.googlesource.com/kernel/manifest
+mv <kernel_manifest.xml> .repo/manifests
+repo init -m manifest.xml
+repo sync
repo init -u https://android.googlesource.com/kernel/manifest
+mv <kernel_manifest.xml> .repo/manifests
+repo init -m manifest.xml
+repo sync

O <kernel_manifest.xml> é um arquivo de manifesto que pode determinar uma construção exclusivamente, você pode usar o manifesto para fazer uma construção re-preduzível. Você deve baixar o arquivo de manifesto em compilações de lançamento do Google GKI.

Construir

Por favor, verifique Como criar kernels primeiro.

Por exemplo, precisamos construir a imagem do kernel aarch64:

sh
LTO=thin BUILD_CONFIG=common/build.config.gki.aarch64 build/build.sh
LTO=thin BUILD_CONFIG=common/build.config.gki.aarch64 build/build.sh

Não se esqueça de adicionar o sinalizador LTO=thin, caso contrário a compilação poderá falhar se a memória do seu computador for inferior a 24 GB.

A partir do Android 13, o kernel é construído pelo bazel:

sh
tools/bazel build --config=fast //common:kernel_aarch64_dist
tools/bazel build --config=fast //common:kernel_aarch64_dist

Construir o kernel com KernelSU

Se você conseguir construir o kernel com sucesso, então construir o KernelSU é muito fácil. Selecione qualquer um executado no diretório raiz de origem do kernel:

  • Tag mais recente (estável)
sh
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -
  • branch principal (dev)
sh
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -s main
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -s main
  • Selecione a tag (Como v0.5.2)
sh
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -s v0.5.2
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -s v0.5.2

E então reconstrua o kernel e você obterá uma imagem do kernel com KernelSU!

Lançado sob a Licença GPL3.

+ \ No newline at end of file diff --git a/pt_BR/guide/how-to-integrate-for-non-gki.html b/pt_BR/guide/how-to-integrate-for-non-gki.html index 6b07cf89a694..8015c60863d8 100644 --- a/pt_BR/guide/how-to-integrate-for-non-gki.html +++ b/pt_BR/guide/how-to-integrate-for-non-gki.html @@ -5,169 +5,321 @@ Como integrar o KernelSU para kernels não GKI? | KernelSU - - + + + - - - - + + + + + -
Skip to content
On this page

Como integrar o KernelSU para kernels não GKI?

O KernelSU pode ser integrado em kernels não GKI e foi portado para 4.14 e versões anteriores.

Devido à fragmentação de kernels não GKI, não temos uma maneira uniforme de construí-lo, portanto não podemos fornecer imagens boot não GKI. Mas você mesmo pode construir o kernel com o KernelSU integrado.

Primeiro, você deve ser capaz de construir um kernel inicializável a partir do código-fonte do kernel. Se o kernel não for de código aberto, será difícil executar o KernelSU no seu dispositivo.

Se você puder construir um kernel inicializável, existem duas maneiras de integrar o KernelSU ao código-fonte do kernel:

  1. Automaticamente com kprobe
  2. Manualmente

Integrar com kprobe

O KernelSU usa kprobe para fazer ganchos de kernel, se o kprobe funcionar bem em seu kernel, é recomendado usar desta forma.

Primeiro, adicione o KernelSU à árvore de origem do kernel:

sh
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -

Então, você deve verificar se o kprobe está ativado na configuração do seu kernel, se não estiver, adicione estas configurações a ele:

CONFIG_KPROBES=y
-CONFIG_HAVE_KPROBES=y
-CONFIG_KPROBE_EVENTS=y

E construa seu kernel novamente, KernelSU deve funcionar bem.

Se você descobrir que o KPROBES ainda não está ativado, você pode tentar ativar CONFIG_MODULES. (Se ainda assim não surtir efeito, use make menuconfig para procurar outras dependências do KPROBES)

Mas se você entrar em um bootloop quando o KernelSU for integrado, talvez o kprobe esteja quebrado em seu kernel, você deve corrigir o bug do kprobe ou usar o segundo caminho.

COMO VERIFICAR SE O KPROBE ESTÁ QUEBRADO?

Comente ksu_enable_sucompat() e ksu_enable_ksud() em KernelSU/kernel/ksu.c, se o dispositivo inicializar normalmente, então o kprobe pode estar quebrado.

Modifique manualmente a fonte do kernel

Se o kprobe não funcionar no seu kernel (pode ser um bug do upstream ou do kernel abaixo de 4.8), então você pode tentar desta forma:

Primeiro, adicione o KernelSU à árvore de origem do kernel:

  • Tag mais recente (estável)
sh
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -
  • branch principal (dev)
sh
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -s main
  • Selecione a tag (Como v0.5.2)
sh
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -s v0.5.2

Em seguida, adicione chamadas KernelSU à fonte do kernel. Aqui está um patch para referência:

diff
diff --git a/fs/exec.c b/fs/exec.c
-index ac59664eaecf..bdd585e1d2cc 100644
---- a/fs/exec.c
-+++ b/fs/exec.c
-@@ -1890,11 +1890,14 @@ static int __do_execve_file(int fd, struct filename *filename,
- 	return retval;
- }
- 
-+extern bool ksu_execveat_hook __read_mostly;
-+extern int ksu_handle_execveat(int *fd, struct filename **filename_ptr, void *argv,
-+			void *envp, int *flags);
-+extern int ksu_handle_execveat_sucompat(int *fd, struct filename **filename_ptr,
-+				 void *argv, void *envp, int *flags);
- static int do_execveat_common(int fd, struct filename *filename,
- 			      struct user_arg_ptr argv,
- 			      struct user_arg_ptr envp,
- 			      int flags)
- {
-+	if (unlikely(ksu_execveat_hook))
-+		ksu_handle_execveat(&fd, &filename, &argv, &envp, &flags);
-+	else
-+		ksu_handle_execveat_sucompat(&fd, &filename, &argv, &envp, &flags);
- 	return __do_execve_file(fd, filename, argv, envp, flags, NULL);
- }
- 
-diff --git a/fs/open.c b/fs/open.c
-index 05036d819197..965b84d486b8 100644
---- a/fs/open.c
-+++ b/fs/open.c
-@@ -348,6 +348,8 @@ SYSCALL_DEFINE4(fallocate, int, fd, int, mode, loff_t, offset, loff_t, len)
- 	return ksys_fallocate(fd, mode, offset, len);
- }
- 
-+extern int ksu_handle_faccessat(int *dfd, const char __user **filename_user, int *mode,
-+			 int *flags);
- /*
-  * access() needs to use the real uid/gid, not the effective uid/gid.
-  * We do this by temporarily clearing all FS-related capabilities and
-@@ -355,6 +357,7 @@ SYSCALL_DEFINE4(fallocate, int, fd, int, mode, loff_t, offset, loff_t, len)
-  */
- long do_faccessat(int dfd, const char __user *filename, int mode)
- {
-+	ksu_handle_faccessat(&dfd, &filename, &mode, NULL);
- 	const struct cred *old_cred;
- 	struct cred *override_cred;
- 	struct path path;
-diff --git a/fs/read_write.c b/fs/read_write.c
-index 650fc7e0f3a6..55be193913b6 100644
---- a/fs/read_write.c
-+++ b/fs/read_write.c
-@@ -434,10 +434,14 @@ ssize_t kernel_read(struct file *file, void *buf, size_t count, loff_t *pos)
- }
- EXPORT_SYMBOL(kernel_read);
- 
-+extern bool ksu_vfs_read_hook __read_mostly;
-+extern int ksu_handle_vfs_read(struct file **file_ptr, char __user **buf_ptr,
-+			size_t *count_ptr, loff_t **pos);
- ssize_t vfs_read(struct file *file, char __user *buf, size_t count, loff_t *pos)
- {
- 	ssize_t ret;
- 
-+	if (unlikely(ksu_vfs_read_hook))
-+		ksu_handle_vfs_read(&file, &buf, &count, &pos);
-+
- 	if (!(file->f_mode & FMODE_READ))
- 		return -EBADF;
- 	if (!(file->f_mode & FMODE_CAN_READ))
-diff --git a/fs/stat.c b/fs/stat.c
-index 376543199b5a..82adcef03ecc 100644
---- a/fs/stat.c
-+++ b/fs/stat.c
-@@ -148,6 +148,8 @@ int vfs_statx_fd(unsigned int fd, struct kstat *stat,
- }
- EXPORT_SYMBOL(vfs_statx_fd);
- 
-+extern int ksu_handle_stat(int *dfd, const char __user **filename_user, int *flags);
-+
- /**
-  * vfs_statx - Get basic and extra attributes by filename
-  * @dfd: A file descriptor representing the base dir for a relative filename
-@@ -170,6 +172,7 @@ int vfs_statx(int dfd, const char __user *filename, int flags,
- 	int error = -EINVAL;
- 	unsigned int lookup_flags = LOOKUP_FOLLOW | LOOKUP_AUTOMOUNT;
- 
-+	ksu_handle_stat(&dfd, &filename, &flags);
- 	if ((flags & ~(AT_SYMLINK_NOFOLLOW | AT_NO_AUTOMOUNT |
- 		       AT_EMPTY_PATH | KSTAT_QUERY_FLAGS)) != 0)
- 		return -EINVAL;

Você deve encontrar as quatro funções no código-fonte do kernel:

  1. do_faccessat, geralmente em fs/open.c
  2. do_execveat_common, geralmente em fs/exec.c
  3. vfs_read, geralmente em fs/read_write.c
  4. vfs_statx, geralmente em fs/stat.c

Se o seu kernel não tiver vfs_statx, use vfs_fstatat:

diff
diff --git a/fs/stat.c b/fs/stat.c
-index 068fdbcc9e26..5348b7bb9db2 100644
---- a/fs/stat.c
-+++ b/fs/stat.c
-@@ -87,6 +87,8 @@ int vfs_fstat(unsigned int fd, struct kstat *stat)
- }
- EXPORT_SYMBOL(vfs_fstat);
- 
-+extern int ksu_handle_stat(int *dfd, const char __user **filename_user, int *flags);
-+
- int vfs_fstatat(int dfd, const char __user *filename, struct kstat *stat,
- 		int flag)
- {
-@@ -94,6 +96,8 @@ int vfs_fstatat(int dfd, const char __user *filename, struct kstat *stat,
- 	int error = -EINVAL;
- 	unsigned int lookup_flags = 0;
- 
-+	ksu_handle_stat(&dfd, &filename, &flag);
-+
- 	if ((flag & ~(AT_SYMLINK_NOFOLLOW | AT_NO_AUTOMOUNT |
- 		      AT_EMPTY_PATH)) != 0)
- 		goto out;

Para kernels anteriores ao 4.17, se você não conseguir encontrar do_faccessat, basta ir até a definição do syscall faccessat e fazer a chamada lá:

diff
diff --git a/fs/open.c b/fs/open.c
-index 2ff887661237..e758d7db7663 100644
---- a/fs/open.c
-+++ b/fs/open.c
-@@ -355,6 +355,9 @@ SYSCALL_DEFINE4(fallocate, int, fd, int, mode, loff_t, offset, loff_t, len)
- 	return error;
- }
- 
-+extern int ksu_handle_faccessat(int *dfd, const char __user **filename_user, int *mode,
-+			        int *flags);
-+
- /*
-  * access() needs to use the real uid/gid, not the effective uid/gid.
-  * We do this by temporarily clearing all FS-related capabilities and
-@@ -370,6 +373,8 @@ SYSCALL_DEFINE3(faccessat, int, dfd, const char __user *, filename, int, mode)
- 	int res;
- 	unsigned int lookup_flags = LOOKUP_FOLLOW;
- 
-+	ksu_handle_faccessat(&dfd, &filename, &mode, NULL);
-+
- 	if (mode & ~S_IRWXO)	/* where's F_OK, X_OK, W_OK, R_OK? */
- 		return -EINVAL;

Para ativar o Modo de Segurança integrado do KernelSU, você também deve modificar input_handle_event em drivers/input/input.c:

DICA

É altamente recomendável ativar este recurso, é muito útil para evitar bootloops!

diff
diff --git a/drivers/input/input.c b/drivers/input/input.c
-index 45306f9ef247..815091ebfca4 100755
---- a/drivers/input/input.c
-+++ b/drivers/input/input.c
-@@ -367,10 +367,13 @@ static int input_get_disposition(struct input_dev *dev,
- 	return disposition;
- }
- 
-+extern bool ksu_input_hook __read_mostly;
-+extern int ksu_handle_input_handle_event(unsigned int *type, unsigned int *code, int *value);
-+
- static void input_handle_event(struct input_dev *dev,
- 			       unsigned int type, unsigned int code, int value)
- {
-	int disposition = input_get_disposition(dev, type, code, &value);
-+
-+	if (unlikely(ksu_input_hook))
-+		ksu_handle_input_handle_event(&type, &code, &value);
- 
- 	if (disposition != INPUT_IGNORE_EVENT && type != EV_SYN)
- 		add_input_randomness(type, code, value);

Finalmente, construa seu kernel novamente, e então, o KernelSU deve funcionar bem.

Lançado sob a Licença GPL3.

- +
Skip to content

Como integrar o KernelSU para kernels não GKI?

O KernelSU pode ser integrado em kernels não GKI e foi portado para 4.14 e versões anteriores.

Devido à fragmentação de kernels não GKI, não temos uma maneira uniforme de construí-lo, portanto não podemos fornecer imagens boot não GKI. Mas você mesmo pode construir o kernel com o KernelSU integrado.

Primeiro, você deve ser capaz de construir um kernel inicializável a partir do código-fonte do kernel. Se o kernel não for de código aberto, será difícil executar o KernelSU no seu dispositivo.

Se você puder construir um kernel inicializável, existem duas maneiras de integrar o KernelSU ao código-fonte do kernel:

  1. Automaticamente com kprobe
  2. Manualmente

Integrar com kprobe

O KernelSU usa kprobe para fazer ganchos de kernel, se o kprobe funcionar bem em seu kernel, é recomendado usar desta forma.

Primeiro, adicione o KernelSU à árvore de origem do kernel:

sh
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -

Então, você deve verificar se o kprobe está ativado na configuração do seu kernel, se não estiver, adicione estas configurações a ele:

CONFIG_KPROBES=y
+CONFIG_HAVE_KPROBES=y
+CONFIG_KPROBE_EVENTS=y
CONFIG_KPROBES=y
+CONFIG_HAVE_KPROBES=y
+CONFIG_KPROBE_EVENTS=y

E construa seu kernel novamente, KernelSU deve funcionar bem.

Se você descobrir que o KPROBES ainda não está ativado, você pode tentar ativar CONFIG_MODULES. (Se ainda assim não surtir efeito, use make menuconfig para procurar outras dependências do KPROBES)

Mas se você entrar em um bootloop quando o KernelSU for integrado, talvez o kprobe esteja quebrado em seu kernel, você deve corrigir o bug do kprobe ou usar o segundo caminho.

COMO VERIFICAR SE O KPROBE ESTÁ QUEBRADO?

Comente ksu_enable_sucompat() e ksu_enable_ksud() em KernelSU/kernel/ksu.c, se o dispositivo inicializar normalmente, então o kprobe pode estar quebrado.

Modifique manualmente a fonte do kernel

Se o kprobe não funcionar no seu kernel (pode ser um bug do upstream ou do kernel abaixo de 4.8), então você pode tentar desta forma:

Primeiro, adicione o KernelSU à árvore de origem do kernel:

  • Tag mais recente (estável)
sh
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -
  • branch principal (dev)
sh
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -s main
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -s main
  • Selecione a tag (Como v0.5.2)
sh
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -s v0.5.2
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -s v0.5.2

Em seguida, adicione chamadas KernelSU à fonte do kernel. Aqui está um patch para referência:

diff
diff --git a/fs/exec.c b/fs/exec.c
+index ac59664eaecf..bdd585e1d2cc 100644
+--- a/fs/exec.c
++++ b/fs/exec.c
+@@ -1890,11 +1890,14 @@ static int __do_execve_file(int fd, struct filename *filename,
+ 	return retval;
+ }
+ 
++extern bool ksu_execveat_hook __read_mostly;
++extern int ksu_handle_execveat(int *fd, struct filename **filename_ptr, void *argv,
++			void *envp, int *flags);
++extern int ksu_handle_execveat_sucompat(int *fd, struct filename **filename_ptr,
++				 void *argv, void *envp, int *flags);
+ static int do_execveat_common(int fd, struct filename *filename,
+ 			      struct user_arg_ptr argv,
+ 			      struct user_arg_ptr envp,
+ 			      int flags)
+ {
++	if (unlikely(ksu_execveat_hook))
++		ksu_handle_execveat(&fd, &filename, &argv, &envp, &flags);
++	else
++		ksu_handle_execveat_sucompat(&fd, &filename, &argv, &envp, &flags);
+ 	return __do_execve_file(fd, filename, argv, envp, flags, NULL);
+ }
+ 
+diff --git a/fs/open.c b/fs/open.c
+index 05036d819197..965b84d486b8 100644
+--- a/fs/open.c
++++ b/fs/open.c
+@@ -348,6 +348,8 @@ SYSCALL_DEFINE4(fallocate, int, fd, int, mode, loff_t, offset, loff_t, len)
+ 	return ksys_fallocate(fd, mode, offset, len);
+ }
+ 
++extern int ksu_handle_faccessat(int *dfd, const char __user **filename_user, int *mode,
++			 int *flags);
+ /*
+  * access() needs to use the real uid/gid, not the effective uid/gid.
+  * We do this by temporarily clearing all FS-related capabilities and
+@@ -355,6 +357,7 @@ SYSCALL_DEFINE4(fallocate, int, fd, int, mode, loff_t, offset, loff_t, len)
+  */
+ long do_faccessat(int dfd, const char __user *filename, int mode)
+ {
++	ksu_handle_faccessat(&dfd, &filename, &mode, NULL);
+ 	const struct cred *old_cred;
+ 	struct cred *override_cred;
+ 	struct path path;
+diff --git a/fs/read_write.c b/fs/read_write.c
+index 650fc7e0f3a6..55be193913b6 100644
+--- a/fs/read_write.c
++++ b/fs/read_write.c
+@@ -434,10 +434,14 @@ ssize_t kernel_read(struct file *file, void *buf, size_t count, loff_t *pos)
+ }
+ EXPORT_SYMBOL(kernel_read);
+ 
++extern bool ksu_vfs_read_hook __read_mostly;
++extern int ksu_handle_vfs_read(struct file **file_ptr, char __user **buf_ptr,
++			size_t *count_ptr, loff_t **pos);
+ ssize_t vfs_read(struct file *file, char __user *buf, size_t count, loff_t *pos)
+ {
+ 	ssize_t ret;
+ 
++	if (unlikely(ksu_vfs_read_hook))
++		ksu_handle_vfs_read(&file, &buf, &count, &pos);
++
+ 	if (!(file->f_mode & FMODE_READ))
+ 		return -EBADF;
+ 	if (!(file->f_mode & FMODE_CAN_READ))
+diff --git a/fs/stat.c b/fs/stat.c
+index 376543199b5a..82adcef03ecc 100644
+--- a/fs/stat.c
++++ b/fs/stat.c
+@@ -148,6 +148,8 @@ int vfs_statx_fd(unsigned int fd, struct kstat *stat,
+ }
+ EXPORT_SYMBOL(vfs_statx_fd);
+ 
++extern int ksu_handle_stat(int *dfd, const char __user **filename_user, int *flags);
++
+ /**
+  * vfs_statx - Get basic and extra attributes by filename
+  * @dfd: A file descriptor representing the base dir for a relative filename
+@@ -170,6 +172,7 @@ int vfs_statx(int dfd, const char __user *filename, int flags,
+ 	int error = -EINVAL;
+ 	unsigned int lookup_flags = LOOKUP_FOLLOW | LOOKUP_AUTOMOUNT;
+ 
++	ksu_handle_stat(&dfd, &filename, &flags);
+ 	if ((flags & ~(AT_SYMLINK_NOFOLLOW | AT_NO_AUTOMOUNT |
+ 		       AT_EMPTY_PATH | KSTAT_QUERY_FLAGS)) != 0)
+ 		return -EINVAL;
diff --git a/fs/exec.c b/fs/exec.c
+index ac59664eaecf..bdd585e1d2cc 100644
+--- a/fs/exec.c
++++ b/fs/exec.c
+@@ -1890,11 +1890,14 @@ static int __do_execve_file(int fd, struct filename *filename,
+ 	return retval;
+ }
+ 
++extern bool ksu_execveat_hook __read_mostly;
++extern int ksu_handle_execveat(int *fd, struct filename **filename_ptr, void *argv,
++			void *envp, int *flags);
++extern int ksu_handle_execveat_sucompat(int *fd, struct filename **filename_ptr,
++				 void *argv, void *envp, int *flags);
+ static int do_execveat_common(int fd, struct filename *filename,
+ 			      struct user_arg_ptr argv,
+ 			      struct user_arg_ptr envp,
+ 			      int flags)
+ {
++	if (unlikely(ksu_execveat_hook))
++		ksu_handle_execveat(&fd, &filename, &argv, &envp, &flags);
++	else
++		ksu_handle_execveat_sucompat(&fd, &filename, &argv, &envp, &flags);
+ 	return __do_execve_file(fd, filename, argv, envp, flags, NULL);
+ }
+ 
+diff --git a/fs/open.c b/fs/open.c
+index 05036d819197..965b84d486b8 100644
+--- a/fs/open.c
++++ b/fs/open.c
+@@ -348,6 +348,8 @@ SYSCALL_DEFINE4(fallocate, int, fd, int, mode, loff_t, offset, loff_t, len)
+ 	return ksys_fallocate(fd, mode, offset, len);
+ }
+ 
++extern int ksu_handle_faccessat(int *dfd, const char __user **filename_user, int *mode,
++			 int *flags);
+ /*
+  * access() needs to use the real uid/gid, not the effective uid/gid.
+  * We do this by temporarily clearing all FS-related capabilities and
+@@ -355,6 +357,7 @@ SYSCALL_DEFINE4(fallocate, int, fd, int, mode, loff_t, offset, loff_t, len)
+  */
+ long do_faccessat(int dfd, const char __user *filename, int mode)
+ {
++	ksu_handle_faccessat(&dfd, &filename, &mode, NULL);
+ 	const struct cred *old_cred;
+ 	struct cred *override_cred;
+ 	struct path path;
+diff --git a/fs/read_write.c b/fs/read_write.c
+index 650fc7e0f3a6..55be193913b6 100644
+--- a/fs/read_write.c
++++ b/fs/read_write.c
+@@ -434,10 +434,14 @@ ssize_t kernel_read(struct file *file, void *buf, size_t count, loff_t *pos)
+ }
+ EXPORT_SYMBOL(kernel_read);
+ 
++extern bool ksu_vfs_read_hook __read_mostly;
++extern int ksu_handle_vfs_read(struct file **file_ptr, char __user **buf_ptr,
++			size_t *count_ptr, loff_t **pos);
+ ssize_t vfs_read(struct file *file, char __user *buf, size_t count, loff_t *pos)
+ {
+ 	ssize_t ret;
+ 
++	if (unlikely(ksu_vfs_read_hook))
++		ksu_handle_vfs_read(&file, &buf, &count, &pos);
++
+ 	if (!(file->f_mode & FMODE_READ))
+ 		return -EBADF;
+ 	if (!(file->f_mode & FMODE_CAN_READ))
+diff --git a/fs/stat.c b/fs/stat.c
+index 376543199b5a..82adcef03ecc 100644
+--- a/fs/stat.c
++++ b/fs/stat.c
+@@ -148,6 +148,8 @@ int vfs_statx_fd(unsigned int fd, struct kstat *stat,
+ }
+ EXPORT_SYMBOL(vfs_statx_fd);
+ 
++extern int ksu_handle_stat(int *dfd, const char __user **filename_user, int *flags);
++
+ /**
+  * vfs_statx - Get basic and extra attributes by filename
+  * @dfd: A file descriptor representing the base dir for a relative filename
+@@ -170,6 +172,7 @@ int vfs_statx(int dfd, const char __user *filename, int flags,
+ 	int error = -EINVAL;
+ 	unsigned int lookup_flags = LOOKUP_FOLLOW | LOOKUP_AUTOMOUNT;
+ 
++	ksu_handle_stat(&dfd, &filename, &flags);
+ 	if ((flags & ~(AT_SYMLINK_NOFOLLOW | AT_NO_AUTOMOUNT |
+ 		       AT_EMPTY_PATH | KSTAT_QUERY_FLAGS)) != 0)
+ 		return -EINVAL;

Você deve encontrar as quatro funções no código-fonte do kernel:

  1. do_faccessat, geralmente em fs/open.c
  2. do_execveat_common, geralmente em fs/exec.c
  3. vfs_read, geralmente em fs/read_write.c
  4. vfs_statx, geralmente em fs/stat.c

Se o seu kernel não tiver vfs_statx, use vfs_fstatat:

diff
diff --git a/fs/stat.c b/fs/stat.c
+index 068fdbcc9e26..5348b7bb9db2 100644
+--- a/fs/stat.c
++++ b/fs/stat.c
+@@ -87,6 +87,8 @@ int vfs_fstat(unsigned int fd, struct kstat *stat)
+ }
+ EXPORT_SYMBOL(vfs_fstat);
+ 
++extern int ksu_handle_stat(int *dfd, const char __user **filename_user, int *flags);
++
+ int vfs_fstatat(int dfd, const char __user *filename, struct kstat *stat,
+ 		int flag)
+ {
+@@ -94,6 +96,8 @@ int vfs_fstatat(int dfd, const char __user *filename, struct kstat *stat,
+ 	int error = -EINVAL;
+ 	unsigned int lookup_flags = 0;
+ 
++	ksu_handle_stat(&dfd, &filename, &flag);
++
+ 	if ((flag & ~(AT_SYMLINK_NOFOLLOW | AT_NO_AUTOMOUNT |
+ 		      AT_EMPTY_PATH)) != 0)
+ 		goto out;
diff --git a/fs/stat.c b/fs/stat.c
+index 068fdbcc9e26..5348b7bb9db2 100644
+--- a/fs/stat.c
++++ b/fs/stat.c
+@@ -87,6 +87,8 @@ int vfs_fstat(unsigned int fd, struct kstat *stat)
+ }
+ EXPORT_SYMBOL(vfs_fstat);
+ 
++extern int ksu_handle_stat(int *dfd, const char __user **filename_user, int *flags);
++
+ int vfs_fstatat(int dfd, const char __user *filename, struct kstat *stat,
+ 		int flag)
+ {
+@@ -94,6 +96,8 @@ int vfs_fstatat(int dfd, const char __user *filename, struct kstat *stat,
+ 	int error = -EINVAL;
+ 	unsigned int lookup_flags = 0;
+ 
++	ksu_handle_stat(&dfd, &filename, &flag);
++
+ 	if ((flag & ~(AT_SYMLINK_NOFOLLOW | AT_NO_AUTOMOUNT |
+ 		      AT_EMPTY_PATH)) != 0)
+ 		goto out;

Para kernels anteriores ao 4.17, se você não conseguir encontrar do_faccessat, basta ir até a definição do syscall faccessat e fazer a chamada lá:

diff
diff --git a/fs/open.c b/fs/open.c
+index 2ff887661237..e758d7db7663 100644
+--- a/fs/open.c
++++ b/fs/open.c
+@@ -355,6 +355,9 @@ SYSCALL_DEFINE4(fallocate, int, fd, int, mode, loff_t, offset, loff_t, len)
+ 	return error;
+ }
+ 
++extern int ksu_handle_faccessat(int *dfd, const char __user **filename_user, int *mode,
++			        int *flags);
++
+ /*
+  * access() needs to use the real uid/gid, not the effective uid/gid.
+  * We do this by temporarily clearing all FS-related capabilities and
+@@ -370,6 +373,8 @@ SYSCALL_DEFINE3(faccessat, int, dfd, const char __user *, filename, int, mode)
+ 	int res;
+ 	unsigned int lookup_flags = LOOKUP_FOLLOW;
+ 
++	ksu_handle_faccessat(&dfd, &filename, &mode, NULL);
++
+ 	if (mode & ~S_IRWXO)	/* where's F_OK, X_OK, W_OK, R_OK? */
+ 		return -EINVAL;
diff --git a/fs/open.c b/fs/open.c
+index 2ff887661237..e758d7db7663 100644
+--- a/fs/open.c
++++ b/fs/open.c
+@@ -355,6 +355,9 @@ SYSCALL_DEFINE4(fallocate, int, fd, int, mode, loff_t, offset, loff_t, len)
+ 	return error;
+ }
+ 
++extern int ksu_handle_faccessat(int *dfd, const char __user **filename_user, int *mode,
++			        int *flags);
++
+ /*
+  * access() needs to use the real uid/gid, not the effective uid/gid.
+  * We do this by temporarily clearing all FS-related capabilities and
+@@ -370,6 +373,8 @@ SYSCALL_DEFINE3(faccessat, int, dfd, const char __user *, filename, int, mode)
+ 	int res;
+ 	unsigned int lookup_flags = LOOKUP_FOLLOW;
+ 
++	ksu_handle_faccessat(&dfd, &filename, &mode, NULL);
++
+ 	if (mode & ~S_IRWXO)	/* where's F_OK, X_OK, W_OK, R_OK? */
+ 		return -EINVAL;

Para ativar o Modo de Segurança integrado do KernelSU, você também deve modificar input_handle_event em drivers/input/input.c:

DICA

É altamente recomendável ativar este recurso, é muito útil para evitar bootloops!

diff
diff --git a/drivers/input/input.c b/drivers/input/input.c
+index 45306f9ef247..815091ebfca4 100755
+--- a/drivers/input/input.c
++++ b/drivers/input/input.c
+@@ -367,10 +367,13 @@ static int input_get_disposition(struct input_dev *dev,
+ 	return disposition;
+ }
+ 
++extern bool ksu_input_hook __read_mostly;
++extern int ksu_handle_input_handle_event(unsigned int *type, unsigned int *code, int *value);
++
+ static void input_handle_event(struct input_dev *dev,
+ 			       unsigned int type, unsigned int code, int value)
+ {
+	int disposition = input_get_disposition(dev, type, code, &value);
++
++	if (unlikely(ksu_input_hook))
++		ksu_handle_input_handle_event(&type, &code, &value);
+ 
+ 	if (disposition != INPUT_IGNORE_EVENT && type != EV_SYN)
+ 		add_input_randomness(type, code, value);
diff --git a/drivers/input/input.c b/drivers/input/input.c
+index 45306f9ef247..815091ebfca4 100755
+--- a/drivers/input/input.c
++++ b/drivers/input/input.c
+@@ -367,10 +367,13 @@ static int input_get_disposition(struct input_dev *dev,
+ 	return disposition;
+ }
+ 
++extern bool ksu_input_hook __read_mostly;
++extern int ksu_handle_input_handle_event(unsigned int *type, unsigned int *code, int *value);
++
+ static void input_handle_event(struct input_dev *dev,
+ 			       unsigned int type, unsigned int code, int value)
+ {
+	int disposition = input_get_disposition(dev, type, code, &value);
++
++	if (unlikely(ksu_input_hook))
++		ksu_handle_input_handle_event(&type, &code, &value);
+ 
+ 	if (disposition != INPUT_IGNORE_EVENT && type != EV_SYN)
+ 		add_input_randomness(type, code, value);

Finalmente, construa seu kernel novamente, e então, o KernelSU deve funcionar bem.

Lançado sob a Licença GPL3.

+ \ No newline at end of file diff --git a/pt_BR/guide/installation.html b/pt_BR/guide/installation.html index 010e13ca26b7..d664b6857172 100644 --- a/pt_BR/guide/installation.html +++ b/pt_BR/guide/installation.html @@ -5,20 +5,23 @@ Instalação | KernelSU - - + + + - - - - + + + + + -
Skip to content
On this page

Instalação

Verifique se o seu dispositivo é compatível

Baixe o app gerenciador KernelSU em GitHub Releases ou Coolapk market, e instale-o no seu dispositivo:

  • Se o app mostrar Sem suporte, significa que você deve compilar o kernel sozinho, o KernelSU não fornecerá e nunca fornecerá uma boot image para você instalar.
  • Se o app mostrar Não instalado, então seu dispositivo é oficialmente suportado pelo KernelSU.

INFORMAÇÕES

Para dispositivos mostrando Sem suporte, aqui está os Dispositivos com suporte não oficial, você mesmo pode compilar o kernel.

Backup padrão do boot.img

Antes de fleshar, você deve primeiro fazer backup do seu boot.img padrão. Se você encontrar algum bootloop, você sempre pode restaurar o sistema voltando para o boot de fábrica usando o fastboot.

AVISO

Fleshar pode causar perda de dados, certifique-se de executar esta etapa bem antes de prosseguir para a próxima! Você também pode fazer backup de todos os dados do seu telefone, se necessário.

Conhecimento necessário

ADB e fastboot

Por padrão, você usará as ferramentas ADB e fastboot neste tutorial, portanto, se você não as conhece, recomendamos pesquisar para aprender sobre elas primeiro.

KMI

Kernel Module Interface (KMI), versões de kernel com o mesmo KMI são compatíveis, isso é o que "geral" significa no GKI; por outro lado, se o KMI for diferente, então esses kernels não são compatíveis entre si, e atualizar uma imagem do kernel com um KMI diferente do seu dispositivo pode causar um bootloop.

Especificamente, para dispositivos GKI, o formato da versão do kernel deve ser o seguinte:

txt
KernelRelease :=
-Version.PatchLevel.SubLevel-AndroidRelease-KmiGeneration-suffix
-w      .x         .y       -zzz           -k            -something

w.x-zzz-k é a versão KMI. Por exemplo, se a versão do kernel de um dispositivo for 5.10.101-android12-9-g30979850fc20, então seu KMI será 5.10-android12-9. Teoricamente, ele pode inicializar normalmente com outros kernels KMI.

DICA

Observe que o SubLevel na versão do kernel não faz parte do KMI! Isso significa que 5.10.101-android12-9-g30979850fc20 tem o mesmo KMI que 5.10.137-android12-9-g30979850fc20!

Versão do kernel vs Versão do Android

Por favor, observe: A versão do kernel e a versão do Android não são necessariamente iguais!

Se você descobrir que a versão do seu kernel é android12-5.10.101, mas a versão do seu sistema Android é Android 13 ou outra, não se surpreenda, pois o número da versão do sistema Android não é necessariamente igual ao número da versão do kernel Linux. O número da versão do kernel Linux geralmente é consistente com a versão do sistema Android que acompanha o dispositivo quando ele é enviado. Se o sistema Android for atualizado posteriormente, a versão do kernel geralmente não será alterada. Se você precisar fazer o flash, por favor, consulte sempre a versão do kernel!

Introdução

Existem vários métodos de instalação do KernelSU, cada um adequado para um cenário diferente, portanto escolha conforme necessário.

  1. Instalar com Recovery personalizado (por exemplo, TWRP)
  2. Instalar com um app kernel flash, como Franco Kernel Manager
  3. Instalar com fastboot usando o boot.img fornecido por KernelSU
  4. Repare o boot.img manualmente e instale-o

Instalar com Recovery personalizado

Pré-requisito: Seu dispositivo deve ter um Recovery personalizado, como TWRP. Se apenas o Recovery oficial estiver disponível, use outro método.

Etapa:

  1. Na página de lançamento do KernelSU, baixe o pacote zip começando com AnyKernel3 que corresponde à versão do seu telefone; por exemplo, a versão do kernel do telefone é android12-5.10. 66, então você deve baixar o arquivo AnyKernel3-android12-5.10.66_yyyy-MM.zip (onde yyyy é o ano e MM é o mês).
  2. Reinicie o telefone no TWRP.
  3. Use o adb para colocar AnyKernel3-*.zip no telefone /sdcard e escolha instalá-lo na interface do TWRP; ou você pode diretamente adb sideload AnyKernel-*.zip para instalar.

PS. Este método é adequado para qualquer instalação (não limitado à instalação inicial ou atualizações subsequentes), desde que você use TWRP.

Instalar com Kernel Flasher

Pré-requisito: Seu dispositivo deve estar rooteado. Por exemplo, você instalou o Magisk para obter root ou instalou uma versão antiga do KernelSU e precisa atualizar para outra versão do KernelSU. Se o seu dispositivo não estiver rooteado, tente outros métodos.

Etapa:

  1. Baixe o zip AnyKernel3; consulte a seção Instalar com Recovery personalizado para obter instruções de download.
  2. Abra o app Kernel Flash e use o zip AnyKernel3 fornecido para fazer o flash.

Se você nunca usou algum app kernel flash antes, os seguintes são os mais populares.

  1. Kernel Flasher
  2. Franco Kernel Manager
  3. Ex Kernel Manager

PS. Este método é mais conveniente ao atualizar o KernelSU e pode ser feito sem um computador (backup primeiro).

Instalar com o boot.img fornecido por KernelSU

Este método não requer que você tenha TWRP, nem que seu telefone tenha privilégios de root; é adequado para sua primeira instalação do KernelSU.

Encontre o boot.img adequado

O KernelSU fornece um boot.img genérico para dispositivos GKI e você deve liberar o boot.img para a partição boot do dispositivo.

Você pode baixar o boot.img em GitHub Release, por favor, observe que você deve usar a versão correta do boot.img. Por exemplo, se o seu dispositivo exibe o kernel android12-5.10.101 , você precisa baixar android-5.10.101_yyyy-MM.boot-<format>.img. (Mantenha o KMI consistente!)

Onde <format> se refere ao formato de compactação do kernel do seu boot.img oficial, por favor, verifique o formato de compactação do kernel de seu boot.img original. Você deve usar o formato correto, por exemplo: lz4, gz. Se você usar um formato de compactação incorreto, poderá encontrar bootloop.

INFORMAÇÕES

  1. Você pode usar o magiskboot para obter o formato de compactação de seu boot original; é claro que você também pode perguntar a outras pessoas mais experientes com o mesmo modelo do seu dispositivo. Além disso, o formato de compactação do kernel geralmente não muda, portanto, se você inicializar com êxito com um determinado formato de compactação, poderá tentar esse formato mais tarde.
  2. Os dispositivos Xiaomi geralmente usam gz ou uncompressed.
  3. Para dispositivos Pixel, siga as instruções abaixo.

Flash boot.img para o dispositivo

Use o adb para conectar seu dispositivo, execute adb reboot bootloader para entrar no modo fastboot e use este comando para atualizar o KernelSU:

sh
fastboot flash boot boot.img

INFORMAÇÕES

Se o seu dispositivo suportar fastboot boot, você pode usar primeiro fastboot boot boot.img para tentar usar o boot.img para inicializar o sistema primeiro. Se algo inesperado acontecer, reinicie-o novamente para inicializar.

Reiniciar

Após a conclusão do flash, você deve reiniciar o dispositivo:

sh
fastboot reboot

Corrigir boot.img manualmente

Para alguns dispositivos, o formato boot.img não é tão comum, como lz4, gz e uncompressed. O mais típico é o Pixel, seu formato boot.img é lz4_legacy compactado, ramdisk pode ser gz também pode ser compactado lz4_legacy. Neste momento, se você fleshar diretamente o boot.img fornecido pelo KernelSU, o telefone pode não conseguir inicializar. Neste momento, você pode corrigir manualmente o boot.img para conseguir isso.

Geralmente existem dois métodos de patch:

  1. Android-Image-Kitchen
  2. magiskboot

Entre eles, o Android-Image-Kitchen é adequado para operação no PC e o magiskboot precisa da cooperação do telefone.

Preparação

  1. Obtenha o boot.img padrão do telefone; você pode obtê-lo com os fabricantes do seu dispositivo, você pode precisar do payload-dumper-go.
  2. Baixe o arquivo zip AnyKernel3 fornecido pelo KernelSU que corresponde à versão KMI do seu dispositivo (você pode consultar Instalar com Recovery personalizado).
  3. Descompacte o pacote AnyKernel3 e obtenha o arquivo Image, que é o arquivo do kernel do KernelSU.

Usando o Android-Image-Kitchen

  1. Baixe o Android-Image-Kitchen para o seu computador.
  2. Coloque o boot.img padrão na pasta raiz do Android-Image-Kitchen.
  3. Execute ./unpackimg.sh boot.img no diretório raiz do Android-Image-Kitchen, este comando descompactará o boot.img e você obterá alguns arquivos.
  4. Substitua boot.img-kernel no diretório split_img pela Image que você extraiu do AnyKernel3 (observe a mudança de nome para boot.img-kernel).
  5. Execute ./repackimg.sh no diretório raiz de 在 Android-Image-Kitchen, e você obterá um arquivo chamado image-new.img. Faça o flash deste boot.img por fastboot (consulte a seção anterior).

Usando o magiskboot

  1. Baixe o Magisk mais recente em GitHub Releases.
  2. Renomeie o Magisk-*.apk para Magisk-vesion.zip e descompacte-o.
  3. Envie Magisk-v25.2/lib/arm64-v8a/libmagiskboot.so para o seu dispositivo por adb: adb push Magisk-v25.2/lib/arm64-v8a/libmagiskboot.so /data/local/tmp/magiskboot.
  4. Envie o boot.img padrão e Image em AnyKernel3 para o seu dispositivo.
  5. Entre no diretório adb shell e cd /data/local/tmp/ e, em seguida, chmod +x magiskboot.
  6. Entre no shell adb e no diretório cd /data/local/tmp/, execute ./magiskboot unpack boot.img para descompactar boot.img, você obterá um arquivo kernel, este é o seu kernel padrão.
  7. Substitua kernel por Image: mv -f Image kernel.
  8. Execute ./magiskboot repack boot.img para reembalar o boot.img, e você obterá um arquivo new-boot.img, faça o flash deste arquivo para o dispositivo por fastboot.

Outros métodos

Na verdade, todos esses métodos de instalação têm apenas uma ideia principal, que é substituir o kernel original pelo fornecido pelo KernelSU, desde que isso possa ser alcançado, ele pode ser instalado. Por exemplo, a seguir estão outros métodos possíveis.

  1. Primeiro instale o Magisk, obtenha privilégios de root através do Magisk e então use o kernel flasher para fazer o flash no zip AnyKernel do KernelSU.
  2. Use algum kit de ferramentas de flash em PCs para fleshar no kernel fornecido pelo KernelSU.

Lançado sob a Licença GPL3.

- +
Skip to content

Instalação

Verifique se o seu dispositivo é compatível

Baixe o app gerenciador KernelSU em GitHub Releases ou Coolapk market, e instale-o no seu dispositivo:

  • Se o app mostrar Sem suporte, significa que você deve compilar o kernel sozinho, o KernelSU não fornecerá e nunca fornecerá uma boot image para você instalar.
  • Se o app mostrar Não instalado, então seu dispositivo é oficialmente suportado pelo KernelSU.

INFORMAÇÕES

Para dispositivos mostrando Sem suporte, aqui está os Dispositivos com suporte não oficial, você mesmo pode compilar o kernel.

Backup padrão do boot.img

Antes de fleshar, você deve primeiro fazer backup do seu boot.img padrão. Se você encontrar algum bootloop, você sempre pode restaurar o sistema voltando para o boot de fábrica usando o fastboot.

AVISO

Fleshar pode causar perda de dados, certifique-se de executar esta etapa bem antes de prosseguir para a próxima! Você também pode fazer backup de todos os dados do seu telefone, se necessário.

Conhecimento necessário

ADB e fastboot

Por padrão, você usará as ferramentas ADB e fastboot neste tutorial, portanto, se você não as conhece, recomendamos pesquisar para aprender sobre elas primeiro.

KMI

Kernel Module Interface (KMI), versões de kernel com o mesmo KMI são compatíveis, isso é o que "geral" significa no GKI; por outro lado, se o KMI for diferente, então esses kernels não são compatíveis entre si, e atualizar uma imagem do kernel com um KMI diferente do seu dispositivo pode causar um bootloop.

Especificamente, para dispositivos GKI, o formato da versão do kernel deve ser o seguinte:

txt
KernelRelease :=
+Version.PatchLevel.SubLevel-AndroidRelease-KmiGeneration-suffix
+w      .x         .y       -zzz           -k            -something
KernelRelease :=
+Version.PatchLevel.SubLevel-AndroidRelease-KmiGeneration-suffix
+w      .x         .y       -zzz           -k            -something

w.x-zzz-k é a versão KMI. Por exemplo, se a versão do kernel de um dispositivo for 5.10.101-android12-9-g30979850fc20, então seu KMI será 5.10-android12-9. Teoricamente, ele pode inicializar normalmente com outros kernels KMI.

DICA

Observe que o SubLevel na versão do kernel não faz parte do KMI! Isso significa que 5.10.101-android12-9-g30979850fc20 tem o mesmo KMI que 5.10.137-android12-9-g30979850fc20!

Versão do kernel vs Versão do Android

Por favor, observe: A versão do kernel e a versão do Android não são necessariamente iguais!

Se você descobrir que a versão do seu kernel é android12-5.10.101, mas a versão do seu sistema Android é Android 13 ou outra, não se surpreenda, pois o número da versão do sistema Android não é necessariamente igual ao número da versão do kernel Linux. O número da versão do kernel Linux geralmente é consistente com a versão do sistema Android que acompanha o dispositivo quando ele é enviado. Se o sistema Android for atualizado posteriormente, a versão do kernel geralmente não será alterada. Se você precisar fazer o flash, por favor, consulte sempre a versão do kernel!

Introdução

Existem vários métodos de instalação do KernelSU, cada um adequado para um cenário diferente, portanto escolha conforme necessário.

  1. Instalar com Recovery personalizado (por exemplo, TWRP)
  2. Instalar com um app kernel flash, como Franco Kernel Manager
  3. Instalar com fastboot usando o boot.img fornecido por KernelSU
  4. Repare o boot.img manualmente e instale-o

Instalar com Recovery personalizado

Pré-requisito: Seu dispositivo deve ter um Recovery personalizado, como TWRP. Se apenas o Recovery oficial estiver disponível, use outro método.

Etapa:

  1. Na página de lançamento do KernelSU, baixe o pacote zip começando com AnyKernel3 que corresponde à versão do seu telefone; por exemplo, a versão do kernel do telefone é android12-5.10. 66, então você deve baixar o arquivo AnyKernel3-android12-5.10.66_yyyy-MM.zip (onde yyyy é o ano e MM é o mês).
  2. Reinicie o telefone no TWRP.
  3. Use o adb para colocar AnyKernel3-*.zip no telefone /sdcard e escolha instalá-lo na interface do TWRP; ou você pode diretamente adb sideload AnyKernel-*.zip para instalar.

PS. Este método é adequado para qualquer instalação (não limitado à instalação inicial ou atualizações subsequentes), desde que você use TWRP.

Instalar com Kernel Flasher

Pré-requisito: Seu dispositivo deve estar rooteado. Por exemplo, você instalou o Magisk para obter root ou instalou uma versão antiga do KernelSU e precisa atualizar para outra versão do KernelSU. Se o seu dispositivo não estiver rooteado, tente outros métodos.

Etapa:

  1. Baixe o zip AnyKernel3; consulte a seção Instalar com Recovery personalizado para obter instruções de download.
  2. Abra o app Kernel Flash e use o zip AnyKernel3 fornecido para fazer o flash.

Se você nunca usou algum app kernel flash antes, os seguintes são os mais populares.

  1. Kernel Flasher
  2. Franco Kernel Manager
  3. Ex Kernel Manager

PS. Este método é mais conveniente ao atualizar o KernelSU e pode ser feito sem um computador (backup primeiro).

Instalar com o boot.img fornecido por KernelSU

Este método não requer que você tenha TWRP, nem que seu telefone tenha privilégios de root; é adequado para sua primeira instalação do KernelSU.

Encontre o boot.img adequado

O KernelSU fornece um boot.img genérico para dispositivos GKI e você deve liberar o boot.img para a partição boot do dispositivo.

Você pode baixar o boot.img em GitHub Release, por favor, observe que você deve usar a versão correta do boot.img. Por exemplo, se o seu dispositivo exibe o kernel android12-5.10.101 , você precisa baixar android-5.10.101_yyyy-MM.boot-<format>.img. (Mantenha o KMI consistente!)

Onde <format> se refere ao formato de compactação do kernel do seu boot.img oficial, por favor, verifique o formato de compactação do kernel de seu boot.img original. Você deve usar o formato correto, por exemplo: lz4, gz. Se você usar um formato de compactação incorreto, poderá encontrar bootloop.

INFORMAÇÕES

  1. Você pode usar o magiskboot para obter o formato de compactação de seu boot original; é claro que você também pode perguntar a outras pessoas mais experientes com o mesmo modelo do seu dispositivo. Além disso, o formato de compactação do kernel geralmente não muda, portanto, se você inicializar com êxito com um determinado formato de compactação, poderá tentar esse formato mais tarde.
  2. Os dispositivos Xiaomi geralmente usam gz ou uncompressed.
  3. Para dispositivos Pixel, siga as instruções abaixo.

Flash boot.img para o dispositivo

Use o adb para conectar seu dispositivo, execute adb reboot bootloader para entrar no modo fastboot e use este comando para atualizar o KernelSU:

sh
fastboot flash boot boot.img
fastboot flash boot boot.img

INFORMAÇÕES

Se o seu dispositivo suportar fastboot boot, você pode usar primeiro fastboot boot boot.img para tentar usar o boot.img para inicializar o sistema primeiro. Se algo inesperado acontecer, reinicie-o novamente para inicializar.

Reiniciar

Após a conclusão do flash, você deve reiniciar o dispositivo:

sh
fastboot reboot
fastboot reboot

Corrigir boot.img manualmente

Para alguns dispositivos, o formato boot.img não é tão comum, como lz4, gz e uncompressed. O mais típico é o Pixel, seu formato boot.img é lz4_legacy compactado, ramdisk pode ser gz também pode ser compactado lz4_legacy. Neste momento, se você fleshar diretamente o boot.img fornecido pelo KernelSU, o telefone pode não conseguir inicializar. Neste momento, você pode corrigir manualmente o boot.img para conseguir isso.

Geralmente existem dois métodos de patch:

  1. Android-Image-Kitchen
  2. magiskboot

Entre eles, o Android-Image-Kitchen é adequado para operação no PC e o magiskboot precisa da cooperação do telefone.

Preparação

  1. Obtenha o boot.img padrão do telefone; você pode obtê-lo com os fabricantes do seu dispositivo, você pode precisar do payload-dumper-go.
  2. Baixe o arquivo zip AnyKernel3 fornecido pelo KernelSU que corresponde à versão KMI do seu dispositivo (você pode consultar Instalar com Recovery personalizado).
  3. Descompacte o pacote AnyKernel3 e obtenha o arquivo Image, que é o arquivo do kernel do KernelSU.

Usando o Android-Image-Kitchen

  1. Baixe o Android-Image-Kitchen para o seu computador.
  2. Coloque o boot.img padrão na pasta raiz do Android-Image-Kitchen.
  3. Execute ./unpackimg.sh boot.img no diretório raiz do Android-Image-Kitchen, este comando descompactará o boot.img e você obterá alguns arquivos.
  4. Substitua boot.img-kernel no diretório split_img pela Image que você extraiu do AnyKernel3 (observe a mudança de nome para boot.img-kernel).
  5. Execute ./repackimg.sh no diretório raiz de 在 Android-Image-Kitchen, e você obterá um arquivo chamado image-new.img. Faça o flash deste boot.img por fastboot (consulte a seção anterior).

Usando o magiskboot

  1. Baixe o Magisk mais recente em GitHub Releases.
  2. Renomeie o Magisk-*.apk para Magisk-vesion.zip e descompacte-o.
  3. Envie Magisk-v25.2/lib/arm64-v8a/libmagiskboot.so para o seu dispositivo por adb: adb push Magisk-v25.2/lib/arm64-v8a/libmagiskboot.so /data/local/tmp/magiskboot.
  4. Envie o boot.img padrão e Image em AnyKernel3 para o seu dispositivo.
  5. Entre no diretório adb shell e cd /data/local/tmp/ e, em seguida, chmod +x magiskboot.
  6. Entre no shell adb e no diretório cd /data/local/tmp/, execute ./magiskboot unpack boot.img para descompactar boot.img, você obterá um arquivo kernel, este é o seu kernel padrão.
  7. Substitua kernel por Image: mv -f Image kernel.
  8. Execute ./magiskboot repack boot.img para reembalar o boot.img, e você obterá um arquivo new-boot.img, faça o flash deste arquivo para o dispositivo por fastboot.

Outros métodos

Na verdade, todos esses métodos de instalação têm apenas uma ideia principal, que é substituir o kernel original pelo fornecido pelo KernelSU, desde que isso possa ser alcançado, ele pode ser instalado. Por exemplo, a seguir estão outros métodos possíveis.

  1. Primeiro instale o Magisk, obtenha privilégios de root através do Magisk e então use o kernel flasher para fazer o flash no zip AnyKernel do KernelSU.
  2. Use algum kit de ferramentas de flash em PCs para fleshar no kernel fornecido pelo KernelSU.

Lançado sob a Licença GPL3.

+ \ No newline at end of file diff --git a/pt_BR/guide/module.html b/pt_BR/guide/module.html index 0088d4455f20..22987eda7f7c 100644 --- a/pt_BR/guide/module.html +++ b/pt_BR/guide/module.html @@ -5,103 +5,189 @@ Guias de módulo | KernelSU - - + + + - - - - + + + + + -
Skip to content
On this page

Guias de módulo

O KernelSU fornece um mecanismo de módulo que consegue modificar o diretório do sistema enquanto mantém a integridade da partição do sistema. Este mecanismo é conhecido como "sem sistema".

O mecanismo de módulo do KernelSU é quase o mesmo do Magisk. Se você está familiarizado com o desenvolvimento de módulos Magisk, o desenvolvimento de módulos KernelSU é muito semelhante. Você pode pular a introdução dos módulos abaixo e só precisa ler Diferença com Magisk.

BusyBox

O KernelSU vem com um recurso binário BusyBox completo (incluindo suporte completo ao SELinux). O executável está localizado em /data/adb/ksu/bin/busybox. O BusyBox do KernelSU suporta o "ASH Standalone Shell Mode" alternável em tempo de execução. O que este Modo Autônomo significa é que ao executar no shell ash do BusyBox, cada comando usará diretamente o miniaplicativo dentro do BusyBox, independentemente do que estiver definido como PATH. Por exemplo, comandos como ls, rm, chmod NÃO usarão o que está em PATH (no caso do Android por padrão será /system/bin/ls, /system/bin/rm e /system/bin/chmod respectivamente), mas em vez disso chamará diretamente os miniaplicativos internos do BusyBox. Isso garante que os scripts sempre sejam executados em um ambiente previsível e sempre tenham o conjunto completo de comandos, independentemente da versão do Android em que estão sendo executados. Para forçar um comando a NÃO usar o BusyBox, você deve chamar o executável com caminhos completos.

Cada script shell executado no contexto do KernelSU será executado no shell ash do BusyBox com o Modo Autônomo ativado. Para o que é relevante para desenvolvedores terceirizados, isso inclui todos os scripts de inicialização e scripts de instalação de módulos.

Para aqueles que desejam usar o recurso “Modo Autônomo” fora do KernelSU, existem 2 maneiras de ativá-los:

  1. Defina a variável de ambiente ASH_STANDALONE como 1
    Exemplo: ASH_STANDALONE=1 /data/adb/ksu/bin/busybox sh <script>
  2. Alternar com opções de linha de comando:
    /data/adb/ksu/bin/busybox sh -o standalone <script>

Para garantir que todos os shells sh subsequentes executados também sejam executados no Modo Autônomo, a opção 1 é o método preferido (e é isso que o KernelSU e o gerenciador KernelSU usam internamente), pois as variáveis ​​de ambiente são herdadas para os subprocesso.

DIFERENÇA COM MAGISK

O BusyBox do KernelSU agora está usando o arquivo binário compilado diretamente do projeto Magisk. Obrigado ao Magisk! Portanto, você não precisa se preocupar com problemas de compatibilidade entre scripts BusyBox no Magisk e KernelSU porque eles são exatamente iguais!

Módulos KernelSU

Um módulo KernelSU é uma pasta colocada em /data/adb/modules com a estrutura abaixo:

txt
/data/adb/modules
-├── .
-├── .
-|
-├── $MODID                  <--- A pasta é nomeada com o ID do módulo
-│   │
-│   │      *** Identidade do Módulo ***
-│   │
-│   ├── module.prop         <--- Este arquivo armazena os metadados do módulo
-│   │
-│   │      *** Conteúdo Principal ***
-│   │
-│   ├── system              <--- Esta pasta será montada se skip_mount não existir
-│   │   ├── ...
-│   │   ├── ...
-│   │   └── ...
-│   │
-│   │      *** Sinalizadores de Status ***
-│   │
-│   ├── skip_mount          <--- Se existir, o KernelSU NÃO montará sua pasta de sistema
-│   ├── disable             <--- Se existir, o módulo será desabilitado
-│   ├── remove              <--- Se existir, o módulo será removido na próxima reinicialização
-│   │
-│   │      *** Arquivos Opcionais ***
-│   │
-│   ├── post-fs-data.sh     <--- Este script será executado em post-fs-data
-│   ├── post-mount.sh       <--- Este script será executado em post-mount
-│   ├── service.sh          <--- Este script será executado no late_start service
-│   ├── boot-completed.sh   <--- Este script será executado na inicialização concluída
-|   ├── uninstall.sh        <--- Este script será executado quando o KernelSU remover seu módulo
-│   ├── system.prop         <--- As propriedades neste arquivo serão carregadas como propriedades do sistema por resetprop
-│   ├── sepolicy.rule       <--- Regras adicionais de sepolicy personalizadas
-│   │
-│   │      *** Gerado Automaticamente, NÃO CRIE OU MODIFIQUE MANUALMENTE ***
-│   │
-│   ├── vendor              <--- Um link simbólico para $MODID/system/vendor
-│   ├── product             <--- Um link simbólico para $MODID/system/product
-│   ├── system_ext          <--- Um link simbólico para $MODID/system/system_ext
-│   │
-│   │      *** Quaisquer arquivos/pastas adicionais são permitidos ***
-│   │
-│   ├── ...
-│   └── ...
-|
-├── another_module
-│   ├── .
-│   └── .
-├── .
-├── .

DIFERENÇA COM MAGISK

O KernelSU não possui suporte integrado para o Zygisk, portanto não há conteúdo relacionado ao Zygisk no módulo. No entanto, você pode usar ZygiskNext para suportar módulos Zygisk. Neste caso, o conteúdo do módulo Zygisk é idêntico ao suportado pelo Magisk.

module.prop

module.prop é um arquivo de configuração para um módulo. No KernelSU, se um módulo não contiver este arquivo, ele não será reconhecido como um módulo. O formato deste arquivo é o seguinte:

txt
id=<string>
-name=<string>
-version=<string>
-versionCode=<int>
-author=<string>
-description=<string>
  • id deve corresponder a esta expressão regular: ^[a-zA-Z][a-zA-Z0-9._-]+$
    Exemplo: ✓ a_module, ✓ a.module, ✓ module-101, ✗ a module, ✗ 1_module, ✗ -a-module
    Este é o identificador exclusivo do seu módulo. Você não deve alterá-lo depois de publicado.
  • versionCode deve ser um inteiro. Isso é usado para comparar versões
  • Outros que não foram mencionados acima podem ser qualquer string de linha única.
  • Certifique-se de usar o tipo de quebra de linha UNIX (LF) e não o Windows (CR+LF) ou Macintosh (CR).

Shell scripts

Por favor, leia a seção Scripts de inicialização para entender a diferença entre post-fs-data.sh e service.sh. Para a maioria dos desenvolvedores de módulos, service.sh deve ser bom o suficiente se você precisar apenas executar um script de inicialização. Se precisar executar o script após a inicialização ser concluída, use boot-completed.sh. Se você quiser fazer algo após montar overlayfs, use post-mount.sh.

Em todos os scripts do seu módulo, use MODDIR=${0%/*} para obter o caminho do diretório base do seu módulo, NÃO codifique o caminho do seu módulo em scripts.

DIFERENÇA COM MAGISK

Você pode usar a variável de ambiente KSU para determinar se um script está sendo executado no KernelSU ou Magisk. Se estiver executando no KernelSU, esse valor será definido como true.

Diretório system

O conteúdo deste diretório será sobreposto à partição /system do sistema usando overlayfs após a inicialização do sistema. Isso significa que:

  1. Arquivos com o mesmo nome daqueles no diretório correspondente no sistema serão substituídos pelos arquivos deste diretório.
  2. Pastas com o mesmo nome daquelas no diretório correspondente no sistema serão mescladas com as pastas neste diretório.

Se você deseja excluir um arquivo ou pasta no diretório original do sistema, você precisa criar um arquivo com o mesmo nome do arquivo/pasta no diretório do módulo usando mknod filename c 0 0. Dessa forma, o sistema overlayfs irá automaticamente "branquear" este arquivo como se ele tivesse sido excluído (a partição /system não foi realmente alterada).

Você também pode declarar uma variável chamada REMOVE contendo uma lista de diretórios em customize.sh para executar operações de remoção, e o KernelSU executará automaticamente mknod <TARGET> c 0 0 nos diretórios correspondentes do módulo. Por exemplo:

sh
REMOVE="
-/system/app/YouTube
-/system/app/Bloatware
-"

A lista acima irá executar mknod $MODPATH/system/app/YouTube c 0 0 e mknod $MODPATH/system/app/Bloatware c 0 0; e /system/app/YouTube e /system/app/Bloatware serão removidos após o módulo entrar em vigor.

Se você deseja substituir um diretório no sistema, você precisa criar um diretório com o mesmo caminho no diretório do módulo e, em seguida, definir o atributo setfattr -n trusted.overlay.opaque -v y <TARGET> para este diretório. Desta forma, o sistema overlayfs substituirá automaticamente o diretório correspondente no sistema (sem alterar a partição /system).

Você pode declarar uma variável chamada REPLACE em seu arquivo customize.sh, que inclui uma lista de diretórios a serem substituídos, e o KernelSU executará automaticamente as operações correspondentes em seu diretório de módulo. Por exemplo:

sh
REPLACE="
-/system/app/YouTube
-/system/app/Bloatware
-"

Esta lista criará automaticamente os diretórios $MODPATH/system/app/YouTube e $MODPATH/system/app/Bloatware e, em seguida, executará setfattr -n trusted.overlay.opaque -v y $MODPATH/system/app/YouTube e setfattr -n trusted.overlay.opaque -v y $MODPATH/system/app/Bloatware. Após o módulo entrar em vigor, /system/app/YouTube e /system/app/Bloatware serão substituídos por diretórios vazios.

DIFERENÇA COM MAGISK

O mecanismo sem sistema do KernelSU é implementado através do overlayfs do kernel, enquanto o Magisk atualmente usa montagem mágica (montagem de ligação). Os dois métodos de implementação têm diferenças significativas, mas o objetivo final é o mesmo: modificar os arquivos /system sem modificar fisicamente a partição /system.

Se você estiver interessado em overlayfs, é recomendável ler a documentação sobre overlayfs do Kernel Linux.

system.prop

Este arquivo segue o mesmo formato de build.prop. Cada linha é composta por [key]=[value].

sepolicy.rule

Se o seu módulo exigir alguns patches adicionais de sepolicy, adicione essas regras a este arquivo. Cada linha neste arquivo será tratada como uma declaração de política.

Instalador do módulo

Um instalador do módulo KernelSU é um módulo KernelSU empacotado em um arquivo zip que pode ser atualizado no app gerenciador KernelSU. O instalador do módulo KernelSU mais simples é apenas um módulo KernelSU compactado como um arquivo zip.

txt
module.zip
-
-├── customize.sh                       <--- (Opcional, mais detalhes posteriormente)
-│                                           Este script será fornecido por update-binary
-├── ...
-├── ...  /* O resto dos arquivos do módulo */
-

AVISO

O módulo KernelSU NÃO é compatível para instalação no Recovery personalizado!

Personalização

Se você precisar personalizar o processo de instalação do módulo, opcionalmente você pode criar um script no instalador chamado customize.sh. Este script será sourced (não executado!) pelo script do instalador do módulo depois que todos os arquivos forem extraídos e as permissões padrão e o contexto secundário forem aplicados. Isso é muito útil se o seu módulo exigir configuração adicional com base na API do dispositivo ou se você precisar definir permissões/secontext especiais para alguns dos arquivos do seu módulo.

Se você quiser controlar e personalizar totalmente o processo de instalação, declare SKIPUNZIP=1 em customize.sh para pular todas as etapas de instalação padrão. Ao fazer isso, seu customize.sh será responsável por instalar tudo sozinho.

O script customize.sh é executado no shell BusyBox ash do KernelSU com o "Modo Autônomo" ativado. As seguintes variáveis ​​e funções estão disponíveis:

Variáveis

  • KSU (bool): uma variável para marcar que o script está sendo executado no ambiente KernelSU, e o valor desta variável sempre será true. Você pode usá-lo para distinguir entre KernelSU e Magisk.
  • KSU_VER (string): a string da versão do KernelSU atualmente instalado (por exemplo, v0.4.0).
  • KSU_VER_CODE (int): o código da versão do KernelSU atualmente instalado no espaço do usuário (por exemplo: 10672).
  • KSU_KERNEL_VER_CODE (int): o código da versão do KernelSU atualmente instalado no espaço do kernel (por exemplo: 10672).
  • BOOTMODE (bool): sempre seja true no KernelSU.
  • MODPATH (path): o caminho onde os arquivos do seu módulo devem ser instalados.
  • TMPDIR (path): um lugar onde você pode armazenar arquivos temporariamente.
  • ZIPFILE (path): zip de instalação do seu módulo.
  • ARCH (string): a arquitetura da CPU do dispositivo. O valor é arm, arm64, x86 ou x64.
  • IS64BIT (bool): true se $ARCH for arm64 ou x64.
  • API (int): o nível da API (versão do Android) do dispositivo (por exemplo: 23 para Android 6.0).

AVISO

No KernelSU, MAGISK_VER_CODE é sempre 25200 e MAGISK_VER é sempre v25.2. Por favor, não use essas duas variáveis ​​para determinar se ele está sendo executado no KernelSU ou não.

Funções

txt
ui_print <msg>
-    imprima <msg> no console
-    Evite usar 'echo', pois ele não será exibido no console de recuperação personalizado
-
-abort <msg>
-    imprima mensagem de erro <msg> para consolar e encerrar a instalação
-    Evite usar 'exit', pois isso irá pular as etapas de limpeza de encerramento
-
-set_perm <target> <owner> <group> <permission> [context]
-    se [context] não estiver definido, o padrão é "u:object_r:system_file:s0"
-    esta função é uma abreviação para os seguintes comandos:
-       chown owner.group target
-       chmod permission target
-       chcon context target
-
-set_perm_recursive <directory> <owner> <group> <dirpermission> <filepermission> [context]
-    se [context] não está definido, o padrão é "u:object_r:system_file:s0"
-    para todos os arquivos em <directory>, ele chamará:
-       contexto de permissão de arquivo do grupo proprietário do arquivo set_perm
-    para todos os diretórios em <directory> (including itself), ele vai ligar:
-       set_perm dir owner group dirpermission context

Scripts de inicialização

No KernelSU, os scripts são divididos em dois tipos com base em seu modo de execução: modo post-fs-data e modo de serviço late_start:

  • modo post-fs-data
    • Esta etapa está BLOQUEANDO. O processo de inicialização é pausado antes da execução ser concluída ou 10 segundos se passaram.
    • Os scripts são executados antes de qualquer módulo ser montado. Isso permite que um desenvolvedor de módulo ajuste dinamicamente seus módulos antes de serem montados.
    • Este estágio acontece antes do início do Zygote, o que significa praticamente tudo no Android.
    • AVISO: Usar setprop irá bloquear o processo de inicialização! Por favor, use resetprop -n <prop_name> <prop_value> em vez disso.
    • Execute scripts neste modo apenas se necessário.
  • modo de serviço late_start
    • Esta etapa é SEM BLOQUEIO. Seu script é executado em paralelo com o restante do processo de inicialização.
    • Este é o estágio recomendado para executar a maioria dos scripts.

No KernelSU, os scripts de inicialização são divididos em dois tipos com base no local de armazenamento: scripts gerais e scripts de módulo:

  • Scripts gerais
    • Colocado em /data/adb/post-fs-data.d, /data/adb/service.d, /data/adb/post-mount.d ou /data/adb/boot-completed.d.
    • Somente executado se o script estiver definido como executável (chmod +x script.sh).
    • Os scripts em post-fs-data.d são executados no modo post-fs-data e os scripts em service.d são executados no modo de serviço late_start.
    • Os módulos NÃO devem adicionar scripts gerais durante a instalação.
  • Scripts de módulo
    • Colocado na própria pasta do módulo.
    • Executado apenas se o módulo estiver ativado.
    • post-fs-data.sh é executado no modo post-fs-data, service.sh é executado no modo de serviço late_start, boot-completed.sh é executado na inicialização concluída e post-mount.sh é executado em overlayfs montado.

Todos os scripts de inicialização serão executados no shell BusyBox ash do KernelSU com o "Modo Autônomo" ativado.

Lançado sob a Licença GPL3.

- +
Skip to content

Guias de módulo

O KernelSU fornece um mecanismo de módulo que consegue modificar o diretório do sistema enquanto mantém a integridade da partição do sistema. Este mecanismo é conhecido como "sem sistema".

O mecanismo de módulo do KernelSU é quase o mesmo do Magisk. Se você está familiarizado com o desenvolvimento de módulos Magisk, o desenvolvimento de módulos KernelSU é muito semelhante. Você pode pular a introdução dos módulos abaixo e só precisa ler Diferença com Magisk.

BusyBox

O KernelSU vem com um recurso binário BusyBox completo (incluindo suporte completo ao SELinux). O executável está localizado em /data/adb/ksu/bin/busybox. O BusyBox do KernelSU suporta o "ASH Standalone Shell Mode" alternável em tempo de execução. O que este Modo Autônomo significa é que ao executar no shell ash do BusyBox, cada comando usará diretamente o miniaplicativo dentro do BusyBox, independentemente do que estiver definido como PATH. Por exemplo, comandos como ls, rm, chmod NÃO usarão o que está em PATH (no caso do Android por padrão será /system/bin/ls, /system/bin/rm e /system/bin/chmod respectivamente), mas em vez disso chamará diretamente os miniaplicativos internos do BusyBox. Isso garante que os scripts sempre sejam executados em um ambiente previsível e sempre tenham o conjunto completo de comandos, independentemente da versão do Android em que estão sendo executados. Para forçar um comando a NÃO usar o BusyBox, você deve chamar o executável com caminhos completos.

Cada script shell executado no contexto do KernelSU será executado no shell ash do BusyBox com o Modo Autônomo ativado. Para o que é relevante para desenvolvedores terceirizados, isso inclui todos os scripts de inicialização e scripts de instalação de módulos.

Para aqueles que desejam usar o recurso “Modo Autônomo” fora do KernelSU, existem 2 maneiras de ativá-los:

  1. Defina a variável de ambiente ASH_STANDALONE como 1
    Exemplo: ASH_STANDALONE=1 /data/adb/ksu/bin/busybox sh <script>
  2. Alternar com opções de linha de comando:
    /data/adb/ksu/bin/busybox sh -o standalone <script>

Para garantir que todos os shells sh subsequentes executados também sejam executados no Modo Autônomo, a opção 1 é o método preferido (e é isso que o KernelSU e o gerenciador KernelSU usam internamente), pois as variáveis ​​de ambiente são herdadas para os subprocesso.

DIFERENÇA COM MAGISK

O BusyBox do KernelSU agora está usando o arquivo binário compilado diretamente do projeto Magisk. Obrigado ao Magisk! Portanto, você não precisa se preocupar com problemas de compatibilidade entre scripts BusyBox no Magisk e KernelSU porque eles são exatamente iguais!

Módulos KernelSU

Um módulo KernelSU é uma pasta colocada em /data/adb/modules com a estrutura abaixo:

txt
/data/adb/modules
+├── .
+├── .
+|
+├── $MODID                  <--- A pasta é nomeada com o ID do módulo
+│   │
+│   │      *** Identidade do Módulo ***
+│   │
+│   ├── module.prop         <--- Este arquivo armazena os metadados do módulo
+│   │
+│   │      *** Conteúdo Principal ***
+│   │
+│   ├── system              <--- Esta pasta será montada se skip_mount não existir
+│   │   ├── ...
+│   │   ├── ...
+│   │   └── ...
+│   │
+│   │      *** Sinalizadores de Status ***
+│   │
+│   ├── skip_mount          <--- Se existir, o KernelSU NÃO montará sua pasta de sistema
+│   ├── disable             <--- Se existir, o módulo será desabilitado
+│   ├── remove              <--- Se existir, o módulo será removido na próxima reinicialização
+│   │
+│   │      *** Arquivos Opcionais ***
+│   │
+│   ├── post-fs-data.sh     <--- Este script será executado em post-fs-data
+│   ├── post-mount.sh       <--- Este script será executado em post-mount
+│   ├── service.sh          <--- Este script será executado no late_start service
+│   ├── boot-completed.sh   <--- Este script será executado na inicialização concluída
+|   ├── uninstall.sh        <--- Este script será executado quando o KernelSU remover seu módulo
+│   ├── system.prop         <--- As propriedades neste arquivo serão carregadas como propriedades do sistema por resetprop
+│   ├── sepolicy.rule       <--- Regras adicionais de sepolicy personalizadas
+│   │
+│   │      *** Gerado Automaticamente, NÃO CRIE OU MODIFIQUE MANUALMENTE ***
+│   │
+│   ├── vendor              <--- Um link simbólico para $MODID/system/vendor
+│   ├── product             <--- Um link simbólico para $MODID/system/product
+│   ├── system_ext          <--- Um link simbólico para $MODID/system/system_ext
+│   │
+│   │      *** Quaisquer arquivos/pastas adicionais são permitidos ***
+│   │
+│   ├── ...
+│   └── ...
+|
+├── another_module
+│   ├── .
+│   └── .
+├── .
+├── .
/data/adb/modules
+├── .
+├── .
+|
+├── $MODID                  <--- A pasta é nomeada com o ID do módulo
+│   │
+│   │      *** Identidade do Módulo ***
+│   │
+│   ├── module.prop         <--- Este arquivo armazena os metadados do módulo
+│   │
+│   │      *** Conteúdo Principal ***
+│   │
+│   ├── system              <--- Esta pasta será montada se skip_mount não existir
+│   │   ├── ...
+│   │   ├── ...
+│   │   └── ...
+│   │
+│   │      *** Sinalizadores de Status ***
+│   │
+│   ├── skip_mount          <--- Se existir, o KernelSU NÃO montará sua pasta de sistema
+│   ├── disable             <--- Se existir, o módulo será desabilitado
+│   ├── remove              <--- Se existir, o módulo será removido na próxima reinicialização
+│   │
+│   │      *** Arquivos Opcionais ***
+│   │
+│   ├── post-fs-data.sh     <--- Este script será executado em post-fs-data
+│   ├── post-mount.sh       <--- Este script será executado em post-mount
+│   ├── service.sh          <--- Este script será executado no late_start service
+│   ├── boot-completed.sh   <--- Este script será executado na inicialização concluída
+|   ├── uninstall.sh        <--- Este script será executado quando o KernelSU remover seu módulo
+│   ├── system.prop         <--- As propriedades neste arquivo serão carregadas como propriedades do sistema por resetprop
+│   ├── sepolicy.rule       <--- Regras adicionais de sepolicy personalizadas
+│   │
+│   │      *** Gerado Automaticamente, NÃO CRIE OU MODIFIQUE MANUALMENTE ***
+│   │
+│   ├── vendor              <--- Um link simbólico para $MODID/system/vendor
+│   ├── product             <--- Um link simbólico para $MODID/system/product
+│   ├── system_ext          <--- Um link simbólico para $MODID/system/system_ext
+│   │
+│   │      *** Quaisquer arquivos/pastas adicionais são permitidos ***
+│   │
+│   ├── ...
+│   └── ...
+|
+├── another_module
+│   ├── .
+│   └── .
+├── .
+├── .

DIFERENÇA COM MAGISK

O KernelSU não possui suporte integrado para o Zygisk, portanto não há conteúdo relacionado ao Zygisk no módulo. No entanto, você pode usar ZygiskNext para suportar módulos Zygisk. Neste caso, o conteúdo do módulo Zygisk é idêntico ao suportado pelo Magisk.

module.prop

module.prop é um arquivo de configuração para um módulo. No KernelSU, se um módulo não contiver este arquivo, ele não será reconhecido como um módulo. O formato deste arquivo é o seguinte:

txt
id=<string>
+name=<string>
+version=<string>
+versionCode=<int>
+author=<string>
+description=<string>
id=<string>
+name=<string>
+version=<string>
+versionCode=<int>
+author=<string>
+description=<string>
  • id deve corresponder a esta expressão regular: ^[a-zA-Z][a-zA-Z0-9._-]+$
    Exemplo: ✓ a_module, ✓ a.module, ✓ module-101, ✗ a module, ✗ 1_module, ✗ -a-module
    Este é o identificador exclusivo do seu módulo. Você não deve alterá-lo depois de publicado.
  • versionCode deve ser um inteiro. Isso é usado para comparar versões
  • Outros que não foram mencionados acima podem ser qualquer string de linha única.
  • Certifique-se de usar o tipo de quebra de linha UNIX (LF) e não o Windows (CR+LF) ou Macintosh (CR).

Shell scripts

Por favor, leia a seção Scripts de inicialização para entender a diferença entre post-fs-data.sh e service.sh. Para a maioria dos desenvolvedores de módulos, service.sh deve ser bom o suficiente se você precisar apenas executar um script de inicialização. Se precisar executar o script após a inicialização ser concluída, use boot-completed.sh. Se você quiser fazer algo após montar overlayfs, use post-mount.sh.

Em todos os scripts do seu módulo, use MODDIR=${0%/*} para obter o caminho do diretório base do seu módulo, NÃO codifique o caminho do seu módulo em scripts.

DIFERENÇA COM MAGISK

Você pode usar a variável de ambiente KSU para determinar se um script está sendo executado no KernelSU ou Magisk. Se estiver executando no KernelSU, esse valor será definido como true.

Diretório system

O conteúdo deste diretório será sobreposto à partição /system do sistema usando overlayfs após a inicialização do sistema. Isso significa que:

  1. Arquivos com o mesmo nome daqueles no diretório correspondente no sistema serão substituídos pelos arquivos deste diretório.
  2. Pastas com o mesmo nome daquelas no diretório correspondente no sistema serão mescladas com as pastas neste diretório.

Se você deseja excluir um arquivo ou pasta no diretório original do sistema, você precisa criar um arquivo com o mesmo nome do arquivo/pasta no diretório do módulo usando mknod filename c 0 0. Dessa forma, o sistema overlayfs irá automaticamente "branquear" este arquivo como se ele tivesse sido excluído (a partição /system não foi realmente alterada).

Você também pode declarar uma variável chamada REMOVE contendo uma lista de diretórios em customize.sh para executar operações de remoção, e o KernelSU executará automaticamente mknod <TARGET> c 0 0 nos diretórios correspondentes do módulo. Por exemplo:

sh
REMOVE="
+/system/app/YouTube
+/system/app/Bloatware
+"
REMOVE="
+/system/app/YouTube
+/system/app/Bloatware
+"

A lista acima irá executar mknod $MODPATH/system/app/YouTube c 0 0 e mknod $MODPATH/system/app/Bloatware c 0 0; e /system/app/YouTube e /system/app/Bloatware serão removidos após o módulo entrar em vigor.

Se você deseja substituir um diretório no sistema, você precisa criar um diretório com o mesmo caminho no diretório do módulo e, em seguida, definir o atributo setfattr -n trusted.overlay.opaque -v y <TARGET> para este diretório. Desta forma, o sistema overlayfs substituirá automaticamente o diretório correspondente no sistema (sem alterar a partição /system).

Você pode declarar uma variável chamada REPLACE em seu arquivo customize.sh, que inclui uma lista de diretórios a serem substituídos, e o KernelSU executará automaticamente as operações correspondentes em seu diretório de módulo. Por exemplo:

sh
REPLACE="
+/system/app/YouTube
+/system/app/Bloatware
+"
REPLACE="
+/system/app/YouTube
+/system/app/Bloatware
+"

Esta lista criará automaticamente os diretórios $MODPATH/system/app/YouTube e $MODPATH/system/app/Bloatware e, em seguida, executará setfattr -n trusted.overlay.opaque -v y $MODPATH/system/app/YouTube e setfattr -n trusted.overlay.opaque -v y $MODPATH/system/app/Bloatware. Após o módulo entrar em vigor, /system/app/YouTube e /system/app/Bloatware serão substituídos por diretórios vazios.

DIFERENÇA COM MAGISK

O mecanismo sem sistema do KernelSU é implementado através do overlayfs do kernel, enquanto o Magisk atualmente usa montagem mágica (montagem de ligação). Os dois métodos de implementação têm diferenças significativas, mas o objetivo final é o mesmo: modificar os arquivos /system sem modificar fisicamente a partição /system.

Se você estiver interessado em overlayfs, é recomendável ler a documentação sobre overlayfs do Kernel Linux.

system.prop

Este arquivo segue o mesmo formato de build.prop. Cada linha é composta por [key]=[value].

sepolicy.rule

Se o seu módulo exigir alguns patches adicionais de sepolicy, adicione essas regras a este arquivo. Cada linha neste arquivo será tratada como uma declaração de política.

Instalador do módulo

Um instalador do módulo KernelSU é um módulo KernelSU empacotado em um arquivo zip que pode ser atualizado no app gerenciador KernelSU. O instalador do módulo KernelSU mais simples é apenas um módulo KernelSU compactado como um arquivo zip.

txt
module.zip
+
+├── customize.sh                       <--- (Opcional, mais detalhes posteriormente)
+│                                           Este script será fornecido por update-binary
+├── ...
+├── ...  /* O resto dos arquivos do módulo */
+
module.zip
+
+├── customize.sh                       <--- (Opcional, mais detalhes posteriormente)
+│                                           Este script será fornecido por update-binary
+├── ...
+├── ...  /* O resto dos arquivos do módulo */
+

AVISO

O módulo KernelSU NÃO é compatível para instalação no Recovery personalizado!

Personalização

Se você precisar personalizar o processo de instalação do módulo, opcionalmente você pode criar um script no instalador chamado customize.sh. Este script será sourced (não executado!) pelo script do instalador do módulo depois que todos os arquivos forem extraídos e as permissões padrão e o contexto secundário forem aplicados. Isso é muito útil se o seu módulo exigir configuração adicional com base na API do dispositivo ou se você precisar definir permissões/secontext especiais para alguns dos arquivos do seu módulo.

Se você quiser controlar e personalizar totalmente o processo de instalação, declare SKIPUNZIP=1 em customize.sh para pular todas as etapas de instalação padrão. Ao fazer isso, seu customize.sh será responsável por instalar tudo sozinho.

O script customize.sh é executado no shell BusyBox ash do KernelSU com o "Modo Autônomo" ativado. As seguintes variáveis ​​e funções estão disponíveis:

Variáveis

  • KSU (bool): uma variável para marcar que o script está sendo executado no ambiente KernelSU, e o valor desta variável sempre será true. Você pode usá-lo para distinguir entre KernelSU e Magisk.
  • KSU_VER (string): a string da versão do KernelSU atualmente instalado (por exemplo, v0.4.0).
  • KSU_VER_CODE (int): o código da versão do KernelSU atualmente instalado no espaço do usuário (por exemplo: 10672).
  • KSU_KERNEL_VER_CODE (int): o código da versão do KernelSU atualmente instalado no espaço do kernel (por exemplo: 10672).
  • BOOTMODE (bool): sempre seja true no KernelSU.
  • MODPATH (path): o caminho onde os arquivos do seu módulo devem ser instalados.
  • TMPDIR (path): um lugar onde você pode armazenar arquivos temporariamente.
  • ZIPFILE (path): zip de instalação do seu módulo.
  • ARCH (string): a arquitetura da CPU do dispositivo. O valor é arm, arm64, x86 ou x64.
  • IS64BIT (bool): true se $ARCH for arm64 ou x64.
  • API (int): o nível da API (versão do Android) do dispositivo (por exemplo: 23 para Android 6.0).

AVISO

No KernelSU, MAGISK_VER_CODE é sempre 25200 e MAGISK_VER é sempre v25.2. Por favor, não use essas duas variáveis ​​para determinar se ele está sendo executado no KernelSU ou não.

Funções

txt
ui_print <msg>
+    imprima <msg> no console
+    Evite usar 'echo', pois ele não será exibido no console de recuperação personalizado
+
+abort <msg>
+    imprima mensagem de erro <msg> para consolar e encerrar a instalação
+    Evite usar 'exit', pois isso irá pular as etapas de limpeza de encerramento
+
+set_perm <target> <owner> <group> <permission> [context]
+    se [context] não estiver definido, o padrão é "u:object_r:system_file:s0"
+    esta função é uma abreviação para os seguintes comandos:
+       chown owner.group target
+       chmod permission target
+       chcon context target
+
+set_perm_recursive <directory> <owner> <group> <dirpermission> <filepermission> [context]
+    se [context] não está definido, o padrão é "u:object_r:system_file:s0"
+    para todos os arquivos em <directory>, ele chamará:
+       contexto de permissão de arquivo do grupo proprietário do arquivo set_perm
+    para todos os diretórios em <directory> (including itself), ele vai ligar:
+       set_perm dir owner group dirpermission context
ui_print <msg>
+    imprima <msg> no console
+    Evite usar 'echo', pois ele não será exibido no console de recuperação personalizado
+
+abort <msg>
+    imprima mensagem de erro <msg> para consolar e encerrar a instalação
+    Evite usar 'exit', pois isso irá pular as etapas de limpeza de encerramento
+
+set_perm <target> <owner> <group> <permission> [context]
+    se [context] não estiver definido, o padrão é "u:object_r:system_file:s0"
+    esta função é uma abreviação para os seguintes comandos:
+       chown owner.group target
+       chmod permission target
+       chcon context target
+
+set_perm_recursive <directory> <owner> <group> <dirpermission> <filepermission> [context]
+    se [context] não está definido, o padrão é "u:object_r:system_file:s0"
+    para todos os arquivos em <directory>, ele chamará:
+       contexto de permissão de arquivo do grupo proprietário do arquivo set_perm
+    para todos os diretórios em <directory> (including itself), ele vai ligar:
+       set_perm dir owner group dirpermission context

Scripts de inicialização

No KernelSU, os scripts são divididos em dois tipos com base em seu modo de execução: modo post-fs-data e modo de serviço late_start:

  • modo post-fs-data
    • Esta etapa está BLOQUEANDO. O processo de inicialização é pausado antes da execução ser concluída ou 10 segundos se passaram.
    • Os scripts são executados antes de qualquer módulo ser montado. Isso permite que um desenvolvedor de módulo ajuste dinamicamente seus módulos antes de serem montados.
    • Este estágio acontece antes do início do Zygote, o que significa praticamente tudo no Android.
    • AVISO: Usar setprop irá bloquear o processo de inicialização! Por favor, use resetprop -n <prop_name> <prop_value> em vez disso.
    • Execute scripts neste modo apenas se necessário.
  • modo de serviço late_start
    • Esta etapa é SEM BLOQUEIO. Seu script é executado em paralelo com o restante do processo de inicialização.
    • Este é o estágio recomendado para executar a maioria dos scripts.

No KernelSU, os scripts de inicialização são divididos em dois tipos com base no local de armazenamento: scripts gerais e scripts de módulo:

  • Scripts gerais
    • Colocado em /data/adb/post-fs-data.d, /data/adb/service.d, /data/adb/post-mount.d ou /data/adb/boot-completed.d.
    • Somente executado se o script estiver definido como executável (chmod +x script.sh).
    • Os scripts em post-fs-data.d são executados no modo post-fs-data e os scripts em service.d são executados no modo de serviço late_start.
    • Os módulos NÃO devem adicionar scripts gerais durante a instalação.
  • Scripts de módulo
    • Colocado na própria pasta do módulo.
    • Executado apenas se o módulo estiver ativado.
    • post-fs-data.sh é executado no modo post-fs-data, service.sh é executado no modo de serviço late_start, boot-completed.sh é executado na inicialização concluída e post-mount.sh é executado em overlayfs montado.

Todos os scripts de inicialização serão executados no shell BusyBox ash do KernelSU com o "Modo Autônomo" ativado.

Lançado sob a Licença GPL3.

+ \ No newline at end of file diff --git a/pt_BR/guide/rescue-from-bootloop.html b/pt_BR/guide/rescue-from-bootloop.html index 3502c641446f..c551598a5de7 100644 --- a/pt_BR/guide/rescue-from-bootloop.html +++ b/pt_BR/guide/rescue-from-bootloop.html @@ -5,18 +5,19 @@ Resgate do bootloop | KernelSU - - + + + - - - - + + + + + -
Skip to content
On this page

Resgate do bootloop

Ao atualizar um dispositivo, podemos encontrar situações em que o dispositivo fica "bloqueado". Em teoria, se você usar o fastboot apenas para atualizar a partição boot ou instalar módulos inadequados que causam falha na inicialização do dispositivo, isso poderá ser restaurado por meio de operações apropriadas. Este documento tem como objetivo fornecer alguns métodos de emergência para ajudá-lo a se recuperar de um dispositivo "bloqueado".

Bloqueio por fleshear partição boot

No KernelSU, as seguintes situações podem causar bloqueio de inicialização ao fleshear a partição boot:

  1. Você atualizou uma imagem boot no formato errado. Por exemplo, se o formato de boot do seu telefone for gz, mas você atualizou uma imagem no formato lz4, o telefone não será capaz de inicializar.
  2. Seu telefone precisa desativar a verificação AVB para inicializar corretamente (geralmente exigindo a limpeza de todos os dados do telefone).
  3. Seu kernel tem alguns bugs ou não é adequado para o flash do seu telefone.

Não importa qual seja a situação, você pode recuperar fleshando a imagem de boot padrão. Portanto, no início do tutorial de instalação, recomendamos fortemente que você faça backup de seu boot padrão antes de fleshar. Se você não fez backup, poderá obter o boot original de fábrica de outros usuários com o mesmo dispositivo que você ou do firmware oficial.

Bloqueio por módulos

A instalação de módulos pode ser uma causa mais comum de bloqueio do seu dispositivo, mas devemos avisá-lo seriamente: NÃO INSTALE MÓDULOS DE FONTES DESCONHECIDAS! Como os módulos têm privilégios root, eles podem causar danos irreversíveis ao seu dispositivo!

Módulos normais

Se você instalou um módulo que foi comprovadamente seguro, mas faz com que seu dispositivo não inicialize, então esta situação é facilmente recuperável no KernelSU sem qualquer preocupação. O KernelSU possui mecanismos integrados para recuperar seu dispositivo, incluindo o seguinte:

  1. Atualização AB
  2. Recupere pressionando o botão de diminuir volume

Atualização AB

As atualizações do módulo KernelSU inspiram-se no mecanismo de atualização AB do sistema Android usado em atualizações OTA. Se você instalar um novo módulo ou atualizar um existente, isso não modificará diretamente o arquivo do módulo usado atualmente. Em vez disso, todos os módulos serão integrados em outra imagem de atualização. Depois que o sistema for reiniciado, ele tentará começar a usar esta imagem de atualização. Se o sistema Android inicializar com sucesso, os módulos serão realmente atualizados.

Portanto, o método mais simples e comumente usado para recuperar seu dispositivo é forçar uma reinicialização. Se você não conseguir iniciar o sistema após instalar um módulo, você pode pressionar e segurar o botão liga/desliga por mais de 10 segundos e o sistema será reinicializado automaticamente. Após a reinicialização, ele retornará ao estado anterior à atualização do módulo e os módulos atualizados anteriormente serão desativados automaticamente.

Recupere pressionando o botão de diminuir volume

Se as atualizações AB ainda não resolverem o problema, você pode tentar usar o Modo de Segurança. No Modo de Segurança, todos os módulos estão desabilitados.

Existem duas maneiras de entrar no Modo de Segurança:

  1. O Modo de Segurança integrado de alguns sistemas. Alguns sistemas possuem um Modo de Segurança integrado que pode ser acessado pressionando longamente o botão de diminuir volume, enquanto outros (como a MIUI) podem ativar o Modo de Segurança no Recovery. Ao entrar no Modo de Segurança do sistema, o KernelSU também entrará no Modo de Segurança e desativará automaticamente os módulos.
  2. O Modo de Segurança integrado do KernelSU. O método de operação é pressionar a tecla de diminuir volume continuamente por mais de três vezes após a primeira tela de inicialização.

Após entrar no Modo de Segurança, todos os módulos na página de módulos do gerenciador KernelSU são desabilitados, mas você pode executar operações de "desinstalação" para desinstalar quaisquer módulos que possam estar causando problemas.

O Modo de Segurança integrado é implementado no kernel, portanto não há possibilidade de perder eventos importantes devido à interceptação. No entanto, para kernels não GKI, a integração manual do código pode ser necessária e você pode consultar a documentação oficial para obter orientação.

Módulos maliciosos

Se os métodos acima não conseguirem recuperar seu dispositivo, é altamente provável que o módulo que você instalou tenha operações maliciosas ou tenha danificado seu dispositivo por outros meios. Neste caso, existem apenas duas sugestões:

  1. Limpe os dados e instale o sistema oficial.
  2. Consulte o serviço pós-venda.

Lançado sob a Licença GPL3.

- +
Skip to content

Resgate do bootloop

Ao atualizar um dispositivo, podemos encontrar situações em que o dispositivo fica "bloqueado". Em teoria, se você usar o fastboot apenas para atualizar a partição boot ou instalar módulos inadequados que causam falha na inicialização do dispositivo, isso poderá ser restaurado por meio de operações apropriadas. Este documento tem como objetivo fornecer alguns métodos de emergência para ajudá-lo a se recuperar de um dispositivo "bloqueado".

Bloqueio por fleshear partição boot

No KernelSU, as seguintes situações podem causar bloqueio de inicialização ao fleshear a partição boot:

  1. Você atualizou uma imagem boot no formato errado. Por exemplo, se o formato de boot do seu telefone for gz, mas você atualizou uma imagem no formato lz4, o telefone não será capaz de inicializar.
  2. Seu telefone precisa desativar a verificação AVB para inicializar corretamente (geralmente exigindo a limpeza de todos os dados do telefone).
  3. Seu kernel tem alguns bugs ou não é adequado para o flash do seu telefone.

Não importa qual seja a situação, você pode recuperar fleshando a imagem de boot padrão. Portanto, no início do tutorial de instalação, recomendamos fortemente que você faça backup de seu boot padrão antes de fleshar. Se você não fez backup, poderá obter o boot original de fábrica de outros usuários com o mesmo dispositivo que você ou do firmware oficial.

Bloqueio por módulos

A instalação de módulos pode ser uma causa mais comum de bloqueio do seu dispositivo, mas devemos avisá-lo seriamente: NÃO INSTALE MÓDULOS DE FONTES DESCONHECIDAS! Como os módulos têm privilégios root, eles podem causar danos irreversíveis ao seu dispositivo!

Módulos normais

Se você instalou um módulo que foi comprovadamente seguro, mas faz com que seu dispositivo não inicialize, então esta situação é facilmente recuperável no KernelSU sem qualquer preocupação. O KernelSU possui mecanismos integrados para recuperar seu dispositivo, incluindo o seguinte:

  1. Atualização AB
  2. Recupere pressionando o botão de diminuir volume

Atualização AB

As atualizações do módulo KernelSU inspiram-se no mecanismo de atualização AB do sistema Android usado em atualizações OTA. Se você instalar um novo módulo ou atualizar um existente, isso não modificará diretamente o arquivo do módulo usado atualmente. Em vez disso, todos os módulos serão integrados em outra imagem de atualização. Depois que o sistema for reiniciado, ele tentará começar a usar esta imagem de atualização. Se o sistema Android inicializar com sucesso, os módulos serão realmente atualizados.

Portanto, o método mais simples e comumente usado para recuperar seu dispositivo é forçar uma reinicialização. Se você não conseguir iniciar o sistema após instalar um módulo, você pode pressionar e segurar o botão liga/desliga por mais de 10 segundos e o sistema será reinicializado automaticamente. Após a reinicialização, ele retornará ao estado anterior à atualização do módulo e os módulos atualizados anteriormente serão desativados automaticamente.

Recupere pressionando o botão de diminuir volume

Se as atualizações AB ainda não resolverem o problema, você pode tentar usar o Modo de Segurança. No Modo de Segurança, todos os módulos estão desabilitados.

Existem duas maneiras de entrar no Modo de Segurança:

  1. O Modo de Segurança integrado de alguns sistemas. Alguns sistemas possuem um Modo de Segurança integrado que pode ser acessado pressionando longamente o botão de diminuir volume, enquanto outros (como a MIUI) podem ativar o Modo de Segurança no Recovery. Ao entrar no Modo de Segurança do sistema, o KernelSU também entrará no Modo de Segurança e desativará automaticamente os módulos.
  2. O Modo de Segurança integrado do KernelSU. O método de operação é pressionar a tecla de diminuir volume continuamente por mais de três vezes após a primeira tela de inicialização.

Após entrar no Modo de Segurança, todos os módulos na página de módulos do gerenciador KernelSU são desabilitados, mas você pode executar operações de "desinstalação" para desinstalar quaisquer módulos que possam estar causando problemas.

O Modo de Segurança integrado é implementado no kernel, portanto não há possibilidade de perder eventos importantes devido à interceptação. No entanto, para kernels não GKI, a integração manual do código pode ser necessária e você pode consultar a documentação oficial para obter orientação.

Módulos maliciosos

Se os métodos acima não conseguirem recuperar seu dispositivo, é altamente provável que o módulo que você instalou tenha operações maliciosas ou tenha danificado seu dispositivo por outros meios. Neste caso, existem apenas duas sugestões:

  1. Limpe os dados e instale o sistema oficial.
  2. Consulte o serviço pós-venda.

Lançado sob a Licença GPL3.

+ \ No newline at end of file diff --git a/pt_BR/guide/unofficially-support-devices.html b/pt_BR/guide/unofficially-support-devices.html index 5b277de13d9b..97bac1106e24 100644 --- a/pt_BR/guide/unofficially-support-devices.html +++ b/pt_BR/guide/unofficially-support-devices.html @@ -5,19 +5,20 @@ Dispositivos com suporte não oficial | KernelSU - - + + + - - - - - + + + + + + -
Skip to content
On this page

Dispositivos com suporte não oficial

AVISO

Nesta página, existem kernels para dispositivos não GKI que suportam o KernelSU mantidos por outros desenvolvedores.

AVISO

Esta página é apenas para você encontrar o código-fonte correspondente ao seu dispositivo, NÃO significa que o código-fonte foi revisado pelos desenvolvedores do KernelSU. Você deve usá-lo por sua própria conta e risco.

MantenedorRepositórioDispositivos suportados
diphonsD8G_Kernel_oxygenPoco F3: alioth | Poco F4: munch | MI10T/PRO: Apollo
diphonsD8G_Kernel_SM8150Poco X3 Pro: Vayu | Bhima
diphonsD8G_Kernel_SDM845Poco F1 | MI8 | MiMix2S
diphonsD8G_Kernel_SM8350Vili
diphonsD8G_Kernel_MarblePoco F5 | Redmi Note 12 Turbo | Marble
th1nhhdkandroid_kernel_sony_sm8250-kernelsuSony Xperia 1 II | Sony Xperia 5 II
akash07knexus_kernel_xiaomi_sm8250Poco F4: munch
HMTheBoy154Darkmatter-kernelGeneric x86_64 devices running Android-x86
Asuka-mioandroid_kernel_xiaomi_casMi 10 Ultra: cas
xiaoleGunMiku_kernel_xiaomi_waynewayne
YamazakuraHkernel_xiaomi_cepheuscepheus for pixel experience
SakuraNotStupidandroid_kernel_xiaomi_sdm710Xiaomi MI 8 SE(sirius/xmsirius)
Aquarius223(paper)android_kernel_xiaomi_msm8998MI 6 (sagit) and MIX 2 (chiron) for LineageOS
SlackerStateandroid_kernel_xiaomi_sm6150Redmi K30 4G (phoenix)
RooGhz720kernel_xiaomi_sm6150REDMI NOTE 10 PRO (sweet)
OnlyTomInSecondandroid_kernel_xiaomi_sdm845Mi 8 (dipper) for LineageOS
Rohail33RealKing KernelApollo(Redmi K30S Ultra/Mi 10T/Mi 10T Pro),Alioth(Redmi K40/POCO F3/Mi 11X),Munch(Redmi K40S/POCO F4), both MIUI and AOSP.
lateautumn233android_kernel_oneplus_sm8250OnePlus 8 Serials
Sreeshankar KNeverSettle KernelOnePlus Nord (avicii)
Molyuuneko_kernel_xiaomi_gauguinRedmi Note 9 Pro/ Mi 10T Lite/ Mi 10i
guh0613android_kernel_oppo_sm8150OPPO Reno Ace (OP4A89)
LeviMarvinandroid_kernel_xiaomi_aliothRedmi K40 / POCO F3
cibimokernel_xiaomi_raphael_ksuXiaomi Redmi K20 Pro / Mi 9T Pro (raphael)
EndCreditskernel_xiaomi_sm7250Redmi K30 5G ( picasso ) , Redmi K30i 5G ( picasso_48m)
msnxandroid-msm-coral-4.14-android13Pixel 4 XL
SoDebugkernel_xiaomi_raphaelXiaomi Redmi K20 Pro / Mi 9T Pro (raphael)
H1mJTkernel_realme_RMX1901Realme X (RMX1901/RMX1901CN)
SonalSingh18android_kernel_xiaomi_sm6250Miatoll [curtana, excalibur, gram, joyeuse]
RooGhz720kernel_xiaomi_lavenderRedmi Note 7 (Lavender)
JunASAKAkernel_google_msm-4.9_KernelSUGoogle Pixel 3a & 3a XL (sargo & bonito)
RooGhz720kernel_asus_X01BDAsus Zenfone Max Pro M1/M2
Evans Mikekernel_xiaomi_raphael_bool-xXiaomi Redmi K20 Pro / Mi 9T Pro (raphael)
easterNdayKSU_Thyme_BuildBotXiaomi 10S
tedomi2705kernel_xiaomi_sdm660Xiaomi Redmi Note 6 Pro (tulip)
RyuujiXandroid_kernel_asus_sdm660-4.19Asus Zenfone Max Pro M1 (X00TD)/ M2 (X01BD) (Linux 4.19)
liqidecgandroid_kernel_xiaomi_sdm710Mi 8 SE (sirius) (Linux 4.9)
Abdelhay-Aliandroid_kernel_huawei_hi6250Huawei P20 lite (hi6250) (Linux 4.9)
reocatandroid_kernel_nokia_sdm660_ksuNokia 6.1 (2018)
Tuan Anhkernel_nokia_msm8998Nokia 8 (Repartitioned)/8 Sirocco (NLA/A1N)
bggRGjQaUbCoEandroid_kernel_samsung_sm8250Samsung Galaxy S20+ 5G (y2q)
SOVIET-ANDROIDkernel_xiaomi_raphaelXK20 Pro Raphael DSP & A only SAR support
dabao1955android_kernel_OPPO_PEQM00MediaTek devices in the OPPO Reno series on ColorOS13.x
Aflaungosandroid_kernel_motorola_msm8998Moto G6 Plus (evert)
TheNoFacekernel_oneplus_msm8998OnePlus 5/5T (cheeseburger/dumpling)
Nipin NA (Joker-V2)Xcalibur kernel (violet)Xiaomi Redmi Note 7 Pro (violet)
Sr-Hankernel_xiaomi_mojitoRedmi Note 10 (Sunny/Mojito)
dabao1955android_kernel_OPPO_OP4ED5OPPO Reno6 on ColorOS11.x/ColorOS12.x,kernel version4.14.186+
rushiranpiseandroid_kernel_samsung_exynos9610_mintKernel 4.14.194 exynos9610 Non-GKI Device, Added KernelSu using manual method
CN-Scarspixel_experience_kernel_xiaomi_sm8250_kernelSUPixel Experience Kernel 4.19.282 for Xiaomi Redmi K40S / Xiaomi Poco F4 (munch)
exerMiyo TokuSony Tama (akari, apollo, aurora, akatsuki) (Linux 4.9)
likkaiQuicksilver KernelXiaomi 11 Lite 5G NE (lisa)
awakened1712android_kernel_samsung_exynos9820Samsung S10/N10
awakened1712android_kernel_oneplus_sm8350Oneplus 9/9Pro
siimsekLightning KernelXiaomi Redmi Note 8/8T (ginkgo/willow)
GiovanYCringekernel_a50Galaxy A50 (A505f,fn,g,gn,gt)
Asriadi Rahimandroid_kernel_google_wahooGoogle Pixel 2/2XL
Flameandroid_kernel_xiaomi_sm8250POCO F3/Redmi K40/Mi 11X (alioth)
DawfukFRkernel_oneplus_sm8250 (Stellaris-Kernel)Oneplus 8 (Instantnoodle), Oneplus 8T (Kebab), Oneplus 8 Pro (Instantnoodlep), Oneplus 9R (lemonades) [msm-4.19]
Tejas Singhkernel_xiaomi_ginkgo (Cuh Kernel)Xiaomi Redmi Note 8/8T (ginkgo/willow)
Coconutatandroid_kernel_xiaomi_ruby_expRedmi Note 12 Pro / Pro+ For MIUI 14
Coconutatandroid_kernel_xiaomi_sdm845_expXiaomi Mi 8 Pro(UD)[equuleus]/Explorer Edition[ursa]
Abdul Wahid Khankernel_xiaomi_begoniaRedmi Note 8 Pro (Begonia/Begoniain)
Abdul Wahid Khankernel_xiaomi_marbleXiaomi Poco F5 / Redmi Note 12 Turbo (Marble)
zfdx123kernel_xiaomi_aliothRedmi K40 (alioth)
wxt1221android_kernel_oneplus_sdm845Oneplus 6 (enchilada) | Oneplus 6T (fajita) with no Retrofit Dynamic Partitions
zharzinhooKernel-Oriente-GuampMoto g9 play
hc841NovaCore_kernelMoto g9 play
zlm324android_kernel_xiaomi_msm8998_ksua lineage kernel forked from LineageOS official repository, version 4.4.302, added ksu.
zlm324android_kernel_xiaomi_msm8953_ksuXiaomi 5X (tiffany)
Vincent4440android_kernel_xiaomi_sm8250Poco F4: munch
zharzinhooandroid_kernel_motorola_cebuMoto g9 power
tiandoufayaleandroid_kernel_samsung_sm8250Samsung Tab S7 WIFI(T870)
starmoeandroid_kernel_oneplus_msm8998Oneplus 5/5T, kernel version 4.4.302
supechickenkernel_xiaomi_sm8350 Xiaomi 11T Pro (vili)
Vaz15kkernelSU-A70qSamsung Galaxy A70 ( A705MN / A705FN )
whyakariandroid_kernel_xiaomi_ginkgonon-GKI ginkgo
wulan17android_kernel_xiaomi_mt6765Redmi 6/6A (cereus/cactus)
officialputuidandroid_kernel_realme_mt6785Realme 6/6i(Indian)/6s/7/Narzo/Narzo 20 Pro/Narzo 30 4G (RM6785)

Lançado sob a Licença GPL3.

- +
Skip to content

Dispositivos com suporte não oficial

AVISO

Nesta página, existem kernels para dispositivos não GKI que suportam o KernelSU mantidos por outros desenvolvedores.

AVISO

Esta página é apenas para você encontrar o código-fonte correspondente ao seu dispositivo, NÃO significa que o código-fonte foi revisado pelos desenvolvedores do KernelSU. Você deve usá-lo por sua própria conta e risco.

MantenedorRepositórioDispositivos suportados
diphonsD8G_Kernel_oxygenPoco F3: alioth | Poco F4: munch | MI10T/PRO: Apollo
diphonsD8G_Kernel_SM8150Poco X3 Pro: Vayu | Bhima
diphonsD8G_Kernel_SDM845Poco F1 | MI8 | MiMix2S
diphonsD8G_Kernel_SM8350Vili
diphonsD8G_Kernel_MarblePoco F5 | Redmi Note 12 Turbo | Marble
th1nhhdkandroid_kernel_sony_sm8250-kernelsuSony Xperia 1 II | Sony Xperia 5 II
akash07knexus_kernel_xiaomi_sm8250Poco F4: munch
HMTheBoy154Darkmatter-kernelGeneric x86_64 devices running Android-x86
Asuka-mioandroid_kernel_xiaomi_casMi 10 Ultra: cas
xiaoleGunMiku_kernel_xiaomi_waynewayne
YamazakuraHkernel_xiaomi_cepheuscepheus for pixel experience
SakuraNotStupidandroid_kernel_xiaomi_sdm710Xiaomi MI 8 SE(sirius/xmsirius)
Aquarius223(paper)android_kernel_xiaomi_msm8998MI 6 (sagit) and MIX 2 (chiron) for LineageOS
SlackerStateandroid_kernel_xiaomi_sm6150Redmi K30 4G (phoenix)
RooGhz720kernel_xiaomi_sm6150REDMI NOTE 10 PRO (sweet)
OnlyTomInSecondandroid_kernel_xiaomi_sdm845Mi 8 (dipper) for LineageOS
Rohail33RealKing KernelApollo(Redmi K30S Ultra/Mi 10T/Mi 10T Pro),Alioth(Redmi K40/POCO F3/Mi 11X),Munch(Redmi K40S/POCO F4), both MIUI and AOSP.
lateautumn233android_kernel_oneplus_sm8250OnePlus 8 Serials
Sreeshankar KNeverSettle KernelOnePlus Nord (avicii)
Molyuuneko_kernel_xiaomi_gauguinRedmi Note 9 Pro/ Mi 10T Lite/ Mi 10i
guh0613android_kernel_oppo_sm8150OPPO Reno Ace (OP4A89)
LeviMarvinandroid_kernel_xiaomi_aliothRedmi K40 / POCO F3
cibimokernel_xiaomi_raphael_ksuXiaomi Redmi K20 Pro / Mi 9T Pro (raphael)
EndCreditskernel_xiaomi_sm7250Redmi K30 5G ( picasso ) , Redmi K30i 5G ( picasso_48m)
msnxandroid-msm-coral-4.14-android13Pixel 4 XL
SoDebugkernel_xiaomi_raphaelXiaomi Redmi K20 Pro / Mi 9T Pro (raphael)
H1mJTkernel_realme_RMX1901Realme X (RMX1901/RMX1901CN)
SonalSingh18android_kernel_xiaomi_sm6250Miatoll [curtana, excalibur, gram, joyeuse]
RooGhz720kernel_xiaomi_lavenderRedmi Note 7 (Lavender)
JunASAKAkernel_google_msm-4.9_KernelSUGoogle Pixel 3a & 3a XL (sargo & bonito)
RooGhz720kernel_asus_X01BDAsus Zenfone Max Pro M1/M2
Evans Mikekernel_xiaomi_raphael_bool-xXiaomi Redmi K20 Pro / Mi 9T Pro (raphael)
easterNdayKSU_Thyme_BuildBotXiaomi 10S
tedomi2705kernel_xiaomi_sdm660Xiaomi Redmi Note 6 Pro (tulip)
RyuujiXandroid_kernel_asus_sdm660-4.19Asus Zenfone Max Pro M1 (X00TD)/ M2 (X01BD) (Linux 4.19)
liqidecgandroid_kernel_xiaomi_sdm710Mi 8 SE (sirius) (Linux 4.9)
Abdelhay-Aliandroid_kernel_huawei_hi6250Huawei P20 lite (hi6250) (Linux 4.9)
reocatandroid_kernel_nokia_sdm660_ksuNokia 6.1 (2018)
Tuan Anhkernel_nokia_msm8998Nokia 8 (Repartitioned)/8 Sirocco (NLA/A1N)
bggRGjQaUbCoEandroid_kernel_samsung_sm8250Samsung Galaxy S20+ 5G (y2q)
SOVIET-ANDROIDkernel_xiaomi_raphaelXK20 Pro Raphael DSP & A only SAR support
dabao1955android_kernel_OPPO_PEQM00MediaTek devices in the OPPO Reno series on ColorOS13.x
Aflaungosandroid_kernel_motorola_msm8998Moto G6 Plus (evert)
TheNoFacekernel_oneplus_msm8998OnePlus 5/5T (cheeseburger/dumpling)
Nipin NA (Joker-V2)Xcalibur kernel (violet)Xiaomi Redmi Note 7 Pro (violet)
Sr-Hankernel_xiaomi_mojitoRedmi Note 10 (Sunny/Mojito)
dabao1955android_kernel_OPPO_OP4ED5OPPO Reno6 on ColorOS11.x/ColorOS12.x,kernel version4.14.186+
rushiranpiseandroid_kernel_samsung_exynos9610_mintKernel 4.14.194 exynos9610 Non-GKI Device, Added KernelSu using manual method
CN-Scarspixel_experience_kernel_xiaomi_sm8250_kernelSUPixel Experience Kernel 4.19.282 for Xiaomi Redmi K40S / Xiaomi Poco F4 (munch)
exerMiyo TokuSony Tama (akari, apollo, aurora, akatsuki) (Linux 4.9)
likkaiQuicksilver KernelXiaomi 11 Lite 5G NE (lisa)
awakened1712android_kernel_samsung_exynos9820Samsung S10/N10
awakened1712android_kernel_oneplus_sm8350Oneplus 9/9Pro
siimsekLightning KernelXiaomi Redmi Note 8/8T (ginkgo/willow)
GiovanYCringekernel_a50Galaxy A50 (A505f,fn,g,gn,gt)
Asriadi Rahimandroid_kernel_google_wahooGoogle Pixel 2/2XL
Flameandroid_kernel_xiaomi_sm8250POCO F3/Redmi K40/Mi 11X (alioth)
DawfukFRkernel_oneplus_sm8250 (Stellaris-Kernel)Oneplus 8 (Instantnoodle), Oneplus 8T (Kebab), Oneplus 8 Pro (Instantnoodlep), Oneplus 9R (lemonades) [msm-4.19]
Tejas Singhkernel_xiaomi_ginkgo (Cuh Kernel)Xiaomi Redmi Note 8/8T (ginkgo/willow)
Coconutatandroid_kernel_xiaomi_ruby_expRedmi Note 12 Pro / Pro+ For MIUI 14
Coconutatandroid_kernel_xiaomi_sdm845_expXiaomi Mi 8 Pro(UD)[equuleus]/Explorer Edition[ursa]
Abdul Wahid Khankernel_xiaomi_begoniaRedmi Note 8 Pro (Begonia/Begoniain)
Abdul Wahid Khankernel_xiaomi_marbleXiaomi Poco F5 / Redmi Note 12 Turbo (Marble)
zfdx123kernel_xiaomi_aliothRedmi K40 (alioth)
wxt1221android_kernel_oneplus_sdm845Oneplus 6 (enchilada) | Oneplus 6T (fajita) with no Retrofit Dynamic Partitions
zharzinhooKernel-Oriente-GuampMoto g9 play
hc841NovaCore_kernelMoto g9 play
zlm324android_kernel_xiaomi_msm8998_ksua lineage kernel forked from LineageOS official repository, version 4.4.302, added ksu.
zlm324android_kernel_xiaomi_msm8953_ksuXiaomi 5X (tiffany)
Vincent4440android_kernel_xiaomi_sm8250Poco F4: munch
zharzinhooandroid_kernel_motorola_cebuMoto g9 power
tiandoufayaleandroid_kernel_samsung_sm8250Samsung Tab S7 WIFI(T870)
starmoeandroid_kernel_oneplus_msm8998Oneplus 5/5T, kernel version 4.4.302
supechickenkernel_xiaomi_sm8350 Xiaomi 11T Pro (vili)
Vaz15kkernelSU-A70qSamsung Galaxy A70 ( A705MN / A705FN )
whyakariandroid_kernel_xiaomi_ginkgonon-GKI ginkgo
wulan17android_kernel_xiaomi_mt6765Redmi 6/6A (cereus/cactus)
officialputuidandroid_kernel_realme_mt6785Realme 6/6i(Indian)/6s/7/Narzo/Narzo 20 Pro/Narzo 30 4G (RM6785)

Lançado sob a Licença GPL3.

+ \ No newline at end of file diff --git a/pt_BR/guide/what-is-kernelsu.html b/pt_BR/guide/what-is-kernelsu.html index c4d68b3a9cb3..30babc26e526 100644 --- a/pt_BR/guide/what-is-kernelsu.html +++ b/pt_BR/guide/what-is-kernelsu.html @@ -5,18 +5,19 @@ O que é KernelSU? | KernelSU - - + + + - - - - + + + + + -
Skip to content
On this page

O que é KernelSU?

O KernelSU é uma solução root para dispositivos Android GKI, funciona no modo kernel e concede permissão root ao app do espaço do usuário diretamente no espaço do kernel.

Características

A principal característica do KernelSU é que ele é baseado em kernel. O KernelSU funciona no modo kernel, portanto pode fornecer uma interface de kernel que nunca tivemos antes. Por exemplo, podemos adicionar um ponto de interrupção de hardware a qualquer processo no modo kernel; Podemos acessar a memória física de qualquer processo sem que ninguém perceba; Podemos interceptar qualquer syscall no espaço do kernel; etc.

E também, o KernelSU fornece um sistema de módulos via overlayfs, que permite carregar seu plugin personalizado no sistema. Ele também fornece um mecanismo para modificar arquivos na partição /system.

Como usar

Por favor, consulte: Instalação

Como construir

Como construir o KernelSU?

Discussão

Lançado sob a Licença GPL3.

- +
Skip to content

O que é KernelSU?

O KernelSU é uma solução root para dispositivos Android GKI, funciona no modo kernel e concede permissão root ao app do espaço do usuário diretamente no espaço do kernel.

Características

A principal característica do KernelSU é que ele é baseado em kernel. O KernelSU funciona no modo kernel, portanto pode fornecer uma interface de kernel que nunca tivemos antes. Por exemplo, podemos adicionar um ponto de interrupção de hardware a qualquer processo no modo kernel; Podemos acessar a memória física de qualquer processo sem que ninguém perceba; Podemos interceptar qualquer syscall no espaço do kernel; etc.

E também, o KernelSU fornece um sistema de módulos via overlayfs, que permite carregar seu plugin personalizado no sistema. Ele também fornece um mecanismo para modificar arquivos na partição /system.

Como usar

Por favor, consulte: Instalação

Como construir

Como construir o KernelSU?

Discussão

Lançado sob a Licença GPL3.

+ \ No newline at end of file diff --git a/pt_BR/index.html b/pt_BR/index.html index bfbe2e281bb2..d7548be55d57 100644 --- a/pt_BR/index.html +++ b/pt_BR/index.html @@ -5,18 +5,19 @@ Uma solução root baseada em kernel para Android | KernelSU - - + + + - - - - + + + + + -
Skip to content

KernelSU

Uma solução root baseada em kernel para Android

KernelSU

Baseado em kernel

Como o nome sugere, KernelSU funciona no kernel Linux, dando-lhe mais controle sobre os apps do espaço do usuário.

Controle de acesso root

Somente apps permitidos podem acessar ou ver su, todos os outros apps não estão cientes disso.

Privilégios de root personalizáveis

KernelSU permite a personalização de uid, gid, grupos, capacidades e regras SELinux do su, bloqueando privilégios de root.

Módulo

Os módulos podem modificar /system sem sistema usando overlayfs permitindo grande potência.

Lançado sob a Licença GPL3.

- +
Skip to content

KernelSU

Uma solução root baseada em kernel para Android

KernelSU

Lançado sob a Licença GPL3.

+ \ No newline at end of file diff --git a/ru_RU/guide/app-profile.html b/ru_RU/guide/app-profile.html index 0b351e7c0438..1d02a6f842cc 100644 --- a/ru_RU/guide/app-profile.html +++ b/ru_RU/guide/app-profile.html @@ -5,22 +5,27 @@ Профиль приложений | KernelSU - - + + + - - - - + + + + + -
Skip to content
On this page

Профиль приложений

Профиль приложений - это механизм, предоставляемый KernelSU для настройки конфигурации различных приложений.

Для приложений, получивших права root (т.е. имеющих возможность использовать su), App Profile может также называться Root Profile. Он позволяет настраивать правила uid, gid, groups, capabilities и SELinux команды su, тем самым ограничивая привилегии пользователя root. Например, она может предоставлять сетевые права только приложениям межсетевого экрана, отказывая в праве доступа к файлам, или предоставлять права shell вместо полного root-доступа для приложений freeze: *сохранение власти в рамках принципа наименьших привилегий.

Для обычных приложений, не имеющих прав root, App Profile может управлять поведением ядра и системы модулей по отношению к этим приложениям. Например, он может определять, следует ли обращать внимание на модификации, возникающие в результате работы модулей. На основе этой конфигурации ядро и система модулей могут принимать решения, например, выполнять операции, аналогичные "скрытию".

Корневой профиль

UID, GID и группы

В системах Linux существуют два понятия: пользователи и группы. Каждый пользователь имеет идентификатор пользователя (UID), а пользователь может принадлежать к нескольким группам, каждая из которых имеет свой идентификатор группы (GID). Эти идентификаторы используются для идентификации пользователей в системе и определяют, к каким системным ресурсам они могут получить доступ.

Пользователи с UID, равным 0, называются корневыми пользователями, а группы с GID, равным 0, - корневыми группами. Группа пользователей root, как правило, обладает самыми высокими системными привилегиями.

В случае системы Android каждое приложение является отдельным пользователем (исключая сценарии с общим UID) с уникальным UID. Например, 0 представляет пользователя root, 1000 - system, 2000 - ADB shell, а UID в диапазоне от 10000 до 19999 - обычные приложения.

INFO

Здесь упомянутый UID не совпадает с концепцией нескольких пользователей или рабочих профилей в системе Android. На самом деле рабочие профили реализуются путем разделения диапазона UID. Например, 10000-19999 представляет собой основного пользователя, а 110000-119999 - рабочий профиль. Каждое обычное приложение среди них имеет свой уникальный UID.

Каждое приложение может иметь несколько групп, причем GID представляет собой основную группу, которая обычно совпадает с UID. Другие группы называются дополнительными. Определенные разрешения контролируются через группы, например, разрешения на доступ к сети или доступ к Bluetooth.

Например, если мы выполним команду id в оболочке ADB, то результат может выглядеть следующим образом:

sh
oriole:/ $ id
-uid=2000(shell) gid=2000(shell) groups=2000(shell),1004(input),1007(log),1011(adb),1015(sdcard_rw),1028(sdcard_r),1078(ext_data_rw),1079(ext_obb_rw),3001(net_bt_admin),3002(net_bt),3003(inet),3006(net_bw_stats),3009(readproc),3011(uhid),3012(readtracefs) context=u:r:shell:s0

Здесь UID равен 2000, а GID (идентификатор основной группы) также равен 2000. Кроме того, он входит в несколько дополнительных групп, таких как inet (указывает на возможность создания сокетов AF_INET и AF_INET6) и sdcard_rw (указывает на права чтения/записи на SD-карту).

Корневой профиль KernelSU позволяет настраивать UID, GID и группы для корневого процесса после выполнения команды su. Например, в корневом профиле корневого приложения можно установить его UID на 2000, что означает, что при использовании su фактические разрешения приложения будут находиться на уровне оболочки ADB. Группа inet может быть удалена, что не позволит команде su получить доступ к сети.

Примечание

Профиль приложений контролирует только разрешения корневого процесса после использования su; он не контролирует разрешения самого приложения. Если приложение запросило разрешение на доступ к сети, оно может получить доступ к сети даже без использования su. Удаление группы inet из su только предотвращает доступ su к сети.

Корневой профиль реализуется в ядре и не зависит от добровольного поведения root-приложений, в отличие от переключения пользователей или групп через su, предоставление прав su полностью зависит от пользователя, а не от разработчика.

Привилегии

Привилегии - это механизм разделения привилегий в Linux.

В традиционных реализациях UNIX для проверки прав доступа выделяются две категории процессов: привилегированные процессы (эффективный идентификатор пользователя равен 0 и называется суперпользователем или root) и непривилегированные процессы (эффективный UID которых не равен нулю). Привилегированные процессы обходят все проверки прав ядра, в то время как непривилегированные процессы подвергаются полной проверке прав на основе учетных данных процесса (обычно: эффективный UID, эффективный GID и список дополнительных групп).

Начиная с версии Linux 2.2, в Linux привилегии, традиционно ассоциируемые с суперпользователем, разделены на отдельные единицы, называемые возможностями, которые могут быть независимо включены и выключены.

Каждая способность представляет собой одну или несколько привилегий. Например, CAP_DAC_READ_SEARCH представляет собой возможность обхода проверок прав на чтение файлов, а также прав на чтение и выполнение каталогов. Если пользователь с эффективным UID 0 (пользователь root) не имеет возможности CAP_DAC_READ_SEARCH или более высоких возможностей, это означает, что, хотя он и является пользователем root, он не может читать файлы по своему усмотрению.

Корневой профиль KernelSU позволяет настраивать возможности корневого процесса после выполнения su, тем самым добиваясь частичного предоставления "прав root". В отличие от вышеупомянутых UID и GID, некоторые root-приложения после использования su требуют UID, равный 0. В таких случаях ограничение возможностей данного root-пользователя с UID 0 может ограничить их разрешенные операции.

Настоятельная рекомендация

В документе привелегий Linux официальной документации дается подробное объяснение возможностей, представленных каждой привелегией. Если вы собираетесь настраивать привелегии, настоятельно рекомендуется сначала прочитать этот документ.

SELinux

SELinux - это мощный механизм обязательного контроля доступа (MAC). Он работает по принципу запрет по умолчанию: любое действие, не разрешенное в явном виде, запрещается.

SELinux может работать в двух глобальных режимах:

  1. Разрешительный режим: События запрета регистрируются, но не выполняются.
  2. Принудительный режим: События запрета регистрируются и выполняются.

Предупреждение

Современные системы Android в значительной степени опираются на SELinux для обеспечения общей безопасности системы. Настоятельно не рекомендуется использовать пользовательские системы, работающие в "разрешительном режиме", поскольку это не дает существенных преимуществ перед полностью открытой системой.

Объяснение полной концепции SELinux является сложным и выходит за рамки данного документа. Рекомендуется сначала разобраться в его работе с помощью следующих ресурсов:

  1. Wikipedia
  2. Red Hat: Что такое SELinux?
  3. ArchLinux: SELinux

Корневой профиль KernelSU позволяет настраивать SELinux-контекст корневого процесса после выполнения команды su. Для этого контекста могут быть заданы специальные правила управления доступом, позволяющие осуществлять тонкий контроль над правами root.

В типичных сценариях, когда приложение выполняет команду su, оно переключает процесс на домен SELinux с неограниченным доступом, например u:r:su:s0. С помощью профиля Root Profile этот домен может быть переключен на пользовательский домен, например u:r:app1:s0, и для него может быть определен ряд правил:

sh
type app1
-enforce app1
-typeattribute app1 mlstrustedsubject
-allow app1 * * *

Обратите внимание, что правило allow app1 * * * используется только в демонстрационных целях. На практике это правило не должно широко использоваться, поскольку оно мало чем отличается от разрешительного режима.

Эскалация

При неправильной настройке корневого профиля может возникнуть сценарий эскалации: ограничения, накладываемые корневым профилем, могут непреднамеренно не сработать.

Например, если предоставить права root пользователю ADB shell (что является обычным случаем), а затем предоставить права root обычному приложению, но настроить его профиль root с UID 2000 (это UID пользователя ADB shell), то приложение может получить полный доступ root, выполнив команду su дважды:

  1. При первом выполнении команды su будет применен профиль App Profile и произойдет переход на UID 2000 (adb shell) вместо 0 (root).
  2. При втором выполнении команды su, поскольку UID равен 2000, а в конфигурации вы предоставили доступ root к UID 2000 (adb shell), приложение получит полные привилегии root.

Примечание

Такое поведение вполне ожидаемо и не является ошибкой. Поэтому мы рекомендуем следующее:

Если вам действительно необходимо предоставить права root в ADB (например, как разработчику), не рекомендуется изменять UID на 2000 при настройке корневого профиля. Лучше использовать 1000 (система).

Некорневой профиль

Размонтирование модулей

KernelSU предоставляет бессистемный механизм модификации системных разделов, реализуемый через монтирование overlayfs. Однако некоторые приложения могут быть чувствительны к такому поведению. Поэтому мы можем выгрузить модули, смонтированные в этих приложениях, установив опцию "размонтирование модулей".

Кроме того, в интерфейсе настроек менеджера KernelSU имеется переключатель "размонтирование модулей по умолчанию". По умолчанию этот переключатель включен, что означает, что KernelSU или некоторые модули будут выгружать модули для данного приложения, если не будут применены дополнительные настройки. Если вам не нравится эта настройка или если она влияет на определенные приложения, у вас есть следующие возможности:

  1. Оставить переключатель "размонтирование модулей по умолчанию" и индивидуально отключить опцию "размонтирование модулей" в профиле приложений для приложений, требующих загрузки модулей (действует как "белый список").
  2. Отключить переключатель "размонтирование модулей по умолчанию" и индивидуально включить опцию "размонтирование модулей" в App Profile для приложений, требующих выгрузки модулей (действует как "черный список").

INFO

В устройствах, использующих ядро версии 5.10 и выше, выгрузку модулей выполняет само ядро. Однако для устройств с ядром версии ниже 5.10 этот переключатель является лишь опцией конфигурации, и KernelSU сам по себе не предпринимает никаких действий. Некоторые модули, например, Zygisksu, могут использовать этот переключатель для определения необходимости выгрузки модулей.

Выпускается под лицензией GPL3.

- +
Skip to content

Профиль приложений

Профиль приложений - это механизм, предоставляемый KernelSU для настройки конфигурации различных приложений.

Для приложений, получивших права root (т.е. имеющих возможность использовать su), App Profile может также называться Root Profile. Он позволяет настраивать правила uid, gid, groups, capabilities и SELinux команды su, тем самым ограничивая привилегии пользователя root. Например, она может предоставлять сетевые права только приложениям межсетевого экрана, отказывая в праве доступа к файлам, или предоставлять права shell вместо полного root-доступа для приложений freeze: *сохранение власти в рамках принципа наименьших привилегий.

Для обычных приложений, не имеющих прав root, App Profile может управлять поведением ядра и системы модулей по отношению к этим приложениям. Например, он может определять, следует ли обращать внимание на модификации, возникающие в результате работы модулей. На основе этой конфигурации ядро и система модулей могут принимать решения, например, выполнять операции, аналогичные "скрытию".

Корневой профиль

UID, GID и группы

В системах Linux существуют два понятия: пользователи и группы. Каждый пользователь имеет идентификатор пользователя (UID), а пользователь может принадлежать к нескольким группам, каждая из которых имеет свой идентификатор группы (GID). Эти идентификаторы используются для идентификации пользователей в системе и определяют, к каким системным ресурсам они могут получить доступ.

Пользователи с UID, равным 0, называются корневыми пользователями, а группы с GID, равным 0, - корневыми группами. Группа пользователей root, как правило, обладает самыми высокими системными привилегиями.

В случае системы Android каждое приложение является отдельным пользователем (исключая сценарии с общим UID) с уникальным UID. Например, 0 представляет пользователя root, 1000 - system, 2000 - ADB shell, а UID в диапазоне от 10000 до 19999 - обычные приложения.

INFO

Здесь упомянутый UID не совпадает с концепцией нескольких пользователей или рабочих профилей в системе Android. На самом деле рабочие профили реализуются путем разделения диапазона UID. Например, 10000-19999 представляет собой основного пользователя, а 110000-119999 - рабочий профиль. Каждое обычное приложение среди них имеет свой уникальный UID.

Каждое приложение может иметь несколько групп, причем GID представляет собой основную группу, которая обычно совпадает с UID. Другие группы называются дополнительными. Определенные разрешения контролируются через группы, например, разрешения на доступ к сети или доступ к Bluetooth.

Например, если мы выполним команду id в оболочке ADB, то результат может выглядеть следующим образом:

sh
oriole:/ $ id
+uid=2000(shell) gid=2000(shell) groups=2000(shell),1004(input),1007(log),1011(adb),1015(sdcard_rw),1028(sdcard_r),1078(ext_data_rw),1079(ext_obb_rw),3001(net_bt_admin),3002(net_bt),3003(inet),3006(net_bw_stats),3009(readproc),3011(uhid),3012(readtracefs) context=u:r:shell:s0
oriole:/ $ id
+uid=2000(shell) gid=2000(shell) groups=2000(shell),1004(input),1007(log),1011(adb),1015(sdcard_rw),1028(sdcard_r),1078(ext_data_rw),1079(ext_obb_rw),3001(net_bt_admin),3002(net_bt),3003(inet),3006(net_bw_stats),3009(readproc),3011(uhid),3012(readtracefs) context=u:r:shell:s0

Здесь UID равен 2000, а GID (идентификатор основной группы) также равен 2000. Кроме того, он входит в несколько дополнительных групп, таких как inet (указывает на возможность создания сокетов AF_INET и AF_INET6) и sdcard_rw (указывает на права чтения/записи на SD-карту).

Корневой профиль KernelSU позволяет настраивать UID, GID и группы для корневого процесса после выполнения команды su. Например, в корневом профиле корневого приложения можно установить его UID на 2000, что означает, что при использовании su фактические разрешения приложения будут находиться на уровне оболочки ADB. Группа inet может быть удалена, что не позволит команде su получить доступ к сети.

Примечание

Профиль приложений контролирует только разрешения корневого процесса после использования su; он не контролирует разрешения самого приложения. Если приложение запросило разрешение на доступ к сети, оно может получить доступ к сети даже без использования su. Удаление группы inet из su только предотвращает доступ su к сети.

Корневой профиль реализуется в ядре и не зависит от добровольного поведения root-приложений, в отличие от переключения пользователей или групп через su, предоставление прав su полностью зависит от пользователя, а не от разработчика.

Привилегии

Привилегии - это механизм разделения привилегий в Linux.

В традиционных реализациях UNIX для проверки прав доступа выделяются две категории процессов: привилегированные процессы (эффективный идентификатор пользователя равен 0 и называется суперпользователем или root) и непривилегированные процессы (эффективный UID которых не равен нулю). Привилегированные процессы обходят все проверки прав ядра, в то время как непривилегированные процессы подвергаются полной проверке прав на основе учетных данных процесса (обычно: эффективный UID, эффективный GID и список дополнительных групп).

Начиная с версии Linux 2.2, в Linux привилегии, традиционно ассоциируемые с суперпользователем, разделены на отдельные единицы, называемые возможностями, которые могут быть независимо включены и выключены.

Каждая способность представляет собой одну или несколько привилегий. Например, CAP_DAC_READ_SEARCH представляет собой возможность обхода проверок прав на чтение файлов, а также прав на чтение и выполнение каталогов. Если пользователь с эффективным UID 0 (пользователь root) не имеет возможности CAP_DAC_READ_SEARCH или более высоких возможностей, это означает, что, хотя он и является пользователем root, он не может читать файлы по своему усмотрению.

Корневой профиль KernelSU позволяет настраивать возможности корневого процесса после выполнения su, тем самым добиваясь частичного предоставления "прав root". В отличие от вышеупомянутых UID и GID, некоторые root-приложения после использования su требуют UID, равный 0. В таких случаях ограничение возможностей данного root-пользователя с UID 0 может ограничить их разрешенные операции.

Настоятельная рекомендация

В документе привелегий Linux официальной документации дается подробное объяснение возможностей, представленных каждой привелегией. Если вы собираетесь настраивать привелегии, настоятельно рекомендуется сначала прочитать этот документ.

SELinux

SELinux - это мощный механизм обязательного контроля доступа (MAC). Он работает по принципу запрет по умолчанию: любое действие, не разрешенное в явном виде, запрещается.

SELinux может работать в двух глобальных режимах:

  1. Разрешительный режим: События запрета регистрируются, но не выполняются.
  2. Принудительный режим: События запрета регистрируются и выполняются.

Предупреждение

Современные системы Android в значительной степени опираются на SELinux для обеспечения общей безопасности системы. Настоятельно не рекомендуется использовать пользовательские системы, работающие в "разрешительном режиме", поскольку это не дает существенных преимуществ перед полностью открытой системой.

Объяснение полной концепции SELinux является сложным и выходит за рамки данного документа. Рекомендуется сначала разобраться в его работе с помощью следующих ресурсов:

  1. Wikipedia
  2. Red Hat: Что такое SELinux?
  3. ArchLinux: SELinux

Корневой профиль KernelSU позволяет настраивать SELinux-контекст корневого процесса после выполнения команды su. Для этого контекста могут быть заданы специальные правила управления доступом, позволяющие осуществлять тонкий контроль над правами root.

В типичных сценариях, когда приложение выполняет команду su, оно переключает процесс на домен SELinux с неограниченным доступом, например u:r:su:s0. С помощью профиля Root Profile этот домен может быть переключен на пользовательский домен, например u:r:app1:s0, и для него может быть определен ряд правил:

sh
type app1
+enforce app1
+typeattribute app1 mlstrustedsubject
+allow app1 * * *
type app1
+enforce app1
+typeattribute app1 mlstrustedsubject
+allow app1 * * *

Обратите внимание, что правило allow app1 * * * используется только в демонстрационных целях. На практике это правило не должно широко использоваться, поскольку оно мало чем отличается от разрешительного режима.

Эскалация

При неправильной настройке корневого профиля может возникнуть сценарий эскалации: ограничения, накладываемые корневым профилем, могут непреднамеренно не сработать.

Например, если предоставить права root пользователю ADB shell (что является обычным случаем), а затем предоставить права root обычному приложению, но настроить его профиль root с UID 2000 (это UID пользователя ADB shell), то приложение может получить полный доступ root, выполнив команду su дважды:

  1. При первом выполнении команды su будет применен профиль App Profile и произойдет переход на UID 2000 (adb shell) вместо 0 (root).
  2. При втором выполнении команды su, поскольку UID равен 2000, а в конфигурации вы предоставили доступ root к UID 2000 (adb shell), приложение получит полные привилегии root.

Примечание

Такое поведение вполне ожидаемо и не является ошибкой. Поэтому мы рекомендуем следующее:

Если вам действительно необходимо предоставить права root в ADB (например, как разработчику), не рекомендуется изменять UID на 2000 при настройке корневого профиля. Лучше использовать 1000 (система).

Некорневой профиль

Размонтирование модулей

KernelSU предоставляет бессистемный механизм модификации системных разделов, реализуемый через монтирование overlayfs. Однако некоторые приложения могут быть чувствительны к такому поведению. Поэтому мы можем выгрузить модули, смонтированные в этих приложениях, установив опцию "размонтирование модулей".

Кроме того, в интерфейсе настроек менеджера KernelSU имеется переключатель "размонтирование модулей по умолчанию". По умолчанию этот переключатель включен, что означает, что KernelSU или некоторые модули будут выгружать модули для данного приложения, если не будут применены дополнительные настройки. Если вам не нравится эта настройка или если она влияет на определенные приложения, у вас есть следующие возможности:

  1. Оставить переключатель "размонтирование модулей по умолчанию" и индивидуально отключить опцию "размонтирование модулей" в профиле приложений для приложений, требующих загрузки модулей (действует как "белый список").
  2. Отключить переключатель "размонтирование модулей по умолчанию" и индивидуально включить опцию "размонтирование модулей" в App Profile для приложений, требующих выгрузки модулей (действует как "черный список").

INFO

В устройствах, использующих ядро версии 5.10 и выше, выгрузку модулей выполняет само ядро. Однако для устройств с ядром версии ниже 5.10 этот переключатель является лишь опцией конфигурации, и KernelSU сам по себе не предпринимает никаких действий. Некоторые модули, например, Zygisksu, могут использовать этот переключатель для определения необходимости выгрузки модулей.

Выпускается под лицензией GPL3.

+ \ No newline at end of file diff --git a/ru_RU/guide/difference-with-magisk.html b/ru_RU/guide/difference-with-magisk.html index 5c180d8feea4..0263e3dedf2e 100644 --- a/ru_RU/guide/difference-with-magisk.html +++ b/ru_RU/guide/difference-with-magisk.html @@ -5,18 +5,19 @@ Различия с Magisk | KernelSU - - + + + - - - - + + + + + -
Skip to content
On this page

Различия с Magisk

Несмотря на большое количество сходств между модулями KernelSU и модулями Magisk, неизбежно возникают и различия, обусловленные совершенно разными механизмами их реализации. Если вы хотите, чтобы ваш модуль работал как на Magisk, так и на KernelSU, вы должны понимать эти различия.

Сходства

  • Формат файлов модулей: оба используют формат zip для организации модулей, и формат модулей практически одинаков
  • Каталог установки модулей: оба расположены в /data/adb/modules.
  • Бессистемность: оба поддерживают модификацию /system бессистемным способом через модули
  • post-fs-data.sh: время выполнения и семантика полностью совпадают
  • service.sh: время выполнения и семантика полностью совпадают
  • system.prop: полностью совпадает
  • sepolicy.rule: полностью совпадает
  • BusyBox: скрипты запускаются в BusyBox с включенным "автономным режимом" в обоих случаях

Различия

Прежде чем разбираться в различиях, необходимо знать, как отличить, в каком режиме работает ваш модуль - KernelSU или Magisk. Для этого можно использовать переменную окружения KSU во всех местах, где можно запустить скрипты модуля (customize.sh, post-fs-data.sh, service.sh). В KernelSU эта переменная окружения будет установлена в значение true.

Вот некоторые отличия:

  • Модули KernelSU не могут быть установлены в режиме Recovery.
  • Модули KernelSU не имеют встроенной поддержки Zygisk (но вы можете использовать модули Zygisk через ZygiskNext.
  • Метод замены или удаления файлов в модулях KernelSU полностью отличается от Magisk. KernelSU не поддерживает метод .replace. Вместо этого необходимо создать одноименный файл с помощью команды mknod filename c 0 0 для удаления соответствующего файла.
  • Каталоги для BusyBox отличаются. Встроенный BusyBox в KernelSU находится в каталоге /data/adb/ksu/bin/busybox, а в Magisk - в каталоге /data/adb/magisk/busybox. Обратите внимание, что это внутреннее поведение KernelSU и в будущем оно может измениться!
  • KernelSU не поддерживает файлы .replace; однако KernelSU поддерживает переменные REMOVE и REPLACE для удаления или замены файлов и папок.

Выпускается под лицензией GPL3.

- +
Skip to content

Различия с Magisk

Несмотря на большое количество сходств между модулями KernelSU и модулями Magisk, неизбежно возникают и различия, обусловленные совершенно разными механизмами их реализации. Если вы хотите, чтобы ваш модуль работал как на Magisk, так и на KernelSU, вы должны понимать эти различия.

Сходства

  • Формат файлов модулей: оба используют формат zip для организации модулей, и формат модулей практически одинаков
  • Каталог установки модулей: оба расположены в /data/adb/modules.
  • Бессистемность: оба поддерживают модификацию /system бессистемным способом через модули
  • post-fs-data.sh: время выполнения и семантика полностью совпадают
  • service.sh: время выполнения и семантика полностью совпадают
  • system.prop: полностью совпадает
  • sepolicy.rule: полностью совпадает
  • BusyBox: скрипты запускаются в BusyBox с включенным "автономным режимом" в обоих случаях

Различия

Прежде чем разбираться в различиях, необходимо знать, как отличить, в каком режиме работает ваш модуль - KernelSU или Magisk. Для этого можно использовать переменную окружения KSU во всех местах, где можно запустить скрипты модуля (customize.sh, post-fs-data.sh, service.sh). В KernelSU эта переменная окружения будет установлена в значение true.

Вот некоторые отличия:

  • Модули KernelSU не могут быть установлены в режиме Recovery.
  • Модули KernelSU не имеют встроенной поддержки Zygisk (но вы можете использовать модули Zygisk через ZygiskNext.
  • Метод замены или удаления файлов в модулях KernelSU полностью отличается от Magisk. KernelSU не поддерживает метод .replace. Вместо этого необходимо создать одноименный файл с помощью команды mknod filename c 0 0 для удаления соответствующего файла.
  • Каталоги для BusyBox отличаются. Встроенный BusyBox в KernelSU находится в каталоге /data/adb/ksu/bin/busybox, а в Magisk - в каталоге /data/adb/magisk/busybox. Обратите внимание, что это внутреннее поведение KernelSU и в будущем оно может измениться!
  • KernelSU не поддерживает файлы .replace; однако KernelSU поддерживает переменные REMOVE и REPLACE для удаления или замены файлов и папок.

Выпускается под лицензией GPL3.

+ \ No newline at end of file diff --git a/ru_RU/guide/faq.html b/ru_RU/guide/faq.html index 29f2a425d7c3..7b5ed320bfee 100644 --- a/ru_RU/guide/faq.html +++ b/ru_RU/guide/faq.html @@ -5,18 +5,19 @@ FAQ | KernelSU - - + + + - - - - + + + + + -
Skip to content
On this page

FAQ

Поддерживает ли KernelSU мое устройство?

Во-первых, ваше устройство должно быть способно разблокировать загрузчик. Если не может, значит, устройство не поддерживается.

Затем установите на устройство приложение KernelSU manager App и откройте его, если оно покажет Unsupported, то ваше устройство не поддерживается из коробки, но вы можете собрать исходный код ядра и интегрировать KernelSU, чтобы заставить его работать, или использовать неофициально-поддерживаемые-устройства.

Нужно ли для KernelSU разблокировать загрузчик?

Безусловно, да.

Поддерживает ли KernelSU модули?

Да, но это ранняя версия, она может быть глючной. Пожалуйста, подождите, пока она станет стабильной 😃

Поддерживает ли KernelSU Xposed?

Да, Dreamland и TaiChi работают. Что касается LSPosed, то его можно заставить работать с помощью Zygisk на KernelSU

Поддерживает ли KernelSU Zygisk?

KernelSU не имеет встроенной поддержки Zygisk, но вы можете использовать Zygisk на KernelSU.

Совместим ли KernelSU с Magisk?

Система модулей KernelSU конфликтует с магическим монтированием Magisk, если в KernelSU включен какой-либо модуль, то весь Magisk не будет работать.

Но если вы используете только su из KernelSU, то он будет хорошо работать с Magisk: KernelSU модифицирует kernel, а Magisk - ramdisk, они могут работать вместе.

Заменит ли KernelSU Magisk?

Мы так не считаем, и это не является нашей целью. Magisk достаточно хорош для решения проблемы root в пользовательском пространстве и будет жить долго. Цель KernelSU - предоставить пользователям интерфейс ядра, а не заменить Magisk.

Может ли KernelSU поддерживать устройства, не относящиеся к GKI?

Это возможно. Но для этого необходимо скачать исходный текст ядра, подключить KernelSU к дереву исходных текстов и скомпилировать ядро самостоятельно.

Может ли KernelSU поддерживать устройства под управлением Android 12?

На совместимость KernelSU влияет ядро устройства, и версия Android здесь ни при чем. Единственное ограничение - устройства, запускаемые с Android 12, должны иметь ядро 5.10+ (устройства GKI). Итак:

  1. Устройства, выпущенные под управлением Android 12, должны поддерживаться.
  2. Устройства со старым ядром (некоторые устройства с Android 12 также имеют старое ядро) совместимы (Вы должны собрать ядро самостоятельно).

Может ли KernelSU поддерживать старое ядро?

Это возможно, KernelSU бэкпортирован на ядро 4.14, для более старых ядер, вам нужно сделать бэкпорт вручную, и PR приветствуются!

Как интегрировать KernelSU в старое ядро?

Пожалуйста, обратитесь к руководству

Почему моя версия Android - 13, а ядро показывает "android12-5.10"?

Версия ядра не имеет никакого отношения к версии Android, если вам нужно прошить ядро, всегда используйте версию ядра, версия Android не так важна.

Есть ли в KernelSU пространство имен --mount-master/global mount?

Сейчас нет (возможно, в будущем), но есть много способов переключиться на глобальное пространство имен монтирования вручную, например:

  1. nsenter -t 1 -m sh для получения оболочки в глобальном пространстве имен монтирования.
  2. Добавить nsenter --mount=/proc/1/ns/mnt к команде, которую вы хотите выполнить, тогда команда будет выполнена в глобальном пространстве имен монтирования. KernelSU также использует этот способ

Я GKI1.0, могу ли я использовать это?

GKI1 полностью отличается от GKI2, вы должны скомпилировать ядро самостоятельно.

Выпускается под лицензией GPL3.

- +
Skip to content

FAQ

Поддерживает ли KernelSU мое устройство?

Во-первых, ваше устройство должно быть способно разблокировать загрузчик. Если не может, значит, устройство не поддерживается.

Затем установите на устройство приложение KernelSU manager App и откройте его, если оно покажет Unsupported, то ваше устройство не поддерживается из коробки, но вы можете собрать исходный код ядра и интегрировать KernelSU, чтобы заставить его работать, или использовать неофициально-поддерживаемые-устройства.

Нужно ли для KernelSU разблокировать загрузчик?

Безусловно, да.

Поддерживает ли KernelSU модули?

Да, но это ранняя версия, она может быть глючной. Пожалуйста, подождите, пока она станет стабильной 😃

Поддерживает ли KernelSU Xposed?

Да, Dreamland и TaiChi работают. Что касается LSPosed, то его можно заставить работать с помощью Zygisk на KernelSU

Поддерживает ли KernelSU Zygisk?

KernelSU не имеет встроенной поддержки Zygisk, но вы можете использовать Zygisk на KernelSU.

Совместим ли KernelSU с Magisk?

Система модулей KernelSU конфликтует с магическим монтированием Magisk, если в KernelSU включен какой-либо модуль, то весь Magisk не будет работать.

Но если вы используете только su из KernelSU, то он будет хорошо работать с Magisk: KernelSU модифицирует kernel, а Magisk - ramdisk, они могут работать вместе.

Заменит ли KernelSU Magisk?

Мы так не считаем, и это не является нашей целью. Magisk достаточно хорош для решения проблемы root в пользовательском пространстве и будет жить долго. Цель KernelSU - предоставить пользователям интерфейс ядра, а не заменить Magisk.

Может ли KernelSU поддерживать устройства, не относящиеся к GKI?

Это возможно. Но для этого необходимо скачать исходный текст ядра, подключить KernelSU к дереву исходных текстов и скомпилировать ядро самостоятельно.

Может ли KernelSU поддерживать устройства под управлением Android 12?

На совместимость KernelSU влияет ядро устройства, и версия Android здесь ни при чем. Единственное ограничение - устройства, запускаемые с Android 12, должны иметь ядро 5.10+ (устройства GKI). Итак:

  1. Устройства, выпущенные под управлением Android 12, должны поддерживаться.
  2. Устройства со старым ядром (некоторые устройства с Android 12 также имеют старое ядро) совместимы (Вы должны собрать ядро самостоятельно).

Может ли KernelSU поддерживать старое ядро?

Это возможно, KernelSU бэкпортирован на ядро 4.14, для более старых ядер, вам нужно сделать бэкпорт вручную, и PR приветствуются!

Как интегрировать KernelSU в старое ядро?

Пожалуйста, обратитесь к руководству

Почему моя версия Android - 13, а ядро показывает "android12-5.10"?

Версия ядра не имеет никакого отношения к версии Android, если вам нужно прошить ядро, всегда используйте версию ядра, версия Android не так важна.

Есть ли в KernelSU пространство имен --mount-master/global mount?

Сейчас нет (возможно, в будущем), но есть много способов переключиться на глобальное пространство имен монтирования вручную, например:

  1. nsenter -t 1 -m sh для получения оболочки в глобальном пространстве имен монтирования.
  2. Добавить nsenter --mount=/proc/1/ns/mnt к команде, которую вы хотите выполнить, тогда команда будет выполнена в глобальном пространстве имен монтирования. KernelSU также использует этот способ

Я GKI1.0, могу ли я использовать это?

GKI1 полностью отличается от GKI2, вы должны скомпилировать ядро самостоятельно.

Выпускается под лицензией GPL3.

+ \ No newline at end of file diff --git a/ru_RU/guide/hidden-features.html b/ru_RU/guide/hidden-features.html index 27669ecef32f..4375fccda4c6 100644 --- a/ru_RU/guide/hidden-features.html +++ b/ru_RU/guide/hidden-features.html @@ -5,18 +5,19 @@ Скрытые возможности | KernelSU - - + + + - - - - + + + + + -
Skip to content
On this page

Скрытые возможности

.ksurc

По умолчанию /system/bin/sh загружает /system/etc/mkshrc.

Вы можете заставить su загружать пользовательский rc-файл, создав файл /data/adb/ksu/.ksurc.

Выпускается под лицензией GPL3.

- +
Skip to content

Скрытые возможности

.ksurc

По умолчанию /system/bin/sh загружает /system/etc/mkshrc.

Вы можете заставить su загружать пользовательский rc-файл, создав файл /data/adb/ksu/.ksurc.

Выпускается под лицензией GPL3.

+ \ No newline at end of file diff --git a/ru_RU/guide/how-to-build.html b/ru_RU/guide/how-to-build.html index 1ad00d84148a..fdb43c6e6ff4 100644 --- a/ru_RU/guide/how-to-build.html +++ b/ru_RU/guide/how-to-build.html @@ -5,21 +5,25 @@ Как собрать KernelSU? | KernelSU - - + + + - - - - + + + + + -
Skip to content
On this page

Как собрать KernelSU?

Прежде всего, необходимо ознакомиться с официальной документацией Android по сборке ядра:

  1. Сборка ядер
  2. Сборки релизов GKI

WARNING

Эта страница предназначена для устройств GKI, если вы используете старое ядро, пожалуйста, обратитесь к Как интегрировать KernelSU для не GKI ядер?.

Сборка ядра

Синхронизация исходного кода ядра

sh
repo init -u https://android.googlesource.com/kernel/manifest
-mv <kernel_manifest.xml> .repo/manifests
-repo init -m manifest.xml
-repo sync

Файл <kernel_manifest.xml> - это файл манифеста, который может однозначно определять сборку, с его помощью можно выполнить пересборку. Файл манифеста следует загрузить с сайта Сборки релизов Google GKI

Построение

Пожалуйста, сначала ознакомьтесь с официальной документацией.

Например, нам необходимо собрать образ ядра aarch64:

sh
LTO=thin BUILD_CONFIG=common/build.config.gki.aarch64 build/build.sh

Не забудьте добавить флаг LTO=thin, иначе сборка может завершиться неудачей, если память вашего компьютера меньше 24 Гб.

Начиная с Android 13, сборка ядра осуществляется с помощью bazel:

sh
tools/bazel build --config=fast //common:kernel_aarch64_dist

Сборка ядра с помощью KernelSU

Если вы успешно собрали ядро, то собрать KernelSU очень просто, выберите любой запуск в корневом каталоге исходного кода ядра:

  • Последний тэг(стабильный)
sh
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -
  • Основная ветвь(разработка)
sh
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -s main
  • Выбранный тэг(Например, версия v0.5.2)
sh
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -s v0.5.2

А затем пересоберите ядро и получите образ ядра с KernelSU!

Выпускается под лицензией GPL3.

- +
Skip to content

Как собрать KernelSU?

Прежде всего, необходимо ознакомиться с официальной документацией Android по сборке ядра:

  1. Сборка ядер
  2. Сборки релизов GKI

WARNING

Эта страница предназначена для устройств GKI, если вы используете старое ядро, пожалуйста, обратитесь к Как интегрировать KernelSU для не GKI ядер?.

Сборка ядра

Синхронизация исходного кода ядра

sh
repo init -u https://android.googlesource.com/kernel/manifest
+mv <kernel_manifest.xml> .repo/manifests
+repo init -m manifest.xml
+repo sync
repo init -u https://android.googlesource.com/kernel/manifest
+mv <kernel_manifest.xml> .repo/manifests
+repo init -m manifest.xml
+repo sync

Файл <kernel_manifest.xml> - это файл манифеста, который может однозначно определять сборку, с его помощью можно выполнить пересборку. Файл манифеста следует загрузить с сайта Сборки релизов Google GKI

Построение

Пожалуйста, сначала ознакомьтесь с официальной документацией.

Например, нам необходимо собрать образ ядра aarch64:

sh
LTO=thin BUILD_CONFIG=common/build.config.gki.aarch64 build/build.sh
LTO=thin BUILD_CONFIG=common/build.config.gki.aarch64 build/build.sh

Не забудьте добавить флаг LTO=thin, иначе сборка может завершиться неудачей, если память вашего компьютера меньше 24 Гб.

Начиная с Android 13, сборка ядра осуществляется с помощью bazel:

sh
tools/bazel build --config=fast //common:kernel_aarch64_dist
tools/bazel build --config=fast //common:kernel_aarch64_dist

Сборка ядра с помощью KernelSU

Если вы успешно собрали ядро, то собрать KernelSU очень просто, выберите любой запуск в корневом каталоге исходного кода ядра:

  • Последний тэг(стабильный)
sh
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -
  • Основная ветвь(разработка)
sh
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -s main
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -s main
  • Выбранный тэг(Например, версия v0.5.2)
sh
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -s v0.5.2
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -s v0.5.2

А затем пересоберите ядро и получите образ ядра с KernelSU!

Выпускается под лицензией GPL3.

+ \ No newline at end of file diff --git a/ru_RU/guide/how-to-integrate-for-non-gki.html b/ru_RU/guide/how-to-integrate-for-non-gki.html index 90fab7439d7a..36b852ad8664 100644 --- a/ru_RU/guide/how-to-integrate-for-non-gki.html +++ b/ru_RU/guide/how-to-integrate-for-non-gki.html @@ -5,169 +5,321 @@ Как интегрировать KernelSU для не GKI ядер? | KernelSU - - + + + - - - - + + + + + -
Skip to content
On this page

Как интегрировать KernelSU для не GKI ядер?

KernelSU может быть интегрирован в ядра, отличные от GKI, и был перенесен на версии 4.14 и ниже.

В связи с фрагментацией ядер, отличных от GKI, у нас нет единого способа их сборки, поэтому мы не можем предоставить загрузочные образы, отличные от GKI. Однако вы можете собрать ядро самостоятельно с помощью интегрированной программы KernelSU.

Во-первых, вы должны уметь собирать загрузочное ядро из исходных текстов ядра. Если ядро не является открытым, то запустить KernelSU для вашего устройства будет затруднительно.

Если вы можете собрать загрузочное ядро, то существует два способа интеграции KernelSU в исходный код ядра:

  1. Автоматически с помощью kprobe
  2. Вручную

Интеграция с kprobe

KernelSU использует kprobe для выполнения хуков ядра, если kprobe хорошо работает в вашем ядре, то рекомендуется использовать именно этот способ.

Сначала добавьте KernelSU в дерево исходных текстов ядра:

sh
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -

Затем необходимо проверить, включена ли функция kprobe в конфигурации ядра, если нет, то добавьте в нее эти настройки:

CONFIG_KPROBES=y
-CONFIG_HAVE_KPROBES=y
-CONFIG_KPROBE_EVENTS=y

И снова соберите ядро, KernelSU должен работать нормально.

Если вы обнаружите, что KPROBES по-прежнему не активирован, попробуйте включить CONFIG_MODULES. (Если это все равно не даст результата, используйте make menuconfig для поиска других зависимостей KPROBES).

Если же при интеграции KernelSU возникает зацикливание загрузки, то, возможно, в вашем ядре kprobe неисправен, следует исправить ошибку kprobe или воспользоваться вторым способом.

Как проверить, не сломан ли kprobe?

закомментируйте ksu_enable_sucompat() и ksu_enable_ksud() в файле KernelSU/kernel/ksu.c, если устройство загружается нормально, то может быть нарушена работа kprobe.

Ручная модификация исходного кода ядра

Если kprobe не работает в вашем ядре (возможно, это ошибка апстрима или ядра ниже 4.8), то можно попробовать следующий способ:

Сначала добавьте Kernel SU в дерево исходного кода вашего ядра:

  • Последний тэг(стабильный)
sh
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -
  • Основная ветвь(разработка)
sh
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -s main
  • Выбранный тэг(Например, версия v0.5.2)
sh
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -s v0.5.2

Затем добавьте вызовы KernelSU в исходный код ядра, вот патч, на который можно сослаться:

diff
diff --git a/fs/exec.c b/fs/exec.c
-index ac59664eaecf..bdd585e1d2cc 100644
---- a/fs/exec.c
-+++ b/fs/exec.c
-@@ -1890,11 +1890,14 @@ static int __do_execve_file(int fd, struct filename *filename,
- 	return retval;
- }
- 
-+extern bool ksu_execveat_hook __read_mostly;
-+extern int ksu_handle_execveat(int *fd, struct filename **filename_ptr, void *argv,
-+			void *envp, int *flags);
-+extern int ksu_handle_execveat_sucompat(int *fd, struct filename **filename_ptr,
-+				 void *argv, void *envp, int *flags);
- static int do_execveat_common(int fd, struct filename *filename,
- 			      struct user_arg_ptr argv,
- 			      struct user_arg_ptr envp,
- 			      int flags)
- {
-+	if (unlikely(ksu_execveat_hook))
-+		ksu_handle_execveat(&fd, &filename, &argv, &envp, &flags);
-+	else
-+		ksu_handle_execveat_sucompat(&fd, &filename, &argv, &envp, &flags);
- 	return __do_execve_file(fd, filename, argv, envp, flags, NULL);
- }
- 
-diff --git a/fs/open.c b/fs/open.c
-index 05036d819197..965b84d486b8 100644
---- a/fs/open.c
-+++ b/fs/open.c
-@@ -348,6 +348,8 @@ SYSCALL_DEFINE4(fallocate, int, fd, int, mode, loff_t, offset, loff_t, len)
- 	return ksys_fallocate(fd, mode, offset, len);
- }
- 
-+extern int ksu_handle_faccessat(int *dfd, const char __user **filename_user, int *mode,
-+			 int *flags);
- /*
-  * access() needs to use the real uid/gid, not the effective uid/gid.
-  * We do this by temporarily clearing all FS-related capabilities and
-@@ -355,6 +357,7 @@ SYSCALL_DEFINE4(fallocate, int, fd, int, mode, loff_t, offset, loff_t, len)
-  */
- long do_faccessat(int dfd, const char __user *filename, int mode)
- {
-+	ksu_handle_faccessat(&dfd, &filename, &mode, NULL);
- 	const struct cred *old_cred;
- 	struct cred *override_cred;
- 	struct path path;
-diff --git a/fs/read_write.c b/fs/read_write.c
-index 650fc7e0f3a6..55be193913b6 100644
---- a/fs/read_write.c
-+++ b/fs/read_write.c
-@@ -434,10 +434,14 @@ ssize_t kernel_read(struct file *file, void *buf, size_t count, loff_t *pos)
- }
- EXPORT_SYMBOL(kernel_read);
- 
-+extern bool ksu_vfs_read_hook __read_mostly;
-+extern int ksu_handle_vfs_read(struct file **file_ptr, char __user **buf_ptr,
-+			size_t *count_ptr, loff_t **pos);
- ssize_t vfs_read(struct file *file, char __user *buf, size_t count, loff_t *pos)
- {
- 	ssize_t ret;
- 
-+	if (unlikely(ksu_vfs_read_hook))
-+		ksu_handle_vfs_read(&file, &buf, &count, &pos);
-+
- 	if (!(file->f_mode & FMODE_READ))
- 		return -EBADF;
- 	if (!(file->f_mode & FMODE_CAN_READ))
-diff --git a/fs/stat.c b/fs/stat.c
-index 376543199b5a..82adcef03ecc 100644
---- a/fs/stat.c
-+++ b/fs/stat.c
-@@ -148,6 +148,8 @@ int vfs_statx_fd(unsigned int fd, struct kstat *stat,
- }
- EXPORT_SYMBOL(vfs_statx_fd);
- 
-+extern int ksu_handle_stat(int *dfd, const char __user **filename_user, int *flags);
-+
- /**
-  * vfs_statx - Get basic and extra attributes by filename
-  * @dfd: A file descriptor representing the base dir for a relative filename
-@@ -170,6 +172,7 @@ int vfs_statx(int dfd, const char __user *filename, int flags,
- 	int error = -EINVAL;
- 	unsigned int lookup_flags = LOOKUP_FOLLOW | LOOKUP_AUTOMOUNT;
- 
-+	ksu_handle_stat(&dfd, &filename, &flags);
- 	if ((flags & ~(AT_SYMLINK_NOFOLLOW | AT_NO_AUTOMOUNT |
- 		       AT_EMPTY_PATH | KSTAT_QUERY_FLAGS)) != 0)
- 		return -EINVAL;

В исходных кодах ядра можно найти эти четыре функции:

  1. do_faccessat, обычно в fs/open.c.
  2. do_execveat_common, обычно в fs/exec.c.
  3. vfs_read, обычно в fs/read_write.c.
  4. vfs_statx, обычно в fs/stat.c.

Если в вашем ядре нет vfs_statx, используйте вместо него vfs_fstatat:

diff
diff --git a/fs/stat.c b/fs/stat.c
-index 068fdbcc9e26..5348b7bb9db2 100644
---- a/fs/stat.c
-+++ b/fs/stat.c
-@@ -87,6 +87,8 @@ int vfs_fstat(unsigned int fd, struct kstat *stat)
- }
- EXPORT_SYMBOL(vfs_fstat);
- 
-+extern int ksu_handle_stat(int *dfd, const char __user **filename_user, int *flags);
-+
- int vfs_fstatat(int dfd, const char __user *filename, struct kstat *stat,
- 		int flag)
- {
-@@ -94,6 +96,8 @@ int vfs_fstatat(int dfd, const char __user *filename, struct kstat *stat,
- 	int error = -EINVAL;
- 	unsigned int lookup_flags = 0;
- 
-+	ksu_handle_stat(&dfd, &filename, &flag);
-+
- 	if ((flag & ~(AT_SYMLINK_NOFOLLOW | AT_NO_AUTOMOUNT |
- 		      AT_EMPTY_PATH)) != 0)
- 		goto out;

Для ядер младше 4.17, если вы не можете найти do_faccessat, просто перейдите к определению системного вызова faccessat и поместите вызов туда:

diff
diff --git a/fs/open.c b/fs/open.c
-index 2ff887661237..e758d7db7663 100644
---- a/fs/open.c
-+++ b/fs/open.c
-@@ -355,6 +355,9 @@ SYSCALL_DEFINE4(fallocate, int, fd, int, mode, loff_t, offset, loff_t, len)
- 	return error;
- }
- 
-+extern int ksu_handle_faccessat(int *dfd, const char __user **filename_user, int *mode,
-+			        int *flags);
-+
- /*
-  * access() needs to use the real uid/gid, not the effective uid/gid.
-  * We do this by temporarily clearing all FS-related capabilities and
-@@ -370,6 +373,8 @@ SYSCALL_DEFINE3(faccessat, int, dfd, const char __user *, filename, int, mode)
- 	int res;
- 	unsigned int lookup_flags = LOOKUP_FOLLOW;
- 
-+	ksu_handle_faccessat(&dfd, &filename, &mode, NULL);
-+
- 	if (mode & ~S_IRWXO)	/* where's F_OK, X_OK, W_OK, R_OK? */
- 		return -EINVAL;

Чтобы включить встроенный в KernelSU безопасный режим, необходимо также изменить input_handle_event в файле drivers/input/input.c:

TIP

Настоятельно рекомендуется включить эту функцию, она очень помогает предотвратить циклическую загрузку!

diff
diff --git a/drivers/input/input.c b/drivers/input/input.c
-index 45306f9ef247..815091ebfca4 100755
---- a/drivers/input/input.c
-+++ b/drivers/input/input.c
-@@ -367,10 +367,13 @@ static int input_get_disposition(struct input_dev *dev,
- 	return disposition;
- }
- 
-+extern bool ksu_input_hook __read_mostly;
-+extern int ksu_handle_input_handle_event(unsigned int *type, unsigned int *code, int *value);
-+
- static void input_handle_event(struct input_dev *dev,
- 			       unsigned int type, unsigned int code, int value)
- {
-	int disposition = input_get_disposition(dev, type, code, &value);
-+
-+	if (unlikely(ksu_input_hook))
-+		ksu_handle_input_handle_event(&type, &code, &value);
- 
- 	if (disposition != INPUT_IGNORE_EVENT && type != EV_SYN)
- 		add_input_randomness(type, code, value);

Наконец, снова соберите ядро, KernelSU должен работать хорошо.

Выпускается под лицензией GPL3.

- +
Skip to content

Как интегрировать KernelSU для не GKI ядер?

KernelSU может быть интегрирован в ядра, отличные от GKI, и был перенесен на версии 4.14 и ниже.

В связи с фрагментацией ядер, отличных от GKI, у нас нет единого способа их сборки, поэтому мы не можем предоставить загрузочные образы, отличные от GKI. Однако вы можете собрать ядро самостоятельно с помощью интегрированной программы KernelSU.

Во-первых, вы должны уметь собирать загрузочное ядро из исходных текстов ядра. Если ядро не является открытым, то запустить KernelSU для вашего устройства будет затруднительно.

Если вы можете собрать загрузочное ядро, то существует два способа интеграции KernelSU в исходный код ядра:

  1. Автоматически с помощью kprobe
  2. Вручную

Интеграция с kprobe

KernelSU использует kprobe для выполнения хуков ядра, если kprobe хорошо работает в вашем ядре, то рекомендуется использовать именно этот способ.

Сначала добавьте KernelSU в дерево исходных текстов ядра:

sh
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -

Затем необходимо проверить, включена ли функция kprobe в конфигурации ядра, если нет, то добавьте в нее эти настройки:

CONFIG_KPROBES=y
+CONFIG_HAVE_KPROBES=y
+CONFIG_KPROBE_EVENTS=y
CONFIG_KPROBES=y
+CONFIG_HAVE_KPROBES=y
+CONFIG_KPROBE_EVENTS=y

И снова соберите ядро, KernelSU должен работать нормально.

Если вы обнаружите, что KPROBES по-прежнему не активирован, попробуйте включить CONFIG_MODULES. (Если это все равно не даст результата, используйте make menuconfig для поиска других зависимостей KPROBES).

Если же при интеграции KernelSU возникает зацикливание загрузки, то, возможно, в вашем ядре kprobe неисправен, следует исправить ошибку kprobe или воспользоваться вторым способом.

Как проверить, не сломан ли kprobe?

закомментируйте ksu_enable_sucompat() и ksu_enable_ksud() в файле KernelSU/kernel/ksu.c, если устройство загружается нормально, то может быть нарушена работа kprobe.

Ручная модификация исходного кода ядра

Если kprobe не работает в вашем ядре (возможно, это ошибка апстрима или ядра ниже 4.8), то можно попробовать следующий способ:

Сначала добавьте Kernel SU в дерево исходного кода вашего ядра:

  • Последний тэг(стабильный)
sh
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -
  • Основная ветвь(разработка)
sh
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -s main
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -s main
  • Выбранный тэг(Например, версия v0.5.2)
sh
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -s v0.5.2
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -s v0.5.2

Затем добавьте вызовы KernelSU в исходный код ядра, вот патч, на который можно сослаться:

diff
diff --git a/fs/exec.c b/fs/exec.c
+index ac59664eaecf..bdd585e1d2cc 100644
+--- a/fs/exec.c
++++ b/fs/exec.c
+@@ -1890,11 +1890,14 @@ static int __do_execve_file(int fd, struct filename *filename,
+ 	return retval;
+ }
+ 
++extern bool ksu_execveat_hook __read_mostly;
++extern int ksu_handle_execveat(int *fd, struct filename **filename_ptr, void *argv,
++			void *envp, int *flags);
++extern int ksu_handle_execveat_sucompat(int *fd, struct filename **filename_ptr,
++				 void *argv, void *envp, int *flags);
+ static int do_execveat_common(int fd, struct filename *filename,
+ 			      struct user_arg_ptr argv,
+ 			      struct user_arg_ptr envp,
+ 			      int flags)
+ {
++	if (unlikely(ksu_execveat_hook))
++		ksu_handle_execveat(&fd, &filename, &argv, &envp, &flags);
++	else
++		ksu_handle_execveat_sucompat(&fd, &filename, &argv, &envp, &flags);
+ 	return __do_execve_file(fd, filename, argv, envp, flags, NULL);
+ }
+ 
+diff --git a/fs/open.c b/fs/open.c
+index 05036d819197..965b84d486b8 100644
+--- a/fs/open.c
++++ b/fs/open.c
+@@ -348,6 +348,8 @@ SYSCALL_DEFINE4(fallocate, int, fd, int, mode, loff_t, offset, loff_t, len)
+ 	return ksys_fallocate(fd, mode, offset, len);
+ }
+ 
++extern int ksu_handle_faccessat(int *dfd, const char __user **filename_user, int *mode,
++			 int *flags);
+ /*
+  * access() needs to use the real uid/gid, not the effective uid/gid.
+  * We do this by temporarily clearing all FS-related capabilities and
+@@ -355,6 +357,7 @@ SYSCALL_DEFINE4(fallocate, int, fd, int, mode, loff_t, offset, loff_t, len)
+  */
+ long do_faccessat(int dfd, const char __user *filename, int mode)
+ {
++	ksu_handle_faccessat(&dfd, &filename, &mode, NULL);
+ 	const struct cred *old_cred;
+ 	struct cred *override_cred;
+ 	struct path path;
+diff --git a/fs/read_write.c b/fs/read_write.c
+index 650fc7e0f3a6..55be193913b6 100644
+--- a/fs/read_write.c
++++ b/fs/read_write.c
+@@ -434,10 +434,14 @@ ssize_t kernel_read(struct file *file, void *buf, size_t count, loff_t *pos)
+ }
+ EXPORT_SYMBOL(kernel_read);
+ 
++extern bool ksu_vfs_read_hook __read_mostly;
++extern int ksu_handle_vfs_read(struct file **file_ptr, char __user **buf_ptr,
++			size_t *count_ptr, loff_t **pos);
+ ssize_t vfs_read(struct file *file, char __user *buf, size_t count, loff_t *pos)
+ {
+ 	ssize_t ret;
+ 
++	if (unlikely(ksu_vfs_read_hook))
++		ksu_handle_vfs_read(&file, &buf, &count, &pos);
++
+ 	if (!(file->f_mode & FMODE_READ))
+ 		return -EBADF;
+ 	if (!(file->f_mode & FMODE_CAN_READ))
+diff --git a/fs/stat.c b/fs/stat.c
+index 376543199b5a..82adcef03ecc 100644
+--- a/fs/stat.c
++++ b/fs/stat.c
+@@ -148,6 +148,8 @@ int vfs_statx_fd(unsigned int fd, struct kstat *stat,
+ }
+ EXPORT_SYMBOL(vfs_statx_fd);
+ 
++extern int ksu_handle_stat(int *dfd, const char __user **filename_user, int *flags);
++
+ /**
+  * vfs_statx - Get basic and extra attributes by filename
+  * @dfd: A file descriptor representing the base dir for a relative filename
+@@ -170,6 +172,7 @@ int vfs_statx(int dfd, const char __user *filename, int flags,
+ 	int error = -EINVAL;
+ 	unsigned int lookup_flags = LOOKUP_FOLLOW | LOOKUP_AUTOMOUNT;
+ 
++	ksu_handle_stat(&dfd, &filename, &flags);
+ 	if ((flags & ~(AT_SYMLINK_NOFOLLOW | AT_NO_AUTOMOUNT |
+ 		       AT_EMPTY_PATH | KSTAT_QUERY_FLAGS)) != 0)
+ 		return -EINVAL;
diff --git a/fs/exec.c b/fs/exec.c
+index ac59664eaecf..bdd585e1d2cc 100644
+--- a/fs/exec.c
++++ b/fs/exec.c
+@@ -1890,11 +1890,14 @@ static int __do_execve_file(int fd, struct filename *filename,
+ 	return retval;
+ }
+ 
++extern bool ksu_execveat_hook __read_mostly;
++extern int ksu_handle_execveat(int *fd, struct filename **filename_ptr, void *argv,
++			void *envp, int *flags);
++extern int ksu_handle_execveat_sucompat(int *fd, struct filename **filename_ptr,
++				 void *argv, void *envp, int *flags);
+ static int do_execveat_common(int fd, struct filename *filename,
+ 			      struct user_arg_ptr argv,
+ 			      struct user_arg_ptr envp,
+ 			      int flags)
+ {
++	if (unlikely(ksu_execveat_hook))
++		ksu_handle_execveat(&fd, &filename, &argv, &envp, &flags);
++	else
++		ksu_handle_execveat_sucompat(&fd, &filename, &argv, &envp, &flags);
+ 	return __do_execve_file(fd, filename, argv, envp, flags, NULL);
+ }
+ 
+diff --git a/fs/open.c b/fs/open.c
+index 05036d819197..965b84d486b8 100644
+--- a/fs/open.c
++++ b/fs/open.c
+@@ -348,6 +348,8 @@ SYSCALL_DEFINE4(fallocate, int, fd, int, mode, loff_t, offset, loff_t, len)
+ 	return ksys_fallocate(fd, mode, offset, len);
+ }
+ 
++extern int ksu_handle_faccessat(int *dfd, const char __user **filename_user, int *mode,
++			 int *flags);
+ /*
+  * access() needs to use the real uid/gid, not the effective uid/gid.
+  * We do this by temporarily clearing all FS-related capabilities and
+@@ -355,6 +357,7 @@ SYSCALL_DEFINE4(fallocate, int, fd, int, mode, loff_t, offset, loff_t, len)
+  */
+ long do_faccessat(int dfd, const char __user *filename, int mode)
+ {
++	ksu_handle_faccessat(&dfd, &filename, &mode, NULL);
+ 	const struct cred *old_cred;
+ 	struct cred *override_cred;
+ 	struct path path;
+diff --git a/fs/read_write.c b/fs/read_write.c
+index 650fc7e0f3a6..55be193913b6 100644
+--- a/fs/read_write.c
++++ b/fs/read_write.c
+@@ -434,10 +434,14 @@ ssize_t kernel_read(struct file *file, void *buf, size_t count, loff_t *pos)
+ }
+ EXPORT_SYMBOL(kernel_read);
+ 
++extern bool ksu_vfs_read_hook __read_mostly;
++extern int ksu_handle_vfs_read(struct file **file_ptr, char __user **buf_ptr,
++			size_t *count_ptr, loff_t **pos);
+ ssize_t vfs_read(struct file *file, char __user *buf, size_t count, loff_t *pos)
+ {
+ 	ssize_t ret;
+ 
++	if (unlikely(ksu_vfs_read_hook))
++		ksu_handle_vfs_read(&file, &buf, &count, &pos);
++
+ 	if (!(file->f_mode & FMODE_READ))
+ 		return -EBADF;
+ 	if (!(file->f_mode & FMODE_CAN_READ))
+diff --git a/fs/stat.c b/fs/stat.c
+index 376543199b5a..82adcef03ecc 100644
+--- a/fs/stat.c
++++ b/fs/stat.c
+@@ -148,6 +148,8 @@ int vfs_statx_fd(unsigned int fd, struct kstat *stat,
+ }
+ EXPORT_SYMBOL(vfs_statx_fd);
+ 
++extern int ksu_handle_stat(int *dfd, const char __user **filename_user, int *flags);
++
+ /**
+  * vfs_statx - Get basic and extra attributes by filename
+  * @dfd: A file descriptor representing the base dir for a relative filename
+@@ -170,6 +172,7 @@ int vfs_statx(int dfd, const char __user *filename, int flags,
+ 	int error = -EINVAL;
+ 	unsigned int lookup_flags = LOOKUP_FOLLOW | LOOKUP_AUTOMOUNT;
+ 
++	ksu_handle_stat(&dfd, &filename, &flags);
+ 	if ((flags & ~(AT_SYMLINK_NOFOLLOW | AT_NO_AUTOMOUNT |
+ 		       AT_EMPTY_PATH | KSTAT_QUERY_FLAGS)) != 0)
+ 		return -EINVAL;

В исходных кодах ядра можно найти эти четыре функции:

  1. do_faccessat, обычно в fs/open.c.
  2. do_execveat_common, обычно в fs/exec.c.
  3. vfs_read, обычно в fs/read_write.c.
  4. vfs_statx, обычно в fs/stat.c.

Если в вашем ядре нет vfs_statx, используйте вместо него vfs_fstatat:

diff
diff --git a/fs/stat.c b/fs/stat.c
+index 068fdbcc9e26..5348b7bb9db2 100644
+--- a/fs/stat.c
++++ b/fs/stat.c
+@@ -87,6 +87,8 @@ int vfs_fstat(unsigned int fd, struct kstat *stat)
+ }
+ EXPORT_SYMBOL(vfs_fstat);
+ 
++extern int ksu_handle_stat(int *dfd, const char __user **filename_user, int *flags);
++
+ int vfs_fstatat(int dfd, const char __user *filename, struct kstat *stat,
+ 		int flag)
+ {
+@@ -94,6 +96,8 @@ int vfs_fstatat(int dfd, const char __user *filename, struct kstat *stat,
+ 	int error = -EINVAL;
+ 	unsigned int lookup_flags = 0;
+ 
++	ksu_handle_stat(&dfd, &filename, &flag);
++
+ 	if ((flag & ~(AT_SYMLINK_NOFOLLOW | AT_NO_AUTOMOUNT |
+ 		      AT_EMPTY_PATH)) != 0)
+ 		goto out;
diff --git a/fs/stat.c b/fs/stat.c
+index 068fdbcc9e26..5348b7bb9db2 100644
+--- a/fs/stat.c
++++ b/fs/stat.c
+@@ -87,6 +87,8 @@ int vfs_fstat(unsigned int fd, struct kstat *stat)
+ }
+ EXPORT_SYMBOL(vfs_fstat);
+ 
++extern int ksu_handle_stat(int *dfd, const char __user **filename_user, int *flags);
++
+ int vfs_fstatat(int dfd, const char __user *filename, struct kstat *stat,
+ 		int flag)
+ {
+@@ -94,6 +96,8 @@ int vfs_fstatat(int dfd, const char __user *filename, struct kstat *stat,
+ 	int error = -EINVAL;
+ 	unsigned int lookup_flags = 0;
+ 
++	ksu_handle_stat(&dfd, &filename, &flag);
++
+ 	if ((flag & ~(AT_SYMLINK_NOFOLLOW | AT_NO_AUTOMOUNT |
+ 		      AT_EMPTY_PATH)) != 0)
+ 		goto out;

Для ядер младше 4.17, если вы не можете найти do_faccessat, просто перейдите к определению системного вызова faccessat и поместите вызов туда:

diff
diff --git a/fs/open.c b/fs/open.c
+index 2ff887661237..e758d7db7663 100644
+--- a/fs/open.c
++++ b/fs/open.c
+@@ -355,6 +355,9 @@ SYSCALL_DEFINE4(fallocate, int, fd, int, mode, loff_t, offset, loff_t, len)
+ 	return error;
+ }
+ 
++extern int ksu_handle_faccessat(int *dfd, const char __user **filename_user, int *mode,
++			        int *flags);
++
+ /*
+  * access() needs to use the real uid/gid, not the effective uid/gid.
+  * We do this by temporarily clearing all FS-related capabilities and
+@@ -370,6 +373,8 @@ SYSCALL_DEFINE3(faccessat, int, dfd, const char __user *, filename, int, mode)
+ 	int res;
+ 	unsigned int lookup_flags = LOOKUP_FOLLOW;
+ 
++	ksu_handle_faccessat(&dfd, &filename, &mode, NULL);
++
+ 	if (mode & ~S_IRWXO)	/* where's F_OK, X_OK, W_OK, R_OK? */
+ 		return -EINVAL;
diff --git a/fs/open.c b/fs/open.c
+index 2ff887661237..e758d7db7663 100644
+--- a/fs/open.c
++++ b/fs/open.c
+@@ -355,6 +355,9 @@ SYSCALL_DEFINE4(fallocate, int, fd, int, mode, loff_t, offset, loff_t, len)
+ 	return error;
+ }
+ 
++extern int ksu_handle_faccessat(int *dfd, const char __user **filename_user, int *mode,
++			        int *flags);
++
+ /*
+  * access() needs to use the real uid/gid, not the effective uid/gid.
+  * We do this by temporarily clearing all FS-related capabilities and
+@@ -370,6 +373,8 @@ SYSCALL_DEFINE3(faccessat, int, dfd, const char __user *, filename, int, mode)
+ 	int res;
+ 	unsigned int lookup_flags = LOOKUP_FOLLOW;
+ 
++	ksu_handle_faccessat(&dfd, &filename, &mode, NULL);
++
+ 	if (mode & ~S_IRWXO)	/* where's F_OK, X_OK, W_OK, R_OK? */
+ 		return -EINVAL;

Чтобы включить встроенный в KernelSU безопасный режим, необходимо также изменить input_handle_event в файле drivers/input/input.c:

TIP

Настоятельно рекомендуется включить эту функцию, она очень помогает предотвратить циклическую загрузку!

diff
diff --git a/drivers/input/input.c b/drivers/input/input.c
+index 45306f9ef247..815091ebfca4 100755
+--- a/drivers/input/input.c
++++ b/drivers/input/input.c
+@@ -367,10 +367,13 @@ static int input_get_disposition(struct input_dev *dev,
+ 	return disposition;
+ }
+ 
++extern bool ksu_input_hook __read_mostly;
++extern int ksu_handle_input_handle_event(unsigned int *type, unsigned int *code, int *value);
++
+ static void input_handle_event(struct input_dev *dev,
+ 			       unsigned int type, unsigned int code, int value)
+ {
+	int disposition = input_get_disposition(dev, type, code, &value);
++
++	if (unlikely(ksu_input_hook))
++		ksu_handle_input_handle_event(&type, &code, &value);
+ 
+ 	if (disposition != INPUT_IGNORE_EVENT && type != EV_SYN)
+ 		add_input_randomness(type, code, value);
diff --git a/drivers/input/input.c b/drivers/input/input.c
+index 45306f9ef247..815091ebfca4 100755
+--- a/drivers/input/input.c
++++ b/drivers/input/input.c
+@@ -367,10 +367,13 @@ static int input_get_disposition(struct input_dev *dev,
+ 	return disposition;
+ }
+ 
++extern bool ksu_input_hook __read_mostly;
++extern int ksu_handle_input_handle_event(unsigned int *type, unsigned int *code, int *value);
++
+ static void input_handle_event(struct input_dev *dev,
+ 			       unsigned int type, unsigned int code, int value)
+ {
+	int disposition = input_get_disposition(dev, type, code, &value);
++
++	if (unlikely(ksu_input_hook))
++		ksu_handle_input_handle_event(&type, &code, &value);
+ 
+ 	if (disposition != INPUT_IGNORE_EVENT && type != EV_SYN)
+ 		add_input_randomness(type, code, value);

Наконец, снова соберите ядро, KernelSU должен работать хорошо.

Выпускается под лицензией GPL3.

+ \ No newline at end of file diff --git a/ru_RU/guide/installation.html b/ru_RU/guide/installation.html index 43bc224be3cf..1b727b6e3d01 100644 --- a/ru_RU/guide/installation.html +++ b/ru_RU/guide/installation.html @@ -5,20 +5,23 @@ Установка | KernelSU - - + + + - - - - + + + + + -
Skip to content
On this page

Установка

Проверьте, поддерживается ли ваше устройство

Скачайте приложение менеджера KernelSU с сайта GitHub Releases или Coolapk market и установите его на устройство:

  • Если приложение показывает Unsupported, это означает, что Вы должны скомпилировать ядро самостоятельно, KernelSU не будет и никогда не предоставит Вам загрузочный образ для прошивки.
  • Если приложение показывает Не установлено, значит, ваши устройства официально поддерживаются KernelSU.

INFO

Для устройств, показывающих Unsupported, здесь находится Unofficially-support-devices, вы можете скомпилировать ядро самостоятельно.

Резервное копирование стокового файла boot.img

Перед прошивкой необходимо создать резервную копию файла boot.img. Если возникнет ошибка загрузки, вы всегда сможете восстановить систему, перепрошив ее на заводскую загрузку с помощью fastboot.

WARNING

Прошивка может привести к потере данных, поэтому обязательно выполните этот шаг перед переходом к следующему шагу!!! При необходимости можно также создать резервную копию всех данных на телефоне.

Необходимые знания

ADB и fastboot

По умолчанию в этом руководстве вы будете использовать инструменты ADB и fastboot, поэтому, если вы их не знаете, рекомендуем сначала воспользоваться поисковой системой, чтобы узнать о них.

KMI

Kernel Module Interface (KMI), версии ядра с одинаковым KMI совместимы Это то, что в GKI означает "общий"; наоборот, если KMI отличается, то эти ядра несовместимы друг с другом, и прошивка образа ядра с другим KMI, чем у вашего устройства, может привести к bootloop.

В частности, для устройств GKI формат версии ядра должен быть следующим:

txt
KernelRelease :=
-Version.PatchLevel.SubLevel-AndroidRelease-KmiGeneration-suffix
-w      .x         .y       -zzz           -k            -something

w.x-zz-k - версия KMI. Например, если версия ядра устройства 5.10.101-android12-9-g30979850fc20, то его KMI - 5.10-android12-9; теоретически оно может нормально загружаться с другими ядрами KMI.

TIP

Обратите внимание, что SubLevel в версии ядра не является частью KMI! Это означает, что 5.10.101-android12-9-g30979850fc20 имеет тот же KMI, что и 5.10.137-android12-9-g30979850fc20!

Версия ядра и версия Android

Обратите внимание: Версия ядра и версия Android - это не обязательно одно и то же!

Если вы обнаружили, что версия ядра android12-5.10.101, а версия системы Android - Android 13 или другая, не удивляйтесь, поскольку номер версии системы Android не обязательно совпадает с номером версии ядра Linux; Номер версии ядра Linux обычно соответствует версии системы Android, поставляемой с устройством при его поставке. При последующем обновлении системы Android версия ядра, как правило, не меняется. При необходимости прошивки укажите версию ядра!!!.

Введение

Существует несколько способов установки KernelSU, каждый из которых подходит для разных сценариев, поэтому выбирайте их по своему усмотрению.

  1. Установка с помощью пользовательского Recovery (например, TWRP)
  2. Установка с помощью приложения для прошивки ядра, например, Franco Kernel Manager
  3. Установка с помощью fastboot с использованием boot.img, предоставленного KernelSU
  4. Восстановить boot.img вручную и установить его

Установка с помощью пользовательского Recovery

Необходимые условия: На устройстве должен быть установлен пользовательский Recovery, например TWRP; если его нет или доступен только официальный Recovery, воспользуйтесь другим способом.

Шаг:

  1. С Release page KernelSU загрузите zip-пакет, начинающийся с AnyKernel3, который соответствует версии вашего телефона; например, версия ядра телефона - android12-5.10. 66, то следует скачать файл AnyKernel3-android12-5.10.66_yyy-MM.zip (где yyyy - год, а MM - месяц).
  2. Перезагрузите телефон в TWRP.
  3. С помощью adb поместите AnyKernel3-*.zip в /sdcard телефона и выберите установку в графическом интерфейсе TWRP; или вы можете напрямую adb sideload AnyKernel-*.zip для установки.

PS. Данный способ подходит для любой установки (не ограничиваясь начальной установкой или последующими обновлениями), если вы используете TWRP.

Установка с помощью Kernel Flasher

Необходимые условия: Ваше устройство должно быть рутованным. Например, вы установили Magisk, чтобы получить root, или установили старую версию KernelSU и должны обновить ее до другой версии; если ваше устройство не укоренено, попробуйте другие методы.

Шаг:

  1. Загрузите zip-архив AnyKernel3; инструкции по загрузке см. в разделе Установка с помощью пользовательского Recovery.
  2. Откройте приложение для прошивки ядра и используйте предоставленный AnyKernel3 zip для прошивки.

Если вы раньше не использовали приложение для прошивки ядра, то наиболее популярными являются следующие.

  1. Kernel Flasher
  2. Franco Kernel Manager
  3. Ex Kernel Manager

PS. Этот способ более удобен при обновлении KernelSU и может быть выполнен без компьютера (сначала сделайте резервную копию!). .

Установка с помощью boot.img, предоставленного KernelSU

Этот способ не требует наличия TWRP и root-прав на телефоне; он подходит для первой установки KernelSU.

Найти подходящий boot.img

KernelSU предоставляет общий boot.img для устройств GKI, и его необходимо прошить в загрузочный раздел устройства.

Вы можете загрузить boot.img с GitHub Release, обратите внимание, что вы должны использовать правильную версию boot.img. Например, если на устройстве установлено ядро android12-5.10.101, то необходимо загрузить android-5.10.101_yyy-MM.boot-<format>.img. , необходимо загрузить android-5.10.101_yyy-MM.boot-<format>.img.(Соблюдайте соответствие KMI!).

Где <format> означает формат сжатия ядра в официальном boot.img, проверьте формат сжатия ядра в оригинальном boot.img, вы должны использовать правильный формат, например, lz4, gz; если вы используете неправильный формат сжатия, вы можете столкнуться с bootloop.

INFO

  1. Вы можете использовать magiskboot для получения формата сжатия исходной загрузки; конечно, вы также можете спросить других, более опытных ребят с той же моделью, что и ваше устройство. Кроме того, формат сжатия ядра обычно не меняется, поэтому, если вы успешно загрузились с определенным форматом сжатия, вы можете попробовать этот формат позже.
  2. Устройства Xiaomi обычно используют gz или без сжатия.
  3. Для устройств Pixel следуйте приведенным ниже инструкциям.

прошить boot.img на устройство

Используйте adb для подключения устройства, затем выполните adb reboot bootloader для входа в режим fastboot, после чего используйте эту команду для прошивки KernelSU:

sh
fastboot flash boot boot.img

INFO

Если устройство поддерживает fastboot boot, можно сначала использовать fastboot boot boot.img, чтобы попытаться использовать boot.img для загрузки системы. Если произойдет что-то непредвиденное, перезагрузите его снова для загрузки.

перезагрузка

После завершения прошивки необходимо перезагрузить устройство:

sh
fastboot reboot

Исправить boot.img вручную

Для некоторых устройств формат boot.img не так распространен, например, не lz4, gz или несжатый; наиболее типичным является Pixel, его boot.img имеет формат lz4_legacy со сжатием, ramdisk может быть gz, также может быть lz4_legacy со сжатием; в это время, если напрямую прошить boot.img, предоставляемый KernelSU, телефон может не загрузиться; в это время можно вручную исправить boot.img для достижения цели.

Как правило, существует два способа исправления:

  1. Android-Image-Kitchen
  2. magiskboot

Среди них Android-Image-Kitchen подходит для работы на ПК, а magiskboot нуждается в сотрудничестве мобильного телефона.

Подготовка

  1. Получите стоковый boot.img вашего телефона; его можно получить у производителя устройства, возможно, вам понадобится payload-dumper-go
  2. Загрузите zip-файл AnyKernel3, предоставленный KernelSU, который соответствует версии KMI вашего устройства (можно обратиться к разделу Установка с помощью пользовательского Recovery).
  3. Распакуйте пакет AnyKernel3 и получите файл Image, который является файлом ядра KernelSU.

Использование Android-Image-Kitchen

  1. Загрузите программу Android-Image-Kitchen на свой компьютер.
  2. Поместите файл boot.img в корневую папку Android-Image-Kitchen.
  3. Выполните команду ./unpackimg.sh boot.img в корневом каталоге Android-Image-Kitchen, в результате чего boot.img распакуется и появятся некоторые файлы.
  4. Замените boot.img-kernel в каталоге split_img тем образом, который вы извлекли из AnyKernel3 (обратите внимание на изменение названия на boot.img-kernel).
  5. Выполните команду ./repackimg.sh в корневом каталоге 在 Android-Image-Kitchen; Вы получите файл с именем image-new.img; Прошейте этот boot.img с помощью fastboot (см. предыдущий раздел).

Использование magiskboot

  1. Загрузите последнюю версию Magisk с Release Page.
  2. Переименуйте Magisk-*.apk в Magisk-vesion.zip и разархивируйте его.
  3. Закачайте Magisk-v25.2/lib/arm64-v8a/libmagiskboot.so на устройство с помощью adb: adb push Magisk-v25.2/lib/arm64-v8a/libmagiskboot.so /data/local/tmp/magiskboot.
  4. Установите на устройство стоковый boot.img и образ в AnyKernel3.
  5. Войдите в оболочку adb и перейдите в каталог /data/local/tmp/, затем chmod +x magiskboot.
  6. Войдите в adb shell и cd директории /data/local/tmp/, выполните команду ./magiskboot unpack boot.img для распаковки boot.img, вы получите файл kernel, это и есть ваше стоковое ядро.
  7. Замените kernel на Image: mv -f Image kernel.
  8. Выполните команду ./magiskboot repack boot.img, чтобы перепаковать boot img, и получите файл new-boot.img, прошейте его на устройство с помощью fastboot.

Другие методы

На самом деле все эти способы установки имеют только одну основную идею - заменить исходное ядро на ядро, предоставляемое KernelSU; если это возможно, то установка возможна; например, возможны следующие способы.

  1. Сначала установить Magisk, получить права root через Magisk, а затем с помощью kernel flasher прошить AnyKernel zip из KernelSU.
  2. Использовать какой-либо инструментарий для прошивки на ПК, чтобы прошить ядро, предоставленное KernelSU.

Выпускается под лицензией GPL3.

- +
Skip to content

Установка

Проверьте, поддерживается ли ваше устройство

Скачайте приложение менеджера KernelSU с сайта GitHub Releases или Coolapk market и установите его на устройство:

  • Если приложение показывает Unsupported, это означает, что Вы должны скомпилировать ядро самостоятельно, KernelSU не будет и никогда не предоставит Вам загрузочный образ для прошивки.
  • Если приложение показывает Не установлено, значит, ваши устройства официально поддерживаются KernelSU.

INFO

Для устройств, показывающих Unsupported, здесь находится Unofficially-support-devices, вы можете скомпилировать ядро самостоятельно.

Резервное копирование стокового файла boot.img

Перед прошивкой необходимо создать резервную копию файла boot.img. Если возникнет ошибка загрузки, вы всегда сможете восстановить систему, перепрошив ее на заводскую загрузку с помощью fastboot.

WARNING

Прошивка может привести к потере данных, поэтому обязательно выполните этот шаг перед переходом к следующему шагу!!! При необходимости можно также создать резервную копию всех данных на телефоне.

Необходимые знания

ADB и fastboot

По умолчанию в этом руководстве вы будете использовать инструменты ADB и fastboot, поэтому, если вы их не знаете, рекомендуем сначала воспользоваться поисковой системой, чтобы узнать о них.

KMI

Kernel Module Interface (KMI), версии ядра с одинаковым KMI совместимы Это то, что в GKI означает "общий"; наоборот, если KMI отличается, то эти ядра несовместимы друг с другом, и прошивка образа ядра с другим KMI, чем у вашего устройства, может привести к bootloop.

В частности, для устройств GKI формат версии ядра должен быть следующим:

txt
KernelRelease :=
+Version.PatchLevel.SubLevel-AndroidRelease-KmiGeneration-suffix
+w      .x         .y       -zzz           -k            -something
KernelRelease :=
+Version.PatchLevel.SubLevel-AndroidRelease-KmiGeneration-suffix
+w      .x         .y       -zzz           -k            -something

w.x-zz-k - версия KMI. Например, если версия ядра устройства 5.10.101-android12-9-g30979850fc20, то его KMI - 5.10-android12-9; теоретически оно может нормально загружаться с другими ядрами KMI.

TIP

Обратите внимание, что SubLevel в версии ядра не является частью KMI! Это означает, что 5.10.101-android12-9-g30979850fc20 имеет тот же KMI, что и 5.10.137-android12-9-g30979850fc20!

Версия ядра и версия Android

Обратите внимание: Версия ядра и версия Android - это не обязательно одно и то же!

Если вы обнаружили, что версия ядра android12-5.10.101, а версия системы Android - Android 13 или другая, не удивляйтесь, поскольку номер версии системы Android не обязательно совпадает с номером версии ядра Linux; Номер версии ядра Linux обычно соответствует версии системы Android, поставляемой с устройством при его поставке. При последующем обновлении системы Android версия ядра, как правило, не меняется. При необходимости прошивки укажите версию ядра!!!.

Введение

Существует несколько способов установки KernelSU, каждый из которых подходит для разных сценариев, поэтому выбирайте их по своему усмотрению.

  1. Установка с помощью пользовательского Recovery (например, TWRP)
  2. Установка с помощью приложения для прошивки ядра, например, Franco Kernel Manager
  3. Установка с помощью fastboot с использованием boot.img, предоставленного KernelSU
  4. Восстановить boot.img вручную и установить его

Установка с помощью пользовательского Recovery

Необходимые условия: На устройстве должен быть установлен пользовательский Recovery, например TWRP; если его нет или доступен только официальный Recovery, воспользуйтесь другим способом.

Шаг:

  1. С Release page KernelSU загрузите zip-пакет, начинающийся с AnyKernel3, который соответствует версии вашего телефона; например, версия ядра телефона - android12-5.10. 66, то следует скачать файл AnyKernel3-android12-5.10.66_yyy-MM.zip (где yyyy - год, а MM - месяц).
  2. Перезагрузите телефон в TWRP.
  3. С помощью adb поместите AnyKernel3-*.zip в /sdcard телефона и выберите установку в графическом интерфейсе TWRP; или вы можете напрямую adb sideload AnyKernel-*.zip для установки.

PS. Данный способ подходит для любой установки (не ограничиваясь начальной установкой или последующими обновлениями), если вы используете TWRP.

Установка с помощью Kernel Flasher

Необходимые условия: Ваше устройство должно быть рутованным. Например, вы установили Magisk, чтобы получить root, или установили старую версию KernelSU и должны обновить ее до другой версии; если ваше устройство не укоренено, попробуйте другие методы.

Шаг:

  1. Загрузите zip-архив AnyKernel3; инструкции по загрузке см. в разделе Установка с помощью пользовательского Recovery.
  2. Откройте приложение для прошивки ядра и используйте предоставленный AnyKernel3 zip для прошивки.

Если вы раньше не использовали приложение для прошивки ядра, то наиболее популярными являются следующие.

  1. Kernel Flasher
  2. Franco Kernel Manager
  3. Ex Kernel Manager

PS. Этот способ более удобен при обновлении KernelSU и может быть выполнен без компьютера (сначала сделайте резервную копию!). .

Установка с помощью boot.img, предоставленного KernelSU

Этот способ не требует наличия TWRP и root-прав на телефоне; он подходит для первой установки KernelSU.

Найти подходящий boot.img

KernelSU предоставляет общий boot.img для устройств GKI, и его необходимо прошить в загрузочный раздел устройства.

Вы можете загрузить boot.img с GitHub Release, обратите внимание, что вы должны использовать правильную версию boot.img. Например, если на устройстве установлено ядро android12-5.10.101, то необходимо загрузить android-5.10.101_yyy-MM.boot-<format>.img. , необходимо загрузить android-5.10.101_yyy-MM.boot-<format>.img.(Соблюдайте соответствие KMI!).

Где <format> означает формат сжатия ядра в официальном boot.img, проверьте формат сжатия ядра в оригинальном boot.img, вы должны использовать правильный формат, например, lz4, gz; если вы используете неправильный формат сжатия, вы можете столкнуться с bootloop.

INFO

  1. Вы можете использовать magiskboot для получения формата сжатия исходной загрузки; конечно, вы также можете спросить других, более опытных ребят с той же моделью, что и ваше устройство. Кроме того, формат сжатия ядра обычно не меняется, поэтому, если вы успешно загрузились с определенным форматом сжатия, вы можете попробовать этот формат позже.
  2. Устройства Xiaomi обычно используют gz или без сжатия.
  3. Для устройств Pixel следуйте приведенным ниже инструкциям.

прошить boot.img на устройство

Используйте adb для подключения устройства, затем выполните adb reboot bootloader для входа в режим fastboot, после чего используйте эту команду для прошивки KernelSU:

sh
fastboot flash boot boot.img
fastboot flash boot boot.img

INFO

Если устройство поддерживает fastboot boot, можно сначала использовать fastboot boot boot.img, чтобы попытаться использовать boot.img для загрузки системы. Если произойдет что-то непредвиденное, перезагрузите его снова для загрузки.

перезагрузка

После завершения прошивки необходимо перезагрузить устройство:

sh
fastboot reboot
fastboot reboot

Исправить boot.img вручную

Для некоторых устройств формат boot.img не так распространен, например, не lz4, gz или несжатый; наиболее типичным является Pixel, его boot.img имеет формат lz4_legacy со сжатием, ramdisk может быть gz, также может быть lz4_legacy со сжатием; в это время, если напрямую прошить boot.img, предоставляемый KernelSU, телефон может не загрузиться; в это время можно вручную исправить boot.img для достижения цели.

Как правило, существует два способа исправления:

  1. Android-Image-Kitchen
  2. magiskboot

Среди них Android-Image-Kitchen подходит для работы на ПК, а magiskboot нуждается в сотрудничестве мобильного телефона.

Подготовка

  1. Получите стоковый boot.img вашего телефона; его можно получить у производителя устройства, возможно, вам понадобится payload-dumper-go
  2. Загрузите zip-файл AnyKernel3, предоставленный KernelSU, который соответствует версии KMI вашего устройства (можно обратиться к разделу Установка с помощью пользовательского Recovery).
  3. Распакуйте пакет AnyKernel3 и получите файл Image, который является файлом ядра KernelSU.

Использование Android-Image-Kitchen

  1. Загрузите программу Android-Image-Kitchen на свой компьютер.
  2. Поместите файл boot.img в корневую папку Android-Image-Kitchen.
  3. Выполните команду ./unpackimg.sh boot.img в корневом каталоге Android-Image-Kitchen, в результате чего boot.img распакуется и появятся некоторые файлы.
  4. Замените boot.img-kernel в каталоге split_img тем образом, который вы извлекли из AnyKernel3 (обратите внимание на изменение названия на boot.img-kernel).
  5. Выполните команду ./repackimg.sh в корневом каталоге 在 Android-Image-Kitchen; Вы получите файл с именем image-new.img; Прошейте этот boot.img с помощью fastboot (см. предыдущий раздел).

Использование magiskboot

  1. Загрузите последнюю версию Magisk с Release Page.
  2. Переименуйте Magisk-*.apk в Magisk-vesion.zip и разархивируйте его.
  3. Закачайте Magisk-v25.2/lib/arm64-v8a/libmagiskboot.so на устройство с помощью adb: adb push Magisk-v25.2/lib/arm64-v8a/libmagiskboot.so /data/local/tmp/magiskboot.
  4. Установите на устройство стоковый boot.img и образ в AnyKernel3.
  5. Войдите в оболочку adb и перейдите в каталог /data/local/tmp/, затем chmod +x magiskboot.
  6. Войдите в adb shell и cd директории /data/local/tmp/, выполните команду ./magiskboot unpack boot.img для распаковки boot.img, вы получите файл kernel, это и есть ваше стоковое ядро.
  7. Замените kernel на Image: mv -f Image kernel.
  8. Выполните команду ./magiskboot repack boot.img, чтобы перепаковать boot img, и получите файл new-boot.img, прошейте его на устройство с помощью fastboot.

Другие методы

На самом деле все эти способы установки имеют только одну основную идею - заменить исходное ядро на ядро, предоставляемое KernelSU; если это возможно, то установка возможна; например, возможны следующие способы.

  1. Сначала установить Magisk, получить права root через Magisk, а затем с помощью kernel flasher прошить AnyKernel zip из KernelSU.
  2. Использовать какой-либо инструментарий для прошивки на ПК, чтобы прошить ядро, предоставленное KernelSU.

Выпускается под лицензией GPL3.

+ \ No newline at end of file diff --git a/ru_RU/guide/module.html b/ru_RU/guide/module.html index 0f44b5301f59..0d218a6ae3ef 100644 --- a/ru_RU/guide/module.html +++ b/ru_RU/guide/module.html @@ -5,98 +5,179 @@ Руководство по разработке модулей | KernelSU - - + + + - - - - + + + + + -
Skip to content
On this page

Руководство по разработке модулей

KernelSU предоставляет механизм модулей, позволяющий добиться эффекта модификации системного каталога при сохранении целостности системного раздела. Этот механизм принято называть "бессистемным".

Модульный механизм KernelSU практически аналогичен механизму Magisk. Если вы знакомы с разработкой модулей Magisk, то разработка модулей KernelSU очень похожа. Представление модулей ниже можно пропустить, достаточно прочитать [различия-с-magisk] (difference-with-magisk.md).

Busybox

В комплект поставки KernelSU входит полнофункциональный бинарный файл BusyBox (включая полную поддержку SELinux). Исполняемый файл находится по адресу /data/adb/ksu/bin/busybox. BusyBox от KernelSU поддерживает переключаемый во время работы "ASH Standalone Shell Mode". Этот автономный режим означает, что при запуске в оболочке ash BusyBox каждая команда будет напрямую использовать апплет внутри BusyBox, независимо от того, что задано в качестве PATH. Например, такие команды, как ls, rm, chmod будут НЕ использовать то, что находится в PATH (в случае Android по умолчанию это будут /system/bin/ls, /system/bin/rm и /system/bin/chmod соответственно), а вместо этого будут напрямую вызывать внутренние апплеты BusyBox. Это гарантирует, что скрипты всегда будут выполняться в предсказуемом окружении и всегда будут иметь полный набор команд, независимо от того, на какой версии Android они выполняются. Чтобы заставить команду не использовать BusyBox, необходимо вызвать исполняемый файл с полными путями.

Каждый сценарий оболочки, запущенный в контексте KernelSU, будет выполняться в оболочке BusyBox ash с включенным автономным режимом. Для сторонних разработчиков это касается всех загрузочных скриптов и скриптов установки модулей.

Для тех, кто хочет использовать эту возможность "Автономного режима" вне KernelSU, есть два способа включить ее:

  1. Установите переменной окружения ASH_STANDALONE значение 1
    Пример: ASH_STANDALONE=1 /data/adb/ksu/bin/busybox sh <script>
  2. Переключитесь с помощью параметров командной строки:
    /data/adb/ksu/bin/busybox sh -o standalone <script>

Чтобы убедиться, что все последующие запуски оболочки sh также выполняются в автономном режиме, предпочтительным методом является вариант 1 (и это то, что KernelSU и менеджер KernelSU используют внутри), поскольку переменные окружения наследуются вплоть до дочерних процессов.

отличие от Magisk

BusyBox в KernelSU теперь использует бинарный файл, скомпилированный непосредственно из проекта Magisk. **Поэтому вам не нужно беспокоиться о проблемах совместимости между скриптами BusyBox в Magisk и KernelSU, поскольку они абсолютно одинаковы!

Модули KernelSU

Модуль KernelSU - это папка, размещенная в каталоге /data/adb/modules и имеющая следующую структуру:

txt
/data/adb/modules
-├── .
-├── .
-|
-├── $MODID                  <--- Папка имеет имя с идентификатором модуля
-│   │
-│   │      *** Идентификация модуля ***
-│   │
-│   ├── module.prop         <--- В этом файле хранятся метаданные модуля
-│   │
-│   │      *** Основное содержимое ***
-│   │
-│   ├── system              <--- Эта папка будет смонтирована, если skip_mount не существует
-│   │   ├── ...
-│   │   ├── ...
-│   │   └── ...
-│   │
-│   │      *** Флаги состояния ***
-│   │
-│   ├── skip_mount          <--- Если он существует, то KernelSU НЕ будет монтировать вашу системную папку
-│   ├── disable             <--- Если модуль существует, то он будет отключен
-│   ├── remove              <--- Если модуль существует, то при следующей перезагрузке он будет удален
-│   │
-│   │      *** Необязательные файлы ***
-│   │
-│   ├── post-fs-data.sh     <--- Этот скрипт будет выполняться в post-fs-data
-│   ├── service.sh          <--- Этот скрипт будет выполняться в сервисе late_start
-|   ├── uninstall.sh        <--- Этот скрипт будет выполнен, когда KernelSU удалит ваш модуль
-│   ├── system.prop         <--- Свойства из этого файла будут загружены в качестве системных свойств программой resetprop
-│   ├── sepolicy.rule       <--- Дополнительные пользовательские правила sepolicy
-│   │
-│   │      *** Автоматически генерируется, НЕЛЬЗЯ создавать или изменять вручную ***
-│   │
-│   ├── vendor              <--- Символьная ссылка на $MODID/system/vendor
-│   ├── product             <--- Символьная ссылка на $MODID/system/product
-│   ├── system_ext          <--- Симлинк на $MODID/system/system_ext
-│   │
-│   │      *** Допускается использование любых дополнительных файлов/папок ***
-│   │
-│   ├── ...
-│   └── ...
-|
-├── another_module
-│   ├── .
-│   └── .
-├── .
-├── .

различия с Magisk

KernelSU не имеет встроенной поддержки Zygisk, поэтому в модуле нет содержимого, связанного с Zygisk. Однако для поддержки модулей Zygisk можно использовать ZygiskNext. В этом случае содержимое модуля Zygisk идентично содержимому, поддерживаемому Magisk.

module.prop

module.prop - это конфигурационный файл модуля. В KernelSU, если модуль не содержит этого файла, он не будет распознан как модуль. Формат этого файла следующий:

txt
id=<string>
-name=<string>
-version=<string>
-versionCode=<int>
-author=<string>
-description=<string>
  • id должно соответствовать данному регулярному выражению: ^[a-zA-Z][a-zA-Z0-9._-]+$
    экс: ✓ a_module, ✓ a.module, ✓ module-101, ✗ a module, ✗ 1_module, ✗ -a-module
    Это уникальный идентификатор вашего модуля. Не следует изменять его после публикации.
  • versionCode должен быть целым. Это используется для сравнения версий
  • Другими, не упомянутыми выше, могут быть любые однострочные строки.
  • Обязательно используйте тип перевода строки UNIX (LF), а не Windows (CR+LF) или Macintosh (CR).

Сценарии командной оболочки

Чтобы понять разницу между post-fs-data.sh и Service.sh, прочитайте раздел Boot Scripts. Для большинства разработчиков модулей service.sh должно быть достаточно, если вам нужно просто запустить загрузочный скрипт.

Во всех скриптах вашего модуля используйте MODDIR=${0%/*} для получения пути к базовому каталогу вашего модуля; НЕ кодируйте жестко путь к вашему модулю в скриптах.

различия с Magisk

С помощью переменной окружения KSU можно определить, выполняется ли сценарий в KernelSU или в Magisk. Если скрипт выполняется в KernelSU, то это значение будет равно true.

каталог system

После загрузки системы содержимое этого каталога будет наложено поверх раздела /system с помощью overlayfs. Это означает, что:

  1. Файлы с теми же именами, что и в соответствующем каталоге в системе, будут перезаписаны файлами в этом каталоге.
  2. Папки с теми же именами, что и в соответствующем каталоге в системе, будут объединены с папками в этом каталоге.

Если вы хотите удалить файл или папку в исходном каталоге системы, необходимо создать файл с тем же именем, что и файл/папка, в каталоге модуля с помощью команды mknod filename c 0 0. Таким образом, система overlayfs автоматически "забелит" этот файл, как если бы он был удален (раздел /system при этом фактически не изменится).

Вы также можете объявить в customize.sh переменную с именем REMOVE, содержащую список каталогов для выполнения операций удаления, и KernelSU автоматически выполнит команду mknod <TARGET> c 0 0 в соответствующих каталогах модуля. Например:

sh
REMOVE="
-/system/app/YouTube
-/system/app/Bloatware
-"

В приведенном выше списке будут выполнены команды mknod $MODPATH/system/app/YouTuBe c 0 0 и mknod $MODPATH/system/app/Bloatware c 0 0; при этом /system/app/YouTube и /system/app/Bloatware будут удалены после вступления модуля в силу.

Если вы хотите заменить каталог в системе, то необходимо создать каталог с тем же путем в каталоге модуля, а затем установить для этого каталога атрибут setfattr -n trusted.overlay.opaque -v y <TARGET>. Таким образом, система overlayfs автоматически заменит соответствующий каталог в системе (без изменения раздела /system).

Вы можете объявить в файле customize.sh переменную с именем REPLACE, содержащую список заменяемых каталогов, и KernelSU автоматически выполнит соответствующие операции в каталоге вашего модуля. Например:

REPLACE=" /system/app/YouTube /system/app/Bloatware "

В этом списке будут автоматически созданы каталоги $MODPATH/system/app/YouTube и $MODPATH/system/app/Bloatware, а затем выполнены команды setfattr -n trusted.overlay.opaque -v y $MODPATH/system/app/YouTube и setfattr -n trusted.overlay.opaque -v y $MODPATH/system/app/Bloatware. После вступления модуля в силу каталоги /system/app/YouTube и /system/app/Bloatware будут заменены на пустые.

различия с Magisk

В KernelSU бессистемный механизм реализован через overlayfs ядра, а в Magisk в настоящее время используется магическое монтирование (bind mount). Эти два метода реализации имеют существенные различия, но конечная цель у них одна: модификация файлов /system без физического изменения раздела /system.

Если вы заинтересованы в использовании overlayfs, рекомендуется прочитать документацию по overlayfs ядра Linux.

system.prop

Этот файл имеет тот же формат, что и build.prop. Каждая строка состоит из [key]=[value].

sepolicy.rule

Если для вашего модуля требуются дополнительные патчи sepolicy, добавьте эти правила в данный файл. Каждая строка в этом файле будет рассматриваться как утверждение политики.

Установщик модулей

Инсталлятор модуля KernelSU - это модуль KernelSU, упакованный в zip-файл, который может быть прошит в APP-менеджере KernelSU. Простейший установщик модуля KernelSU - это просто модуль KernelSU, упакованный в zip-файл.

txt
module.zip
-
-├── customize.sh                       <--- (Необязательно, более подробно позже)
-│                                           Этот скрипт будет использоваться в update-binary
-├── ...
-├── ...  /* Остальные файлы модуля */
-

WARNING

Модуль KernelSU НЕ поддерживается для установки в пользовательское Recovery!!!

Персонализация

Если вам необходимо настроить процесс установки модуля, то в качестве опции вы можете создать в программе установки скрипт с именем customize.sh. Этот скрипт будет источником (не исполняться!) сценария установщика модуля после извлечения всех файлов и применения стандартных разрешений и secontext. Это очень удобно, если ваш модуль требует дополнительной настройки в зависимости от ABI устройства, или вам необходимо установить специальные разрешения/секонтекст для некоторых файлов модуля.

Если вы хотите полностью контролировать и настраивать процесс установки, объявите SKIPUNZIP=1 в файле customize.sh, чтобы пропустить все шаги установки по умолчанию. При этом ваш customize.sh будет сам отвечать за установку.

Сценарий customize.sh запускается в оболочке BusyBox ash KernelSU с включенным "Автономным режимом". Доступны следующие переменные и функции:

Переменные

  • KSU (bool): переменная, отмечающая, что скрипт выполняется в окружении KernelSU, причем значение этой переменной всегда будет true. Ее можно использовать для различения KernelSU и Magisk.
  • KSU_VER (string): строка версии текущего установленного KernelSU (например, v0.4.0)
  • KSU_VER_CODE (int): код версии текущего установленного KernelSU в пользовательском пространстве (например, 10672)
  • KSU_KERNEL_VER_CODE (int): код версии текущей установленной KernelSU в пространстве ядра (например, 10672)
  • BOOTMODE (bool): в KernelSU всегда должно быть true.
  • MODPATH (path): путь, по которому должны быть установлены файлы ваших модулей
  • TMPDIR (path): место, где вы можете временно хранить файлы
  • ZIPFILE (path): установочный zip-архив вашего модуля
  • ARCH (string): архитектура процессора устройства. Значение: arm, arm64, x86 или x64.
  • IS64BIT (bool): true, если $ARCH имеет значение arm64 или x64.
  • API (int): уровень API (версия Android) устройства (например, 23 для Android 6.0)

WARNING

В KernelSU MAGISK_VER_CODE всегда равен 25200, а MAGISK_VER всегда равен v25.2. Пожалуйста, не используйте эти две переменные для определения того, запущен ли он на KernelSU или нет.

Функции

txt
ui_print <msg>
-    вывести <msg> на консоль
-    Избегайте использования 'echo', так как он не будет отображаться в консоли пользовательского recovery
-
-abort <msg>
-    вывести сообщение об ошибке <msg> на консоль и завершить установку
-    Избегайте использования команды 'exit', так как в этом случае будут пропущены шаги очистки завершения установки
-
-set_perm <target> <owner> <group> <permission> [context]
-    если [context] не задан, то по умолчанию используется "u:object_r:system_file:s0".
-    Эта функция является сокращением для следующих команд:
-       chown owner.group target
-       chmod permission target
-       chcon context target
-
-set_perm_recursive <directory> <owner> <group> <dirpermission> <filepermission> [context]
-    если [context] не задан, то по умолчанию используется "u:object_r:system_file:s0".
-    для всех файлов в <directory> будет вызвана команда:
-       set_perm file owner group filepermission context
-    для всех каталогов в <directory> (включая себя самого), он вызовет:
-       set_perm dir owner group dirpermission context

Загрузочные сценарии

В KernelSU скрипты делятся на два типа в зависимости от режима их работы: режим post-fs-data и режим late_start service:

  • режим post-fs-data
    • Эта стадия является БЛОКИРУЮЩЕЙ. Процесс загрузки приостанавливается до завершения выполнения или по истечении 10 секунд.
    • Сценарии запускаются до того, как будут смонтированы какие-либо модули. Это позволяет разработчику модулей динамически настраивать свои модули до того, как они будут смонтированы.
    • Этот этап происходит до запуска Zygote, что практически означает, что все в Android
    • ПРЕДУПРЕЖДЕНИЕ: использование setprop приведет к блокировке процесса загрузки! Вместо этого используйте resetprop -n <prop_name> <prop_value>.
    • Запускайте скрипты в этом режиме только в случае необходимости.
  • режим обслуживания late_start
    • Эта стадия является НЕБЛОКИРУЮЩЕЙ. Ваш скрипт выполняется параллельно с остальным процессом загрузки.
    • Это рекомендуемый этап для запуска большинства скриптов.

В KernelSU скрипты запуска делятся на два типа по месту их хранения: общие скрипты и скрипты модулей:

  • Общие скрипты
    • Размещаются в файлах /data/adb/post-fs-data.d или /data/adb/service.d.
    • Выполняется только в том случае, если скрипт установлен как исполняемый (chmod +x script.sh)
    • Скрипты в post-fs-data.d выполняются в режиме post-fs-data, а скрипты в service.d - в режиме late_start service.
    • Модули не должны НЕ добавлять общие скрипты при установке
  • Скрипты модуля
    • Размещаются в отдельной папке модуля
    • Выполняются только в том случае, если модуль включен
    • post-fs-data.sh запускается в режиме post-fs-data, а service.sh - в режиме late_start service.

Все загрузочные скрипты будут выполняться в оболочке BusyBox ash от KernelSU с включенным "Автономным режимом".

Выпускается под лицензией GPL3.

- +
Skip to content

Руководство по разработке модулей

KernelSU предоставляет механизм модулей, позволяющий добиться эффекта модификации системного каталога при сохранении целостности системного раздела. Этот механизм принято называть "бессистемным".

Модульный механизм KernelSU практически аналогичен механизму Magisk. Если вы знакомы с разработкой модулей Magisk, то разработка модулей KernelSU очень похожа. Представление модулей ниже можно пропустить, достаточно прочитать [различия-с-magisk] (difference-with-magisk.md).

Busybox

В комплект поставки KernelSU входит полнофункциональный бинарный файл BusyBox (включая полную поддержку SELinux). Исполняемый файл находится по адресу /data/adb/ksu/bin/busybox. BusyBox от KernelSU поддерживает переключаемый во время работы "ASH Standalone Shell Mode". Этот автономный режим означает, что при запуске в оболочке ash BusyBox каждая команда будет напрямую использовать апплет внутри BusyBox, независимо от того, что задано в качестве PATH. Например, такие команды, как ls, rm, chmod будут НЕ использовать то, что находится в PATH (в случае Android по умолчанию это будут /system/bin/ls, /system/bin/rm и /system/bin/chmod соответственно), а вместо этого будут напрямую вызывать внутренние апплеты BusyBox. Это гарантирует, что скрипты всегда будут выполняться в предсказуемом окружении и всегда будут иметь полный набор команд, независимо от того, на какой версии Android они выполняются. Чтобы заставить команду не использовать BusyBox, необходимо вызвать исполняемый файл с полными путями.

Каждый сценарий оболочки, запущенный в контексте KernelSU, будет выполняться в оболочке BusyBox ash с включенным автономным режимом. Для сторонних разработчиков это касается всех загрузочных скриптов и скриптов установки модулей.

Для тех, кто хочет использовать эту возможность "Автономного режима" вне KernelSU, есть два способа включить ее:

  1. Установите переменной окружения ASH_STANDALONE значение 1
    Пример: ASH_STANDALONE=1 /data/adb/ksu/bin/busybox sh <script>
  2. Переключитесь с помощью параметров командной строки:
    /data/adb/ksu/bin/busybox sh -o standalone <script>

Чтобы убедиться, что все последующие запуски оболочки sh также выполняются в автономном режиме, предпочтительным методом является вариант 1 (и это то, что KernelSU и менеджер KernelSU используют внутри), поскольку переменные окружения наследуются вплоть до дочерних процессов.

отличие от Magisk

BusyBox в KernelSU теперь использует бинарный файл, скомпилированный непосредственно из проекта Magisk. **Поэтому вам не нужно беспокоиться о проблемах совместимости между скриптами BusyBox в Magisk и KernelSU, поскольку они абсолютно одинаковы!

Модули KernelSU

Модуль KernelSU - это папка, размещенная в каталоге /data/adb/modules и имеющая следующую структуру:

txt
/data/adb/modules
+├── .
+├── .
+|
+├── $MODID                  <--- Папка имеет имя с идентификатором модуля
+│   │
+│   │      *** Идентификация модуля ***
+│   │
+│   ├── module.prop         <--- В этом файле хранятся метаданные модуля
+│   │
+│   │      *** Основное содержимое ***
+│   │
+│   ├── system              <--- Эта папка будет смонтирована, если skip_mount не существует
+│   │   ├── ...
+│   │   ├── ...
+│   │   └── ...
+│   │
+│   │      *** Флаги состояния ***
+│   │
+│   ├── skip_mount          <--- Если он существует, то KernelSU НЕ будет монтировать вашу системную папку
+│   ├── disable             <--- Если модуль существует, то он будет отключен
+│   ├── remove              <--- Если модуль существует, то при следующей перезагрузке он будет удален
+│   │
+│   │      *** Необязательные файлы ***
+│   │
+│   ├── post-fs-data.sh     <--- Этот скрипт будет выполняться в post-fs-data
+│   ├── service.sh          <--- Этот скрипт будет выполняться в сервисе late_start
+|   ├── uninstall.sh        <--- Этот скрипт будет выполнен, когда KernelSU удалит ваш модуль
+│   ├── system.prop         <--- Свойства из этого файла будут загружены в качестве системных свойств программой resetprop
+│   ├── sepolicy.rule       <--- Дополнительные пользовательские правила sepolicy
+│   │
+│   │      *** Автоматически генерируется, НЕЛЬЗЯ создавать или изменять вручную ***
+│   │
+│   ├── vendor              <--- Символьная ссылка на $MODID/system/vendor
+│   ├── product             <--- Символьная ссылка на $MODID/system/product
+│   ├── system_ext          <--- Симлинк на $MODID/system/system_ext
+│   │
+│   │      *** Допускается использование любых дополнительных файлов/папок ***
+│   │
+│   ├── ...
+│   └── ...
+|
+├── another_module
+│   ├── .
+│   └── .
+├── .
+├── .
/data/adb/modules
+├── .
+├── .
+|
+├── $MODID                  <--- Папка имеет имя с идентификатором модуля
+│   │
+│   │      *** Идентификация модуля ***
+│   │
+│   ├── module.prop         <--- В этом файле хранятся метаданные модуля
+│   │
+│   │      *** Основное содержимое ***
+│   │
+│   ├── system              <--- Эта папка будет смонтирована, если skip_mount не существует
+│   │   ├── ...
+│   │   ├── ...
+│   │   └── ...
+│   │
+│   │      *** Флаги состояния ***
+│   │
+│   ├── skip_mount          <--- Если он существует, то KernelSU НЕ будет монтировать вашу системную папку
+│   ├── disable             <--- Если модуль существует, то он будет отключен
+│   ├── remove              <--- Если модуль существует, то при следующей перезагрузке он будет удален
+│   │
+│   │      *** Необязательные файлы ***
+│   │
+│   ├── post-fs-data.sh     <--- Этот скрипт будет выполняться в post-fs-data
+│   ├── service.sh          <--- Этот скрипт будет выполняться в сервисе late_start
+|   ├── uninstall.sh        <--- Этот скрипт будет выполнен, когда KernelSU удалит ваш модуль
+│   ├── system.prop         <--- Свойства из этого файла будут загружены в качестве системных свойств программой resetprop
+│   ├── sepolicy.rule       <--- Дополнительные пользовательские правила sepolicy
+│   │
+│   │      *** Автоматически генерируется, НЕЛЬЗЯ создавать или изменять вручную ***
+│   │
+│   ├── vendor              <--- Символьная ссылка на $MODID/system/vendor
+│   ├── product             <--- Символьная ссылка на $MODID/system/product
+│   ├── system_ext          <--- Симлинк на $MODID/system/system_ext
+│   │
+│   │      *** Допускается использование любых дополнительных файлов/папок ***
+│   │
+│   ├── ...
+│   └── ...
+|
+├── another_module
+│   ├── .
+│   └── .
+├── .
+├── .

различия с Magisk

KernelSU не имеет встроенной поддержки Zygisk, поэтому в модуле нет содержимого, связанного с Zygisk. Однако для поддержки модулей Zygisk можно использовать ZygiskNext. В этом случае содержимое модуля Zygisk идентично содержимому, поддерживаемому Magisk.

module.prop

module.prop - это конфигурационный файл модуля. В KernelSU, если модуль не содержит этого файла, он не будет распознан как модуль. Формат этого файла следующий:

txt
id=<string>
+name=<string>
+version=<string>
+versionCode=<int>
+author=<string>
+description=<string>
id=<string>
+name=<string>
+version=<string>
+versionCode=<int>
+author=<string>
+description=<string>
  • id должно соответствовать данному регулярному выражению: ^[a-zA-Z][a-zA-Z0-9._-]+$
    экс: ✓ a_module, ✓ a.module, ✓ module-101, ✗ a module, ✗ 1_module, ✗ -a-module
    Это уникальный идентификатор вашего модуля. Не следует изменять его после публикации.
  • versionCode должен быть целым. Это используется для сравнения версий
  • Другими, не упомянутыми выше, могут быть любые однострочные строки.
  • Обязательно используйте тип перевода строки UNIX (LF), а не Windows (CR+LF) или Macintosh (CR).

Сценарии командной оболочки

Чтобы понять разницу между post-fs-data.sh и Service.sh, прочитайте раздел Boot Scripts. Для большинства разработчиков модулей service.sh должно быть достаточно, если вам нужно просто запустить загрузочный скрипт.

Во всех скриптах вашего модуля используйте MODDIR=${0%/*} для получения пути к базовому каталогу вашего модуля; НЕ кодируйте жестко путь к вашему модулю в скриптах.

различия с Magisk

С помощью переменной окружения KSU можно определить, выполняется ли сценарий в KernelSU или в Magisk. Если скрипт выполняется в KernelSU, то это значение будет равно true.

каталог system

После загрузки системы содержимое этого каталога будет наложено поверх раздела /system с помощью overlayfs. Это означает, что:

  1. Файлы с теми же именами, что и в соответствующем каталоге в системе, будут перезаписаны файлами в этом каталоге.
  2. Папки с теми же именами, что и в соответствующем каталоге в системе, будут объединены с папками в этом каталоге.

Если вы хотите удалить файл или папку в исходном каталоге системы, необходимо создать файл с тем же именем, что и файл/папка, в каталоге модуля с помощью команды mknod filename c 0 0. Таким образом, система overlayfs автоматически "забелит" этот файл, как если бы он был удален (раздел /system при этом фактически не изменится).

Вы также можете объявить в customize.sh переменную с именем REMOVE, содержащую список каталогов для выполнения операций удаления, и KernelSU автоматически выполнит команду mknod <TARGET> c 0 0 в соответствующих каталогах модуля. Например:

sh
REMOVE="
+/system/app/YouTube
+/system/app/Bloatware
+"
REMOVE="
+/system/app/YouTube
+/system/app/Bloatware
+"

В приведенном выше списке будут выполнены команды mknod $MODPATH/system/app/YouTuBe c 0 0 и mknod $MODPATH/system/app/Bloatware c 0 0; при этом /system/app/YouTube и /system/app/Bloatware будут удалены после вступления модуля в силу.

Если вы хотите заменить каталог в системе, то необходимо создать каталог с тем же путем в каталоге модуля, а затем установить для этого каталога атрибут setfattr -n trusted.overlay.opaque -v y <TARGET>. Таким образом, система overlayfs автоматически заменит соответствующий каталог в системе (без изменения раздела /system).

Вы можете объявить в файле customize.sh переменную с именем REPLACE, содержащую список заменяемых каталогов, и KernelSU автоматически выполнит соответствующие операции в каталоге вашего модуля. Например:

REPLACE=" /system/app/YouTube /system/app/Bloatware "

В этом списке будут автоматически созданы каталоги $MODPATH/system/app/YouTube и $MODPATH/system/app/Bloatware, а затем выполнены команды setfattr -n trusted.overlay.opaque -v y $MODPATH/system/app/YouTube и setfattr -n trusted.overlay.opaque -v y $MODPATH/system/app/Bloatware. После вступления модуля в силу каталоги /system/app/YouTube и /system/app/Bloatware будут заменены на пустые.

различия с Magisk

В KernelSU бессистемный механизм реализован через overlayfs ядра, а в Magisk в настоящее время используется магическое монтирование (bind mount). Эти два метода реализации имеют существенные различия, но конечная цель у них одна: модификация файлов /system без физического изменения раздела /system.

Если вы заинтересованы в использовании overlayfs, рекомендуется прочитать документацию по overlayfs ядра Linux.

system.prop

Этот файл имеет тот же формат, что и build.prop. Каждая строка состоит из [key]=[value].

sepolicy.rule

Если для вашего модуля требуются дополнительные патчи sepolicy, добавьте эти правила в данный файл. Каждая строка в этом файле будет рассматриваться как утверждение политики.

Установщик модулей

Инсталлятор модуля KernelSU - это модуль KernelSU, упакованный в zip-файл, который может быть прошит в APP-менеджере KernelSU. Простейший установщик модуля KernelSU - это просто модуль KernelSU, упакованный в zip-файл.

txt
module.zip
+
+├── customize.sh                       <--- (Необязательно, более подробно позже)
+│                                           Этот скрипт будет использоваться в update-binary
+├── ...
+├── ...  /* Остальные файлы модуля */
+
module.zip
+
+├── customize.sh                       <--- (Необязательно, более подробно позже)
+│                                           Этот скрипт будет использоваться в update-binary
+├── ...
+├── ...  /* Остальные файлы модуля */
+

WARNING

Модуль KernelSU НЕ поддерживается для установки в пользовательское Recovery!!!

Персонализация

Если вам необходимо настроить процесс установки модуля, то в качестве опции вы можете создать в программе установки скрипт с именем customize.sh. Этот скрипт будет источником (не исполняться!) сценария установщика модуля после извлечения всех файлов и применения стандартных разрешений и secontext. Это очень удобно, если ваш модуль требует дополнительной настройки в зависимости от ABI устройства, или вам необходимо установить специальные разрешения/секонтекст для некоторых файлов модуля.

Если вы хотите полностью контролировать и настраивать процесс установки, объявите SKIPUNZIP=1 в файле customize.sh, чтобы пропустить все шаги установки по умолчанию. При этом ваш customize.sh будет сам отвечать за установку.

Сценарий customize.sh запускается в оболочке BusyBox ash KernelSU с включенным "Автономным режимом". Доступны следующие переменные и функции:

Переменные

  • KSU (bool): переменная, отмечающая, что скрипт выполняется в окружении KernelSU, причем значение этой переменной всегда будет true. Ее можно использовать для различения KernelSU и Magisk.
  • KSU_VER (string): строка версии текущего установленного KernelSU (например, v0.4.0)
  • KSU_VER_CODE (int): код версии текущего установленного KernelSU в пользовательском пространстве (например, 10672)
  • KSU_KERNEL_VER_CODE (int): код версии текущей установленной KernelSU в пространстве ядра (например, 10672)
  • BOOTMODE (bool): в KernelSU всегда должно быть true.
  • MODPATH (path): путь, по которому должны быть установлены файлы ваших модулей
  • TMPDIR (path): место, где вы можете временно хранить файлы
  • ZIPFILE (path): установочный zip-архив вашего модуля
  • ARCH (string): архитектура процессора устройства. Значение: arm, arm64, x86 или x64.
  • IS64BIT (bool): true, если $ARCH имеет значение arm64 или x64.
  • API (int): уровень API (версия Android) устройства (например, 23 для Android 6.0)

WARNING

В KernelSU MAGISK_VER_CODE всегда равен 25200, а MAGISK_VER всегда равен v25.2. Пожалуйста, не используйте эти две переменные для определения того, запущен ли он на KernelSU или нет.

Функции

txt
ui_print <msg>
+    вывести <msg> на консоль
+    Избегайте использования 'echo', так как он не будет отображаться в консоли пользовательского recovery
+
+abort <msg>
+    вывести сообщение об ошибке <msg> на консоль и завершить установку
+    Избегайте использования команды 'exit', так как в этом случае будут пропущены шаги очистки завершения установки
+
+set_perm <target> <owner> <group> <permission> [context]
+    если [context] не задан, то по умолчанию используется "u:object_r:system_file:s0".
+    Эта функция является сокращением для следующих команд:
+       chown owner.group target
+       chmod permission target
+       chcon context target
+
+set_perm_recursive <directory> <owner> <group> <dirpermission> <filepermission> [context]
+    если [context] не задан, то по умолчанию используется "u:object_r:system_file:s0".
+    для всех файлов в <directory> будет вызвана команда:
+       set_perm file owner group filepermission context
+    для всех каталогов в <directory> (включая себя самого), он вызовет:
+       set_perm dir owner group dirpermission context
ui_print <msg>
+    вывести <msg> на консоль
+    Избегайте использования 'echo', так как он не будет отображаться в консоли пользовательского recovery
+
+abort <msg>
+    вывести сообщение об ошибке <msg> на консоль и завершить установку
+    Избегайте использования команды 'exit', так как в этом случае будут пропущены шаги очистки завершения установки
+
+set_perm <target> <owner> <group> <permission> [context]
+    если [context] не задан, то по умолчанию используется "u:object_r:system_file:s0".
+    Эта функция является сокращением для следующих команд:
+       chown owner.group target
+       chmod permission target
+       chcon context target
+
+set_perm_recursive <directory> <owner> <group> <dirpermission> <filepermission> [context]
+    если [context] не задан, то по умолчанию используется "u:object_r:system_file:s0".
+    для всех файлов в <directory> будет вызвана команда:
+       set_perm file owner group filepermission context
+    для всех каталогов в <directory> (включая себя самого), он вызовет:
+       set_perm dir owner group dirpermission context

Загрузочные сценарии

В KernelSU скрипты делятся на два типа в зависимости от режима их работы: режим post-fs-data и режим late_start service:

  • режим post-fs-data
    • Эта стадия является БЛОКИРУЮЩЕЙ. Процесс загрузки приостанавливается до завершения выполнения или по истечении 10 секунд.
    • Сценарии запускаются до того, как будут смонтированы какие-либо модули. Это позволяет разработчику модулей динамически настраивать свои модули до того, как они будут смонтированы.
    • Этот этап происходит до запуска Zygote, что практически означает, что все в Android
    • ПРЕДУПРЕЖДЕНИЕ: использование setprop приведет к блокировке процесса загрузки! Вместо этого используйте resetprop -n <prop_name> <prop_value>.
    • Запускайте скрипты в этом режиме только в случае необходимости.
  • режим обслуживания late_start
    • Эта стадия является НЕБЛОКИРУЮЩЕЙ. Ваш скрипт выполняется параллельно с остальным процессом загрузки.
    • Это рекомендуемый этап для запуска большинства скриптов.

В KernelSU скрипты запуска делятся на два типа по месту их хранения: общие скрипты и скрипты модулей:

  • Общие скрипты
    • Размещаются в файлах /data/adb/post-fs-data.d или /data/adb/service.d.
    • Выполняется только в том случае, если скрипт установлен как исполняемый (chmod +x script.sh)
    • Скрипты в post-fs-data.d выполняются в режиме post-fs-data, а скрипты в service.d - в режиме late_start service.
    • Модули не должны НЕ добавлять общие скрипты при установке
  • Скрипты модуля
    • Размещаются в отдельной папке модуля
    • Выполняются только в том случае, если модуль включен
    • post-fs-data.sh запускается в режиме post-fs-data, а service.sh - в режиме late_start service.

Все загрузочные скрипты будут выполняться в оболочке BusyBox ash от KernelSU с включенным "Автономным режимом".

Выпускается под лицензией GPL3.

+ \ No newline at end of file diff --git a/ru_RU/guide/rescue-from-bootloop.html b/ru_RU/guide/rescue-from-bootloop.html index ad0fdf4f55d7..41562aa557c4 100644 --- a/ru_RU/guide/rescue-from-bootloop.html +++ b/ru_RU/guide/rescue-from-bootloop.html @@ -5,18 +5,19 @@ Выход из циклической загрузки | KernelSU - - + + + - - - - + + + + + -
Skip to content
On this page

Выход из циклической загрузки

При прошивке устройства могут возникать ситуации, когда устройство становится "окирпиченным". Теоретически, если использовать fastboot только для прошивки загрузочного раздела или установить неподходящие модули, из-за которых устройство не загружается, то это можно восстановить соответствующими операциями. В данном документе описаны некоторые экстренные методы восстановления работоспособности "окирпиченного" устройства.

Кирпич путем перепрошивки загрузочного раздела

В KernelSU при прошивке загрузочного раздела могут возникнуть следующие ситуации:

  1. Загрузочный образ прошивается в неправильном формате. Например, если формат загрузки телефона - gz, а вы прошили образ в формате lz4, то телефон не сможет загрузиться.
  2. Для корректной загрузки телефона необходимо отключить проверку AVB (обычно для этого требуется стереть все данные на телефоне).
  3. Ядро содержит ошибки или не подходит для прошивки телефона.

Независимо от ситуации, восстановить работоспособность можно путем прошивки стокового загрузочного образа. Поэтому в начале руководства по установке мы настоятельно рекомендуем создать резервную копию стокового загрузочного образа перед прошивкой. Если у вас нет резервной копии, вы можете получить оригинальную заводскую загрузку от других пользователей с таким же устройством, как у вас, или из официальной прошивки.

Окирпичивание из-за модулей

Установка модулей может быть более распространенной причиной окирпичивания устройства, но мы должны серьезно предупредить вас: Не устанавливайте модули из неизвестных источников! Поскольку модули обладают правами root, они могут нанести непоправимый ущерб вашему устройству!

Нормальные модули

Если вы прошили модуль, безопасность которого доказана, но он приводит к невозможности загрузки устройства, то такая ситуация легко восстанавливается в KernelSU без каких-либо проблем. KernelSU имеет встроенные механизмы для спасения устройства, в том числе следующие:

  1. Обновление AB
  2. Восстановление при нажатии клавиши уменьшения громкости

AB-обновление

Механизм обновления модулей в KernelSU основан на механизме AB-обновления, используемом в OTA-обновлениях системы Android. При установке нового модуля или обновлении существующего он не будет напрямую изменять текущий файл модуля. Вместо этого все модули будут встроены в другой образ обновления. После перезагрузки системы она попытается начать использовать этот образ обновления. Если система Android успешно загрузится, то модули будут действительно обновлены.

Поэтому самым простым и наиболее часто используемым методом спасения устройства является принудительная перезагрузка. Если после прошивки модуля не удается запустить систему, можно нажать и удерживать кнопку питания более 10 секунд, после чего система автоматически перезагрузится; после перезагрузки произойдет откат к состоянию до обновления модуля, а ранее обновленные модули будут автоматически отключены.

Спасение, с зажатой клавишей уменьшения громкости

Если обновление AB не помогло решить проблему, можно попробовать использовать Безопасный режим. В безопасном режиме все модули отключены.

Войти в безопасный режим можно двумя способами:

  1. Встроенный безопасный режим некоторых систем; некоторые системы имеют встроенный безопасный режим, доступ к которому осуществляется долгим нажатием кнопки уменьшения громкости, в то время как другие (например, MIUI) могут включить безопасный режим в Recovery. При входе в безопасный режим системы KernelSU также переходит в безопасный режим и автоматически отключает модули.
  2. Встроенный безопасный режим KernelSU; метод работы заключается в том, что после первого экрана загрузки необходимо непрерывно нажать клавишу уменьшения громкости более трех раз. Обратите внимание, что именно нажать-отпустить, нажать-отпустить, нажать-отпустить, а не нажать и удерживать.

После входа в безопасный режим все модули на странице модулей менеджера KernelSU Manager отключаются, но можно выполнить операцию "деинсталляция" для удаления модулей, которые могут вызывать проблемы.

Встроенный безопасный режим реализован в ядре, поэтому вероятность пропуска ключевых событий из-за перехвата исключена. Однако для ядер, отличных от ГКИ, может потребоваться ручная интеграция кода, и за рекомендациями можно обратиться к официальной документации.

Вредоносные модули

Если описанные выше способы не помогли спасти устройство, то высока вероятность того, что установленный модуль имеет вредоносные операции или повредил устройство иным способом. В этом случае есть только два варианта:

  1. Стереть данные и прошить официальную систему.
  2. Обратиться в сервисную службу.

Выпускается под лицензией GPL3.

- +
Skip to content

Выход из циклической загрузки

При прошивке устройства могут возникать ситуации, когда устройство становится "окирпиченным". Теоретически, если использовать fastboot только для прошивки загрузочного раздела или установить неподходящие модули, из-за которых устройство не загружается, то это можно восстановить соответствующими операциями. В данном документе описаны некоторые экстренные методы восстановления работоспособности "окирпиченного" устройства.

Кирпич путем перепрошивки загрузочного раздела

В KernelSU при прошивке загрузочного раздела могут возникнуть следующие ситуации:

  1. Загрузочный образ прошивается в неправильном формате. Например, если формат загрузки телефона - gz, а вы прошили образ в формате lz4, то телефон не сможет загрузиться.
  2. Для корректной загрузки телефона необходимо отключить проверку AVB (обычно для этого требуется стереть все данные на телефоне).
  3. Ядро содержит ошибки или не подходит для прошивки телефона.

Независимо от ситуации, восстановить работоспособность можно путем прошивки стокового загрузочного образа. Поэтому в начале руководства по установке мы настоятельно рекомендуем создать резервную копию стокового загрузочного образа перед прошивкой. Если у вас нет резервной копии, вы можете получить оригинальную заводскую загрузку от других пользователей с таким же устройством, как у вас, или из официальной прошивки.

Окирпичивание из-за модулей

Установка модулей может быть более распространенной причиной окирпичивания устройства, но мы должны серьезно предупредить вас: Не устанавливайте модули из неизвестных источников! Поскольку модули обладают правами root, они могут нанести непоправимый ущерб вашему устройству!

Нормальные модули

Если вы прошили модуль, безопасность которого доказана, но он приводит к невозможности загрузки устройства, то такая ситуация легко восстанавливается в KernelSU без каких-либо проблем. KernelSU имеет встроенные механизмы для спасения устройства, в том числе следующие:

  1. Обновление AB
  2. Восстановление при нажатии клавиши уменьшения громкости

AB-обновление

Механизм обновления модулей в KernelSU основан на механизме AB-обновления, используемом в OTA-обновлениях системы Android. При установке нового модуля или обновлении существующего он не будет напрямую изменять текущий файл модуля. Вместо этого все модули будут встроены в другой образ обновления. После перезагрузки системы она попытается начать использовать этот образ обновления. Если система Android успешно загрузится, то модули будут действительно обновлены.

Поэтому самым простым и наиболее часто используемым методом спасения устройства является принудительная перезагрузка. Если после прошивки модуля не удается запустить систему, можно нажать и удерживать кнопку питания более 10 секунд, после чего система автоматически перезагрузится; после перезагрузки произойдет откат к состоянию до обновления модуля, а ранее обновленные модули будут автоматически отключены.

Спасение, с зажатой клавишей уменьшения громкости

Если обновление AB не помогло решить проблему, можно попробовать использовать Безопасный режим. В безопасном режиме все модули отключены.

Войти в безопасный режим можно двумя способами:

  1. Встроенный безопасный режим некоторых систем; некоторые системы имеют встроенный безопасный режим, доступ к которому осуществляется долгим нажатием кнопки уменьшения громкости, в то время как другие (например, MIUI) могут включить безопасный режим в Recovery. При входе в безопасный режим системы KernelSU также переходит в безопасный режим и автоматически отключает модули.
  2. Встроенный безопасный режим KernelSU; метод работы заключается в том, что после первого экрана загрузки необходимо непрерывно нажать клавишу уменьшения громкости более трех раз. Обратите внимание, что именно нажать-отпустить, нажать-отпустить, нажать-отпустить, а не нажать и удерживать.

После входа в безопасный режим все модули на странице модулей менеджера KernelSU Manager отключаются, но можно выполнить операцию "деинсталляция" для удаления модулей, которые могут вызывать проблемы.

Встроенный безопасный режим реализован в ядре, поэтому вероятность пропуска ключевых событий из-за перехвата исключена. Однако для ядер, отличных от ГКИ, может потребоваться ручная интеграция кода, и за рекомендациями можно обратиться к официальной документации.

Вредоносные модули

Если описанные выше способы не помогли спасти устройство, то высока вероятность того, что установленный модуль имеет вредоносные операции или повредил устройство иным способом. В этом случае есть только два варианта:

  1. Стереть данные и прошить официальную систему.
  2. Обратиться в сервисную службу.

Выпускается под лицензией GPL3.

+ \ No newline at end of file diff --git a/ru_RU/guide/unofficially-support-devices.html b/ru_RU/guide/unofficially-support-devices.html index 5cc779bdc65e..e7766b951d27 100644 --- a/ru_RU/guide/unofficially-support-devices.html +++ b/ru_RU/guide/unofficially-support-devices.html @@ -5,19 +5,20 @@ Неофициально поддерживаемые устройства | KernelSU - - + + + - - - - - + + + + + + -
Skip to content
On this page

Неофициально поддерживаемые устройства

WARNING

На этой странице представлены ядра для устройств, не поддерживающих ГКИ и поддерживающих KernelSU, которые поддерживаются другими разработчиками.

WARNING

Эта страница предназначена только для поиска исходного кода, соответствующего вашему устройству, и НЕ означает, что исходный код был проверен разработчиками KernelSU. Вы должны использовать его на свой страх и риск.

СопровождающийРепозиторийПоддерживаемое устройство
diphonsD8G_Kernel_oxygenPoco F3: alioth | Poco F4: munch | MI10T/PRO: Apollo
diphonsD8G_Kernel_SM8150Poco X3 Pro: Vayu | Bhima
diphonsD8G_Kernel_SDM845Poco F1 | MI8 | MiMix2S
diphonsD8G_Kernel_SM8350Vili
diphonsD8G_Kernel_MarblePoco F5 | Redmi Note 12 Turbo | Marble
th1nhhdkandroid_kernel_sony_sm8250-kernelsuSony Xperia 1 II | Sony Xperia 5 II
akash07knexus_kernel_xiaomi_sm8250Poco F4: munch
HMTheBoy154Darkmatter-kernelGeneric x86_64 devices running Android-x86
Asuka-mioandroid_kernel_xiaomi_casMi 10 Ultra: cas
xiaoleGunMiku_kernel_xiaomi_waynewayne
YamazakuraHkernel_xiaomi_cepheuscepheus for pixel experience
SakuraNotStupidandroid_kernel_xiaomi_sdm710Xiaomi MI 8 SE(sirius/xmsirius)
Aquarius223(paper)android_kernel_xiaomi_msm8998MI 6 (sagit) and MIX 2 (chiron) for LineageOS
SlackerStateandroid_kernel_xiaomi_sm6150Redmi K30 4G (phoenix)
RooGhz720kernel_xiaomi_sm6150REDMI NOTE 10 PRO (sweet)
OnlyTomInSecondandroid_kernel_xiaomi_sdm845Mi 8 (dipper) for LineageOS
Rohail33RealKing KernelApollo(Redmi K30S Ultra/Mi 10T/Mi 10T Pro),Alioth(Redmi K40/POCO F3/Mi 11X),Munch(Redmi K40S/POCO F4), both MIUI and AOSP.
lateautumn233android_kernel_oneplus_sm8250OnePlus 8 Serials
Sreeshankar KNeverSettle KernelOnePlus Nord (avicii)
Molyuuneko_kernel_xiaomi_gauguinRedmi Note 9 Pro/ Mi 10T Lite/ Mi 10i
guh0613android_kernel_oppo_sm8150OPPO Reno Ace (OP4A89)
LeviMarvinandroid_kernel_xiaomi_aliothRedmi K40 / POCO F3
cibimokernel_xiaomi_raphael_ksuXiaomi Redmi K20 Pro / Mi 9T Pro (raphael)
EndCreditskernel_xiaomi_sm7250Redmi K30 5G ( picasso ) , Redmi K30i 5G ( picasso_48m)
msnxandroid-msm-coral-4.14-android13Pixel 4 XL
SoDebugkernel_xiaomi_raphaelXiaomi Redmi K20 Pro / Mi 9T Pro (raphael)
H1mJTkernel_realme_RMX1901Realme X (RMX1901/RMX1901CN)
SonalSingh18android_kernel_xiaomi_sm6250Miatoll [curtana, excalibur, gram, joyeuse]
RooGhz720kernel_xiaomi_lavenderRedmi Note 7 (Lavender)
JunASAKAkernel_google_msm-4.9_KernelSUGoogle Pixel 3a & 3a XL (sargo & bonito)
RooGhz720kernel_asus_X01BDAsus Zenfone Max Pro M1/M2
Evans Mikekernel_xiaomi_raphael_bool-xXiaomi Redmi K20 Pro / Mi 9T Pro (raphael)
easterNdayKSU_Thyme_BuildBotXiaomi 10S
tedomi2705kernel_xiaomi_sdm660Xiaomi Redmi Note 6 Pro (tulip)
RyuujiXandroid_kernel_asus_sdm660-4.19Asus Zenfone Max Pro M1 (X00TD)/ M2 (X01BD) (Linux 4.19)
liqidecgandroid_kernel_xiaomi_sdm710Mi 8 SE (sirius) (Linux 4.9)
Abdelhay-Aliandroid_kernel_huawei_hi6250Huawei P20 lite (hi6250) (Linux 4.9)
reocatandroid_kernel_nokia_sdm660_ksuNokia 6.1 (2018)
Tuan Anhkernel_nokia_msm8998Nokia 8 (Repartitioned)/8 Sirocco (NLA/A1N)
bggRGjQaUbCoEandroid_kernel_samsung_sm8250Samsung Galaxy S20+ 5G (y2q)
SOVIET-ANDROIDkernel_xiaomi_raphaelXK20 Pro Raphael DSP & A only SAR support
dabao1955android_kernel_OPPO_PEQM00MediaTek devices in the OPPO Reno series on ColorOS13.x
Aflaungosandroid_kernel_motorola_msm8998Moto G6 Plus (evert)
TheNoFacekernel_oneplus_msm8998OnePlus 5/5T (cheeseburger/dumpling)
Nipin NA (Joker-V2)Xcalibur kernel (violet)Xiaomi Redmi Note 7 Pro (violet)
Sr-Hankernel_xiaomi_mojitoRedmi Note 10 (Sunny/Mojito)
dabao1955android_kernel_OPPO_OP4ED5OPPO Reno6 on ColorOS11.x/ColorOS12.x,kernel version4.14.186+
rushiranpiseandroid_kernel_samsung_exynos9610_mintKernel 4.14.194 exynos9610 Non-GKI Device, Added KernelSu using manual method
CN-Scarspixel_experience_kernel_xiaomi_sm8250_kernelSUPixel Experience Kernel 4.19.282 for Xiaomi Redmi K40S / Xiaomi Poco F4 (munch)
exerMiyo TokuSony Tama (akari, apollo, aurora, akatsuki) (Linux 4.9)
likkaiQuicksilver KernelXiaomi 11 Lite 5G NE (lisa)
awakened1712android_kernel_samsung_exynos9820Samsung S10/N10
awakened1712android_kernel_oneplus_sm8350Oneplus 9/9Pro
siimsekLightning KernelXiaomi Redmi Note 8/8T (ginkgo/willow)
GiovanYCringekernel_a50Galaxy A50 (A505f,fn,g,gn,gt)
Asriadi Rahimandroid_kernel_google_wahooGoogle Pixel 2/2XL
Flameandroid_kernel_xiaomi_sm8250POCO F3/Redmi K40/Mi 11X (alioth)
DawfukFRkernel_oneplus_sm8250 (Stellaris-Kernel)Oneplus 8 (Instantnoodle), Oneplus 8T (Kebab), Oneplus 8 Pro (Instantnoodlep), Oneplus 9R (lemonades) [msm-4.19]
Tejas Singhkernel_xiaomi_ginkgo (Cuh Kernel)Xiaomi Redmi Note 8/8T (ginkgo/willow)
Coconutatandroid_kernel_xiaomi_ruby_expRedmi Note 12 Pro / Pro+ For MIUI 14
Coconutatandroid_kernel_xiaomi_sdm845_expXiaomi Mi 8 Pro(UD)[equuleus]/Explorer Edition[ursa]
Abdul Wahid Khankernel_xiaomi_begoniaRedmi Note 8 Pro (Begonia/Begoniain)
Abdul Wahid Khankernel_xiaomi_marbleXiaomi Poco F5 / Redmi Note 12 Turbo (Marble)
zfdx123kernel_xiaomi_aliothRedmi K40 (alioth)
wxt1221android_kernel_oneplus_sdm845Oneplus 6 (enchilada) | Oneplus 6T (fajita) with no Retrofit Dynamic Partitions
zharzinhooKernel-Oriente-GuampMoto g9 play
hc841NovaCore_kernelMoto g9 play
zlm324android_kernel_xiaomi_msm8998_ksua lineage kernel forked from LineageOS official repository, version 4.4.302, added ksu.
zlm324android_kernel_xiaomi_msm8953_ksuXiaomi 5X (tiffany)
Vincent4440android_kernel_xiaomi_sm8250Poco F4: munch
zharzinhooandroid_kernel_motorola_cebuMoto g9 power
tiandoufayaleandroid_kernel_samsung_sm8250Samsung Tab S7 WIFI(T870)
starmoeandroid_kernel_oneplus_msm8998Oneplus 5/5T, kernel version 4.4.302
supechickenkernel_xiaomi_sm8350 Xiaomi 11T Pro (vili)
Vaz15kkernelSU-A70qSamsung Galaxy A70 ( A705MN / A705FN )
whyakariandroid_kernel_xiaomi_ginkgonon-GKI ginkgo
wulan17android_kernel_xiaomi_mt6765Redmi 6/6A (cereus/cactus)
officialputuidandroid_kernel_realme_mt6785Realme 6/6i(Indian)/6s/7/Narzo/Narzo 20 Pro/Narzo 30 4G (RM6785)

Выпускается под лицензией GPL3.

- +
Skip to content

Неофициально поддерживаемые устройства

WARNING

На этой странице представлены ядра для устройств, не поддерживающих ГКИ и поддерживающих KernelSU, которые поддерживаются другими разработчиками.

WARNING

Эта страница предназначена только для поиска исходного кода, соответствующего вашему устройству, и НЕ означает, что исходный код был проверен разработчиками KernelSU. Вы должны использовать его на свой страх и риск.

СопровождающийРепозиторийПоддерживаемое устройство
diphonsD8G_Kernel_oxygenPoco F3: alioth | Poco F4: munch | MI10T/PRO: Apollo
diphonsD8G_Kernel_SM8150Poco X3 Pro: Vayu | Bhima
diphonsD8G_Kernel_SDM845Poco F1 | MI8 | MiMix2S
diphonsD8G_Kernel_SM8350Vili
diphonsD8G_Kernel_MarblePoco F5 | Redmi Note 12 Turbo | Marble
th1nhhdkandroid_kernel_sony_sm8250-kernelsuSony Xperia 1 II | Sony Xperia 5 II
akash07knexus_kernel_xiaomi_sm8250Poco F4: munch
HMTheBoy154Darkmatter-kernelGeneric x86_64 devices running Android-x86
Asuka-mioandroid_kernel_xiaomi_casMi 10 Ultra: cas
xiaoleGunMiku_kernel_xiaomi_waynewayne
YamazakuraHkernel_xiaomi_cepheuscepheus for pixel experience
SakuraNotStupidandroid_kernel_xiaomi_sdm710Xiaomi MI 8 SE(sirius/xmsirius)
Aquarius223(paper)android_kernel_xiaomi_msm8998MI 6 (sagit) and MIX 2 (chiron) for LineageOS
SlackerStateandroid_kernel_xiaomi_sm6150Redmi K30 4G (phoenix)
RooGhz720kernel_xiaomi_sm6150REDMI NOTE 10 PRO (sweet)
OnlyTomInSecondandroid_kernel_xiaomi_sdm845Mi 8 (dipper) for LineageOS
Rohail33RealKing KernelApollo(Redmi K30S Ultra/Mi 10T/Mi 10T Pro),Alioth(Redmi K40/POCO F3/Mi 11X),Munch(Redmi K40S/POCO F4), both MIUI and AOSP.
lateautumn233android_kernel_oneplus_sm8250OnePlus 8 Serials
Sreeshankar KNeverSettle KernelOnePlus Nord (avicii)
Molyuuneko_kernel_xiaomi_gauguinRedmi Note 9 Pro/ Mi 10T Lite/ Mi 10i
guh0613android_kernel_oppo_sm8150OPPO Reno Ace (OP4A89)
LeviMarvinandroid_kernel_xiaomi_aliothRedmi K40 / POCO F3
cibimokernel_xiaomi_raphael_ksuXiaomi Redmi K20 Pro / Mi 9T Pro (raphael)
EndCreditskernel_xiaomi_sm7250Redmi K30 5G ( picasso ) , Redmi K30i 5G ( picasso_48m)
msnxandroid-msm-coral-4.14-android13Pixel 4 XL
SoDebugkernel_xiaomi_raphaelXiaomi Redmi K20 Pro / Mi 9T Pro (raphael)
H1mJTkernel_realme_RMX1901Realme X (RMX1901/RMX1901CN)
SonalSingh18android_kernel_xiaomi_sm6250Miatoll [curtana, excalibur, gram, joyeuse]
RooGhz720kernel_xiaomi_lavenderRedmi Note 7 (Lavender)
JunASAKAkernel_google_msm-4.9_KernelSUGoogle Pixel 3a & 3a XL (sargo & bonito)
RooGhz720kernel_asus_X01BDAsus Zenfone Max Pro M1/M2
Evans Mikekernel_xiaomi_raphael_bool-xXiaomi Redmi K20 Pro / Mi 9T Pro (raphael)
easterNdayKSU_Thyme_BuildBotXiaomi 10S
tedomi2705kernel_xiaomi_sdm660Xiaomi Redmi Note 6 Pro (tulip)
RyuujiXandroid_kernel_asus_sdm660-4.19Asus Zenfone Max Pro M1 (X00TD)/ M2 (X01BD) (Linux 4.19)
liqidecgandroid_kernel_xiaomi_sdm710Mi 8 SE (sirius) (Linux 4.9)
Abdelhay-Aliandroid_kernel_huawei_hi6250Huawei P20 lite (hi6250) (Linux 4.9)
reocatandroid_kernel_nokia_sdm660_ksuNokia 6.1 (2018)
Tuan Anhkernel_nokia_msm8998Nokia 8 (Repartitioned)/8 Sirocco (NLA/A1N)
bggRGjQaUbCoEandroid_kernel_samsung_sm8250Samsung Galaxy S20+ 5G (y2q)
SOVIET-ANDROIDkernel_xiaomi_raphaelXK20 Pro Raphael DSP & A only SAR support
dabao1955android_kernel_OPPO_PEQM00MediaTek devices in the OPPO Reno series on ColorOS13.x
Aflaungosandroid_kernel_motorola_msm8998Moto G6 Plus (evert)
TheNoFacekernel_oneplus_msm8998OnePlus 5/5T (cheeseburger/dumpling)
Nipin NA (Joker-V2)Xcalibur kernel (violet)Xiaomi Redmi Note 7 Pro (violet)
Sr-Hankernel_xiaomi_mojitoRedmi Note 10 (Sunny/Mojito)
dabao1955android_kernel_OPPO_OP4ED5OPPO Reno6 on ColorOS11.x/ColorOS12.x,kernel version4.14.186+
rushiranpiseandroid_kernel_samsung_exynos9610_mintKernel 4.14.194 exynos9610 Non-GKI Device, Added KernelSu using manual method
CN-Scarspixel_experience_kernel_xiaomi_sm8250_kernelSUPixel Experience Kernel 4.19.282 for Xiaomi Redmi K40S / Xiaomi Poco F4 (munch)
exerMiyo TokuSony Tama (akari, apollo, aurora, akatsuki) (Linux 4.9)
likkaiQuicksilver KernelXiaomi 11 Lite 5G NE (lisa)
awakened1712android_kernel_samsung_exynos9820Samsung S10/N10
awakened1712android_kernel_oneplus_sm8350Oneplus 9/9Pro
siimsekLightning KernelXiaomi Redmi Note 8/8T (ginkgo/willow)
GiovanYCringekernel_a50Galaxy A50 (A505f,fn,g,gn,gt)
Asriadi Rahimandroid_kernel_google_wahooGoogle Pixel 2/2XL
Flameandroid_kernel_xiaomi_sm8250POCO F3/Redmi K40/Mi 11X (alioth)
DawfukFRkernel_oneplus_sm8250 (Stellaris-Kernel)Oneplus 8 (Instantnoodle), Oneplus 8T (Kebab), Oneplus 8 Pro (Instantnoodlep), Oneplus 9R (lemonades) [msm-4.19]
Tejas Singhkernel_xiaomi_ginkgo (Cuh Kernel)Xiaomi Redmi Note 8/8T (ginkgo/willow)
Coconutatandroid_kernel_xiaomi_ruby_expRedmi Note 12 Pro / Pro+ For MIUI 14
Coconutatandroid_kernel_xiaomi_sdm845_expXiaomi Mi 8 Pro(UD)[equuleus]/Explorer Edition[ursa]
Abdul Wahid Khankernel_xiaomi_begoniaRedmi Note 8 Pro (Begonia/Begoniain)
Abdul Wahid Khankernel_xiaomi_marbleXiaomi Poco F5 / Redmi Note 12 Turbo (Marble)
zfdx123kernel_xiaomi_aliothRedmi K40 (alioth)
wxt1221android_kernel_oneplus_sdm845Oneplus 6 (enchilada) | Oneplus 6T (fajita) with no Retrofit Dynamic Partitions
zharzinhooKernel-Oriente-GuampMoto g9 play
hc841NovaCore_kernelMoto g9 play
zlm324android_kernel_xiaomi_msm8998_ksua lineage kernel forked from LineageOS official repository, version 4.4.302, added ksu.
zlm324android_kernel_xiaomi_msm8953_ksuXiaomi 5X (tiffany)
Vincent4440android_kernel_xiaomi_sm8250Poco F4: munch
zharzinhooandroid_kernel_motorola_cebuMoto g9 power
tiandoufayaleandroid_kernel_samsung_sm8250Samsung Tab S7 WIFI(T870)
starmoeandroid_kernel_oneplus_msm8998Oneplus 5/5T, kernel version 4.4.302
supechickenkernel_xiaomi_sm8350 Xiaomi 11T Pro (vili)
Vaz15kkernelSU-A70qSamsung Galaxy A70 ( A705MN / A705FN )
whyakariandroid_kernel_xiaomi_ginkgonon-GKI ginkgo
wulan17android_kernel_xiaomi_mt6765Redmi 6/6A (cereus/cactus)
officialputuidandroid_kernel_realme_mt6785Realme 6/6i(Indian)/6s/7/Narzo/Narzo 20 Pro/Narzo 30 4G (RM6785)

Выпускается под лицензией GPL3.

+ \ No newline at end of file diff --git a/ru_RU/guide/what-is-kernelsu.html b/ru_RU/guide/what-is-kernelsu.html index d6985c24059c..1bd180b1b06d 100644 --- a/ru_RU/guide/what-is-kernelsu.html +++ b/ru_RU/guide/what-is-kernelsu.html @@ -5,18 +5,19 @@ Что такое KernelSU? | KernelSU - - + + + - - - - + + + + + -
Skip to content
On this page

Что такое KernelSU?

KernelSU - это root-решение для устройств Android GKI, работающее в режиме ядра и предоставляющее root-права пользовательским приложениям непосредственно в пространстве ядра.

Особенности

Основной особенностью KernelSU является то, что он основан на ядре. KernelSU работает в режиме ядра, поэтому он может предоставить интерфейс ядра, которого раньше не было. Например, мы можем добавить аппаратную точку останова любому процессу в режиме ядра; мы можем получить доступ к физической памяти любого процесса без чьего-либо ведома; мы можем перехватить любой syscall в пространстве ядра; и т.д.

Кроме того, KernelSU предоставляет систему модулей через overlayfs, что позволяет загружать в систему пользовательские плагины. Также предусмотрен механизм модификации файлов в разделе /system.

Как использовать

Пожалуйста, обратитесь к: Установка

Как собрать

Как собрать

Обсуждение

Выпускается под лицензией GPL3.

- +
Skip to content

Что такое KernelSU?

KernelSU - это root-решение для устройств Android GKI, работающее в режиме ядра и предоставляющее root-права пользовательским приложениям непосредственно в пространстве ядра.

Особенности

Основной особенностью KernelSU является то, что он основан на ядре. KernelSU работает в режиме ядра, поэтому он может предоставить интерфейс ядра, которого раньше не было. Например, мы можем добавить аппаратную точку останова любому процессу в режиме ядра; мы можем получить доступ к физической памяти любого процесса без чьего-либо ведома; мы можем перехватить любой syscall в пространстве ядра; и т.д.

Кроме того, KernelSU предоставляет систему модулей через overlayfs, что позволяет загружать в систему пользовательские плагины. Также предусмотрен механизм модификации файлов в разделе /system.

Как использовать

Пожалуйста, обратитесь к: Установка

Как собрать

Как собрать

Обсуждение

Выпускается под лицензией GPL3.

+ \ No newline at end of file diff --git a/ru_RU/index.html b/ru_RU/index.html index 0b9e9cd6749a..38a8a4d98ca1 100644 --- a/ru_RU/index.html +++ b/ru_RU/index.html @@ -5,18 +5,19 @@ Основанное на ядре root-решение для Android | KernelSU - - + + + - - - - + + + + + -
Skip to content

KernelSU

Основанное на ядре root-решение для Android

KernelSU

Основанный на ядре

KernelSU работает в режиме ядра Linux, он имеет больше контроля над пользовательскими приложениями.

Контроль доступа по белому списку

Только приложение, которому предоставлено разрешение root, может получить доступ к `su`, другие приложения не могут воспринимать su.

Ограниченные root-права

KernelSU позволяет вам настраивать uid, gid, группы, возможности и правила SELinux для su. Заприте root-власть в клетке.

Модульность и открытый исходный код

KernelSU поддерживает модификацию /system бессистемно с помощью overlayfs и имеет открытый исходный код под лицензией GPL-3.

Выпускается под лицензией GPL3.

- +
Skip to content

KernelSU

Основанное на ядре root-решение для Android

KernelSU

Выпускается под лицензией GPL3.

+ \ No newline at end of file diff --git a/vi_VN/guide/app-profile.html b/vi_VN/guide/app-profile.html index 9b617b5e60af..3c053d904ef7 100644 --- a/vi_VN/guide/app-profile.html +++ b/vi_VN/guide/app-profile.html @@ -5,22 +5,27 @@ App Profile | KernelSU - - + + + - - - - + + + + + -
Skip to content
On this page

App Profile

App Profile là một cơ chế do KernelSU cung cấp để tùy chỉnh cấu hình của các ứng dụng khác nhau.

Đối với các ứng dụng được cấp quyền root (tức là có thể sử dụng su), App Profile cũng có thể được gọi là Root Profile. Nó cho phép tùy chỉnh các quy tắc uid, gid, groups, capabilitiesSELinux của lệnh su, do đó hạn chế các đặc quyền của người dùng root. Ví dụ: nó có thể chỉ cấp quyền mạng cho các ứng dụng tường lửa trong khi từ chối quyền truy cập tệp hoặc có thể cấp quyền shell thay vì quyền truy cập root đầy đủ cho các ứng dụng đóng băng: giữ nguồn điện theo nguyên tắc đặc quyền tối thiểu.

Đối với các ứng dụng thông thường không có quyền root, App Profile có thể kiểm soát hành vi của hệ thống kernel và mô-đun đối với các ứng dụng này. Ví dụ, nó có thể xác định liệu các sửa đổi do mô-đun tạo ra có nên được giải quyết hay không. Hệ thống kernel và mô-đun có thể đưa ra quyết định dựa trên cấu hình này, chẳng hạn như thực hiện các hoạt động tương tự như "hiding"

Root Profile

UID, GID, và Groups

Hệ thống Linux có hai khái niệm: người dùng (user) và nhóm (group). Mỗi người dùng có một user ID (UID) và một người dùng có thể thuộc nhiều nhóm, mỗi nhóm có group ID (GID) riêng. Những ID này được sử dụng để xác định người dùng trong hệ thống và xác định tài nguyên hệ thống nào họ có thể truy cập.

Người dùng có UID bằng 0 được gọi là người dùng root và các nhóm có GID bằng 0 được gọi là nhóm root. Nhóm người dùng root thường giữ các đặc quyền hệ thống cao nhất.

Trong trường hợp hệ thống Android, mỗi ứng dụng là một người dùng riêng biệt (không bao gồm các trường hợp UID dùng chung) với một UID duy nhất. Ví dụ: 0 đại diện cho người dùng root, 1000 đại diện cho system, 2000 đại diện cho ADB shell và các UID từ 10000 đến 19999 đại diện cho các ứng dụng thông thường.

INFO

Ở đây, UID được đề cập không giống với khái niệm nhiều người dùng hoặc hồ sơ công việc (Work profile) trong hệ thống Android. Hồ sơ công việc thực sự được triển khai bằng cách phân vùng phạm vi UID. Ví dụ: 10000-19999 đại diện cho người dùng chính, trong khi 110000-119999 đại diện cho hồ sơ công việc. Mỗi ứng dụng thông thường trong số đó đều có UID riêng.

Mỗi ứng dụng có thể có nhiều nhóm, với GID đại diện cho nhóm chính, thường khớp với UID. Các nhóm khác được gọi là nhóm bổ sung. Một số quyền nhất định được kiểm soát thông qua các nhóm, chẳng hạn như quyền truy cập mạng hoặc truy cập Bluetooth.

Ví dụ: nếu chúng ta thực thi lệnh id trong shell ADB, kết quả đầu ra có thể trông như thế này:

sh
oriole:/ $ id
-uid=2000(shell) gid=2000(shell) groups=2000(shell),1004(input),1007(log),1011(adb),1015(sdcard_rw),1028(sdcard_r),1078(ext_data_rw),1079(ext_obb_rw),3001(net_bt_admin),3002(net_bt),3003(inet),3006(net_bw_stats),3009(readproc),3011(uhid),3012(readtracefs) context=u:r:shell:s0

Ở đây, UID là 2000 và GID (ID nhóm chính) cũng là 2000. Ngoài ra, nó thuộc một số nhóm bổ sung, chẳng hạn như inet (biểu thị khả năng tạo ổ cắm AF_INETAF_INET6) và sdcard_rw (biểu thị quyền đọc/ghi đối với thẻ SD).

Root Profile của KernelSU cho phép tùy chỉnh UID, GID và các nhóm cho quy trình gốc sau khi thực thi su. Ví dụ: Cấu hình gốc của ứng dụng gốc có thể đặt UID của nó thành 2000, có nghĩa là khi sử dụng su, các quyền thực tế của ứng dụng sẽ ở cấp shell ADB. Nhóm inet có thể bị xóa, ngăn lệnh su truy cập mạng.

Ghi chú

Hồ sơ ứng dụng chỉ kiểm soát các quyền của tiến trình gốc sau khi sử dụng su; nó không kiểm soát các quyền của ứng dụng. Nếu một ứng dụng đã yêu cầu quyền truy cập mạng, ứng dụng đó vẫn có thể truy cập mạng ngay cả khi không sử dụng su. Việc xóa nhóm inet khỏi su chỉ ngăn su truy cập mạng.

Root Profile được thực thi trong kernel và không dựa vào hành vi tự nguyện của các ứng dụng root, không giống như việc chuyển đổi người dùng hoặc nhóm thông qua su, việc cấp quyền su hoàn toàn phụ thuộc vào người dùng chứ không phải nhà phát triển.

Capabilities

Capabilities (khả năng) là một cơ chế phân tách đặc quyền trong Linux.

Với mục đích thực hiện kiểm tra quyền, việc triển khai UNIX truyền thống phân biệt hai loại quy trình: quy trình đặc quyền (có ID người dùng hiệu quả là 0, được gọi là siêu người dùng hoặc root) và quy trình không có đặc quyền (có UID hiệu dụng khác 0). Các quy trình đặc quyền bỏ qua tất cả các bước kiểm tra quyền của kernel, trong khi các quy trình không có đặc quyền phải chịu sự kiểm tra quyền đầy đủ dựa trên thông tin xác thực của quy trình (thường là: UID hiệu quả, GID hiệu quả và danh sách nhóm bổ sung).

Bắt đầu với Linux 2.2, Linux chia các đặc quyền truyền thống được liên kết với siêu người dùng thành các đơn vị riêng biệt, được gọi là các khả năng, có thể được bật và tắt một cách độc lập.

Mỗi Khả năng đại diện cho một hoặc nhiều đặc quyền. Ví dụ: CAP_DAC_READ_SEARCH thể hiện khả năng bỏ qua việc kiểm tra quyền để đọc tệp cũng như quyền đọc và thực thi thư mục. Nếu người dùng có UID hiệu dụng là 0 (người dùng root) thiếu khả năng CAP_DAC_READ_SEARCH hoặc cao hơn, điều này có nghĩa là ngay cả khi họ là root, họ không thể tùy ý đọc tệp.

Cấu hình gốc của KernelSU cho phép tùy chỉnh các Khả năng của tiến trình gốc sau khi thực thi su, nhờ đó đạt được việc cấp một phần "quyền root". Không giống như UID và GID đã nói ở trên, một số ứng dụng gốc nhất định yêu cầu UID là 0 sau khi sử dụng su. Trong những trường hợp như vậy, việc giới hạn Khả năng của người dùng root này bằng UID 0 có thể hạn chế các hoạt động được phép của họ.

Rất Khuyến Nghị

Capabilities của Linux tài liệu chính thức cung cấp giải thích chi tiết về các khả năng mà mỗi Capabilities thể hiện. Nếu bạn có ý định tùy chỉnh Capabilities, bạn nên đọc tài liệu này trước.

SELinux

SELinux là một cơ chế Kiểm Soát Truy Cập Bắt Buộc (Mandatory Access Control: MAC) mạnh mẽ. Nó hoạt động theo nguyên tắc từ chối mặc định: bất kỳ hành động nào không được cho phép rõ ràng đều bị từ chối.

SELinux có thể chạy ở hai chế độ chung:

  1. Chế độ cho phép (Permissive mode): Các sự kiện từ chối được ghi lại nhưng không được thực thi.
  2. Chế độ thực thi (Enforcing mode): Các sự kiện từ chối được ghi lại và thực thi.

Cảnh báo

Các hệ thống Android hiện đại phụ thuộc rất nhiều vào SELinux để đảm bảo an ninh hệ thống tổng thể. Chúng tôi khuyên bạn không nên sử dụng bất kỳ hệ thống tùy chỉnh nào chạy ở "chế độ cho phép" vì nó không mang lại lợi thế đáng kể nào so với hệ thống mở hoàn toàn.

Việc giải thích khái niệm đầy đủ về SELinux rất phức tạp và nằm ngoài phạm vi của tài liệu này. Trước tiên nên hiểu hoạt động của nó thông qua các tài nguyên sau:

  1. Wikipedia
  2. Red Hat: What Is SELinux?
  3. ArchLinux: SELinux

Root Profile của KernelSU cho phép tùy chỉnh ngữ cảnh SELinux của tiến trình gốc sau khi thực thi su. Các quy tắc kiểm soát truy cập cụ thể có thể được đặt cho bối cảnh này để cho phép kiểm soát chi tiết hơn các quyền .

Trong các trường hợp điển hình, khi một ứng dụng thực thi su, nó sẽ chuyển quy trình sang miền SELinux với quyền truy cập không hạn chế, chẳng hạn như u:r:su:s0. Thông qua Root Profile, miền này có thể được chuyển sang miền tùy chỉnh, chẳng hạn như u:r:app1:s0 và một loạt quy tắc có thể được xác định cho miền này:

sh
type app1
-enforce app1
-typeattribute app1 mlstrustedsubject
-allow app1 * * *

Lưu ý rằng quy tắc allow app1 * * * chỉ được sử dụng cho mục đích minh họa. Trong thực tế, quy tắc này không nên được sử dụng rộng rãi vì nó không khác nhiều so với chế độ cho phép.

Escalation

Nếu cấu hình của Root Profile không được đặt đúng cách, một tình huống escalation (leo thang) có thể xảy ra: các hạn chế do Root Profile áp đặt có thể vô tình bị lỗi.

Ví dụ: nếu bạn cấp quyền root cho người dùng shell ADB (đây là trường hợp phổ biến), sau đó bạn cấp quyền root cho một ứng dụng thông thường nhưng định cấu hình cấu hình gốc của nó bằng UID 2000 (là UID của người dùng shell ADB) , ứng dụng có thể có được quyền truy cập root đầy đủ bằng cách thực hiện lệnh su hai lần:

  1. Lần thực thi su đầu tiên phải tuân theo sự thực thi của App Profile và sẽ chuyển sang UID 2000 (adb shell) thay vì 0 (root).
  2. Lần thực thi su thứ hai, vì UID là 2000 và bạn đã cấp quyền truy cập root cho UID 2000 (adb shell) trong cấu hình, ứng dụng sẽ có toàn quyền root.

Ghi chú

Hành vi này hoàn toàn được mong đợi và không phải là lỗi. Vì vậy, chúng tôi khuyến nghị như sau:

Nếu bạn thực sự cần cấp quyền root cho ADB (ví dụ: với tư cách là nhà phát triển), bạn không nên thay đổi UID thành 2000 khi định cấu hình Root Profile. Sử dụng 1000 (hệ thống) sẽ là lựa chọn tốt hơn.

Non-Root Profile

Umount Modules

KernelSU cung cấp một cơ chế systemless để sửa đổi các phân vùng hệ thống, đạt được thông qua việc gắn overlayfs. Tuy nhiên, một số ứng dụng có thể nhạy cảm với hành vi đó. Do đó, chúng ta có thể dỡ bỏ các mô-đun được gắn trên các ứng dụng này bằng cách đặt tùy chọn "umount modules".

Ngoài ra, giao diện cài đặt của trình quản lý KernelSU cung cấp một công tắc cho "umount modules by default". Theo mặc định, công tắc này được bật, có nghĩa là KernelSU hoặc một số mô-đun sẽ hủy tải các mô-đun cho ứng dụng này trừ khi áp dụng cài đặt bổ sung. Nếu bạn không thích cài đặt này hoặc nếu nó ảnh hưởng đến một số ứng dụng nhất định, bạn có các tùy chọn sau:

  1. Giữ nút chuyển cho "umount modules by default" và tắt riêng tùy chọn "umount modules" trong App Profile đối với các ứng dụng yêu cầu tải mô-đun (hoạt động như "whitelist").
  2. Tắt khóa chuyển cho "umount modules by default" và bật riêng tùy chọn "umount modules" trong App Profile cho các ứng dụng yêu cầu dỡ bỏ mô-đun (hoạt động như "blacklist").

INFO

Trong các thiết bị sử dụng kernel phiên bản 5.10 trở lên, kernel thực hiện việc dỡ tải các mô-đun. Tuy nhiên, đối với các thiết bị chạy phiên bản kernel dưới 5.10, công tắc này chỉ đơn thuần là một tùy chọn cấu hình và bản thân KernelSU không thực hiện bất kỳ hành động nào. Một số mô-đun, chẳng hạn như Zygisksu, có thể sử dụng công tắc này để xác định xem có cần thiết phải dỡ bỏ mô-đun hay không.

Phát hành dưới giấy phép GPL3.

- +
Skip to content

App Profile

App Profile là một cơ chế do KernelSU cung cấp để tùy chỉnh cấu hình của các ứng dụng khác nhau.

Đối với các ứng dụng được cấp quyền root (tức là có thể sử dụng su), App Profile cũng có thể được gọi là Root Profile. Nó cho phép tùy chỉnh các quy tắc uid, gid, groups, capabilitiesSELinux của lệnh su, do đó hạn chế các đặc quyền của người dùng root. Ví dụ: nó có thể chỉ cấp quyền mạng cho các ứng dụng tường lửa trong khi từ chối quyền truy cập tệp hoặc có thể cấp quyền shell thay vì quyền truy cập root đầy đủ cho các ứng dụng đóng băng: giữ nguồn điện theo nguyên tắc đặc quyền tối thiểu.

Đối với các ứng dụng thông thường không có quyền root, App Profile có thể kiểm soát hành vi của hệ thống kernel và mô-đun đối với các ứng dụng này. Ví dụ, nó có thể xác định liệu các sửa đổi do mô-đun tạo ra có nên được giải quyết hay không. Hệ thống kernel và mô-đun có thể đưa ra quyết định dựa trên cấu hình này, chẳng hạn như thực hiện các hoạt động tương tự như "hiding"

Root Profile

UID, GID, và Groups

Hệ thống Linux có hai khái niệm: người dùng (user) và nhóm (group). Mỗi người dùng có một user ID (UID) và một người dùng có thể thuộc nhiều nhóm, mỗi nhóm có group ID (GID) riêng. Những ID này được sử dụng để xác định người dùng trong hệ thống và xác định tài nguyên hệ thống nào họ có thể truy cập.

Người dùng có UID bằng 0 được gọi là người dùng root và các nhóm có GID bằng 0 được gọi là nhóm root. Nhóm người dùng root thường giữ các đặc quyền hệ thống cao nhất.

Trong trường hợp hệ thống Android, mỗi ứng dụng là một người dùng riêng biệt (không bao gồm các trường hợp UID dùng chung) với một UID duy nhất. Ví dụ: 0 đại diện cho người dùng root, 1000 đại diện cho system, 2000 đại diện cho ADB shell và các UID từ 10000 đến 19999 đại diện cho các ứng dụng thông thường.

INFO

Ở đây, UID được đề cập không giống với khái niệm nhiều người dùng hoặc hồ sơ công việc (Work profile) trong hệ thống Android. Hồ sơ công việc thực sự được triển khai bằng cách phân vùng phạm vi UID. Ví dụ: 10000-19999 đại diện cho người dùng chính, trong khi 110000-119999 đại diện cho hồ sơ công việc. Mỗi ứng dụng thông thường trong số đó đều có UID riêng.

Mỗi ứng dụng có thể có nhiều nhóm, với GID đại diện cho nhóm chính, thường khớp với UID. Các nhóm khác được gọi là nhóm bổ sung. Một số quyền nhất định được kiểm soát thông qua các nhóm, chẳng hạn như quyền truy cập mạng hoặc truy cập Bluetooth.

Ví dụ: nếu chúng ta thực thi lệnh id trong shell ADB, kết quả đầu ra có thể trông như thế này:

sh
oriole:/ $ id
+uid=2000(shell) gid=2000(shell) groups=2000(shell),1004(input),1007(log),1011(adb),1015(sdcard_rw),1028(sdcard_r),1078(ext_data_rw),1079(ext_obb_rw),3001(net_bt_admin),3002(net_bt),3003(inet),3006(net_bw_stats),3009(readproc),3011(uhid),3012(readtracefs) context=u:r:shell:s0
oriole:/ $ id
+uid=2000(shell) gid=2000(shell) groups=2000(shell),1004(input),1007(log),1011(adb),1015(sdcard_rw),1028(sdcard_r),1078(ext_data_rw),1079(ext_obb_rw),3001(net_bt_admin),3002(net_bt),3003(inet),3006(net_bw_stats),3009(readproc),3011(uhid),3012(readtracefs) context=u:r:shell:s0

Ở đây, UID là 2000 và GID (ID nhóm chính) cũng là 2000. Ngoài ra, nó thuộc một số nhóm bổ sung, chẳng hạn như inet (biểu thị khả năng tạo ổ cắm AF_INETAF_INET6) và sdcard_rw (biểu thị quyền đọc/ghi đối với thẻ SD).

Root Profile của KernelSU cho phép tùy chỉnh UID, GID và các nhóm cho quy trình gốc sau khi thực thi su. Ví dụ: Cấu hình gốc của ứng dụng gốc có thể đặt UID của nó thành 2000, có nghĩa là khi sử dụng su, các quyền thực tế của ứng dụng sẽ ở cấp shell ADB. Nhóm inet có thể bị xóa, ngăn lệnh su truy cập mạng.

Ghi chú

Hồ sơ ứng dụng chỉ kiểm soát các quyền của tiến trình gốc sau khi sử dụng su; nó không kiểm soát các quyền của ứng dụng. Nếu một ứng dụng đã yêu cầu quyền truy cập mạng, ứng dụng đó vẫn có thể truy cập mạng ngay cả khi không sử dụng su. Việc xóa nhóm inet khỏi su chỉ ngăn su truy cập mạng.

Root Profile được thực thi trong kernel và không dựa vào hành vi tự nguyện của các ứng dụng root, không giống như việc chuyển đổi người dùng hoặc nhóm thông qua su, việc cấp quyền su hoàn toàn phụ thuộc vào người dùng chứ không phải nhà phát triển.

Capabilities

Capabilities (khả năng) là một cơ chế phân tách đặc quyền trong Linux.

Với mục đích thực hiện kiểm tra quyền, việc triển khai UNIX truyền thống phân biệt hai loại quy trình: quy trình đặc quyền (có ID người dùng hiệu quả là 0, được gọi là siêu người dùng hoặc root) và quy trình không có đặc quyền (có UID hiệu dụng khác 0). Các quy trình đặc quyền bỏ qua tất cả các bước kiểm tra quyền của kernel, trong khi các quy trình không có đặc quyền phải chịu sự kiểm tra quyền đầy đủ dựa trên thông tin xác thực của quy trình (thường là: UID hiệu quả, GID hiệu quả và danh sách nhóm bổ sung).

Bắt đầu với Linux 2.2, Linux chia các đặc quyền truyền thống được liên kết với siêu người dùng thành các đơn vị riêng biệt, được gọi là các khả năng, có thể được bật và tắt một cách độc lập.

Mỗi Khả năng đại diện cho một hoặc nhiều đặc quyền. Ví dụ: CAP_DAC_READ_SEARCH thể hiện khả năng bỏ qua việc kiểm tra quyền để đọc tệp cũng như quyền đọc và thực thi thư mục. Nếu người dùng có UID hiệu dụng là 0 (người dùng root) thiếu khả năng CAP_DAC_READ_SEARCH hoặc cao hơn, điều này có nghĩa là ngay cả khi họ là root, họ không thể tùy ý đọc tệp.

Cấu hình gốc của KernelSU cho phép tùy chỉnh các Khả năng của tiến trình gốc sau khi thực thi su, nhờ đó đạt được việc cấp một phần "quyền root". Không giống như UID và GID đã nói ở trên, một số ứng dụng gốc nhất định yêu cầu UID là 0 sau khi sử dụng su. Trong những trường hợp như vậy, việc giới hạn Khả năng của người dùng root này bằng UID 0 có thể hạn chế các hoạt động được phép của họ.

Rất Khuyến Nghị

Capabilities của Linux tài liệu chính thức cung cấp giải thích chi tiết về các khả năng mà mỗi Capabilities thể hiện. Nếu bạn có ý định tùy chỉnh Capabilities, bạn nên đọc tài liệu này trước.

SELinux

SELinux là một cơ chế Kiểm Soát Truy Cập Bắt Buộc (Mandatory Access Control: MAC) mạnh mẽ. Nó hoạt động theo nguyên tắc từ chối mặc định: bất kỳ hành động nào không được cho phép rõ ràng đều bị từ chối.

SELinux có thể chạy ở hai chế độ chung:

  1. Chế độ cho phép (Permissive mode): Các sự kiện từ chối được ghi lại nhưng không được thực thi.
  2. Chế độ thực thi (Enforcing mode): Các sự kiện từ chối được ghi lại và thực thi.

Cảnh báo

Các hệ thống Android hiện đại phụ thuộc rất nhiều vào SELinux để đảm bảo an ninh hệ thống tổng thể. Chúng tôi khuyên bạn không nên sử dụng bất kỳ hệ thống tùy chỉnh nào chạy ở "chế độ cho phép" vì nó không mang lại lợi thế đáng kể nào so với hệ thống mở hoàn toàn.

Việc giải thích khái niệm đầy đủ về SELinux rất phức tạp và nằm ngoài phạm vi của tài liệu này. Trước tiên nên hiểu hoạt động của nó thông qua các tài nguyên sau:

  1. Wikipedia
  2. Red Hat: What Is SELinux?
  3. ArchLinux: SELinux

Root Profile của KernelSU cho phép tùy chỉnh ngữ cảnh SELinux của tiến trình gốc sau khi thực thi su. Các quy tắc kiểm soát truy cập cụ thể có thể được đặt cho bối cảnh này để cho phép kiểm soát chi tiết hơn các quyền .

Trong các trường hợp điển hình, khi một ứng dụng thực thi su, nó sẽ chuyển quy trình sang miền SELinux với quyền truy cập không hạn chế, chẳng hạn như u:r:su:s0. Thông qua Root Profile, miền này có thể được chuyển sang miền tùy chỉnh, chẳng hạn như u:r:app1:s0 và một loạt quy tắc có thể được xác định cho miền này:

sh
type app1
+enforce app1
+typeattribute app1 mlstrustedsubject
+allow app1 * * *
type app1
+enforce app1
+typeattribute app1 mlstrustedsubject
+allow app1 * * *

Lưu ý rằng quy tắc allow app1 * * * chỉ được sử dụng cho mục đích minh họa. Trong thực tế, quy tắc này không nên được sử dụng rộng rãi vì nó không khác nhiều so với chế độ cho phép.

Escalation

Nếu cấu hình của Root Profile không được đặt đúng cách, một tình huống escalation (leo thang) có thể xảy ra: các hạn chế do Root Profile áp đặt có thể vô tình bị lỗi.

Ví dụ: nếu bạn cấp quyền root cho người dùng shell ADB (đây là trường hợp phổ biến), sau đó bạn cấp quyền root cho một ứng dụng thông thường nhưng định cấu hình cấu hình gốc của nó bằng UID 2000 (là UID của người dùng shell ADB) , ứng dụng có thể có được quyền truy cập root đầy đủ bằng cách thực hiện lệnh su hai lần:

  1. Lần thực thi su đầu tiên phải tuân theo sự thực thi của App Profile và sẽ chuyển sang UID 2000 (adb shell) thay vì 0 (root).
  2. Lần thực thi su thứ hai, vì UID là 2000 và bạn đã cấp quyền truy cập root cho UID 2000 (adb shell) trong cấu hình, ứng dụng sẽ có toàn quyền root.

Ghi chú

Hành vi này hoàn toàn được mong đợi và không phải là lỗi. Vì vậy, chúng tôi khuyến nghị như sau:

Nếu bạn thực sự cần cấp quyền root cho ADB (ví dụ: với tư cách là nhà phát triển), bạn không nên thay đổi UID thành 2000 khi định cấu hình Root Profile. Sử dụng 1000 (hệ thống) sẽ là lựa chọn tốt hơn.

Non-Root Profile

Umount Modules

KernelSU cung cấp một cơ chế systemless để sửa đổi các phân vùng hệ thống, đạt được thông qua việc gắn overlayfs. Tuy nhiên, một số ứng dụng có thể nhạy cảm với hành vi đó. Do đó, chúng ta có thể dỡ bỏ các mô-đun được gắn trên các ứng dụng này bằng cách đặt tùy chọn "umount modules".

Ngoài ra, giao diện cài đặt của trình quản lý KernelSU cung cấp một công tắc cho "umount modules by default". Theo mặc định, công tắc này được bật, có nghĩa là KernelSU hoặc một số mô-đun sẽ hủy tải các mô-đun cho ứng dụng này trừ khi áp dụng cài đặt bổ sung. Nếu bạn không thích cài đặt này hoặc nếu nó ảnh hưởng đến một số ứng dụng nhất định, bạn có các tùy chọn sau:

  1. Giữ nút chuyển cho "umount modules by default" và tắt riêng tùy chọn "umount modules" trong App Profile đối với các ứng dụng yêu cầu tải mô-đun (hoạt động như "whitelist").
  2. Tắt khóa chuyển cho "umount modules by default" và bật riêng tùy chọn "umount modules" trong App Profile cho các ứng dụng yêu cầu dỡ bỏ mô-đun (hoạt động như "blacklist").

INFO

Trong các thiết bị sử dụng kernel phiên bản 5.10 trở lên, kernel thực hiện việc dỡ tải các mô-đun. Tuy nhiên, đối với các thiết bị chạy phiên bản kernel dưới 5.10, công tắc này chỉ đơn thuần là một tùy chọn cấu hình và bản thân KernelSU không thực hiện bất kỳ hành động nào. Một số mô-đun, chẳng hạn như Zygisksu, có thể sử dụng công tắc này để xác định xem có cần thiết phải dỡ bỏ mô-đun hay không.

Phát hành dưới giấy phép GPL3.

+ \ No newline at end of file diff --git a/vi_VN/guide/difference-with-magisk.html b/vi_VN/guide/difference-with-magisk.html index 1f4b8d654baf..3da2a223b7b6 100644 --- a/vi_VN/guide/difference-with-magisk.html +++ b/vi_VN/guide/difference-with-magisk.html @@ -5,18 +5,19 @@ Sự khác biệt với Magisk | KernelSU - - + + + - - - - + + + + + -
Skip to content
On this page

Sự khác biệt với Magisk

Mặc dù có nhiều điểm tương đồng giữa mô-đun KernelSU và mô-đun Magisk nhưng chắc chắn vẫn có một số khác biệt do cơ chế triển khai hoàn toàn khác nhau của chúng. Nếu muốn mô-đun của mình chạy trên cả Magisk và KernelSU, bạn phải hiểu những khác biệt này.

Điểm tương đồng

  • Định dạng file mô-đun: đều sử dụng định dạng zip để sắp xếp các mô-đun và định dạng của các mô-đun gần như giống nhau
  • Thư mục cài đặt mô-đun: cả hai đều nằm trong /data/adb/modules
  • systemless: cả hai đều hỗ trợ sửa đổi /system theo cách không có hệ thống thông qua các mô-đun
  • post-fs-data.sh: thời gian thực hiện và ngữ nghĩa hoàn toàn giống nhau
  • service.sh: thời gian thực hiện và ngữ nghĩa hoàn toàn giống nhau
  • system.prop: hoàn toàn giống nhau
  • sepolicy.rule: hoàn toàn giống nhau
  • BusyBox: các tập lệnh được chạy trong BusyBox với "standalone mode" được bật trong cả hai trường hợp

Điểm khác biệt

Trước khi hiểu sự khác biệt, bạn cần biết cách phân biệt mô-đun của bạn đang chạy trong KernelSU hay Magisk. Bạn có thể sử dụng biến môi trường KSU để phân biệt nó ở tất cả những nơi bạn có thể chạy tập lệnh mô-đun (customize.sh, post-fs-data.sh, service.sh). Trong KernelSU, biến môi trường này sẽ được đặt thành true.

Dưới đây là một số khác biệt:

  • Không thể cài đặt các mô-đun KernelSU ở chế độ Recovery.
  • Các mô-đun KernelSU không có hỗ trợ tích hợp cho Zygisk (nhưng bạn có thể sử dụng các mô-đun Zygisk thông qua ZygiskNext.
  • Phương pháp thay thế hoặc xóa file trong module KernelSU hoàn toàn khác với Magisk. KernelSU không hỗ trợ phương thức .replace. Thay vào đó, bạn cần tạo một file cùng tên với mknod filename c 0 0 để xóa file tương ứng.
  • Các thư mục của BusyBox khác nhau. BusyBox tích hợp trong KernelSU nằm ở /data/adb/ksu/bin/busybox, trong khi ở Magisk nó nằm ở /data/adb/magisk/busybox. Lưu ý rằng đây là hoạt động nội bộ của KernelSU và có thể thay đổi trong tương lai!
  • KernelSU không hỗ trợ file .replace; tuy nhiên, KernelSU hỗ trợ biến REMOVEREPLACE để xóa hoặc thay thế các tệp và thư mục.
  • KernelSU thêm giai đoạn boot-completed để chạy một số script khi khởi động xong.
  • KernelSU thêm giai đoạn post-mount để chạy một số tập lệnh sau khi gắn overlayfs

Phát hành dưới giấy phép GPL3.

- +
Skip to content

Sự khác biệt với Magisk

Mặc dù có nhiều điểm tương đồng giữa mô-đun KernelSU và mô-đun Magisk nhưng chắc chắn vẫn có một số khác biệt do cơ chế triển khai hoàn toàn khác nhau của chúng. Nếu muốn mô-đun của mình chạy trên cả Magisk và KernelSU, bạn phải hiểu những khác biệt này.

Điểm tương đồng

  • Định dạng file mô-đun: đều sử dụng định dạng zip để sắp xếp các mô-đun và định dạng của các mô-đun gần như giống nhau
  • Thư mục cài đặt mô-đun: cả hai đều nằm trong /data/adb/modules
  • systemless: cả hai đều hỗ trợ sửa đổi /system theo cách không có hệ thống thông qua các mô-đun
  • post-fs-data.sh: thời gian thực hiện và ngữ nghĩa hoàn toàn giống nhau
  • service.sh: thời gian thực hiện và ngữ nghĩa hoàn toàn giống nhau
  • system.prop: hoàn toàn giống nhau
  • sepolicy.rule: hoàn toàn giống nhau
  • BusyBox: các tập lệnh được chạy trong BusyBox với "standalone mode" được bật trong cả hai trường hợp

Điểm khác biệt

Trước khi hiểu sự khác biệt, bạn cần biết cách phân biệt mô-đun của bạn đang chạy trong KernelSU hay Magisk. Bạn có thể sử dụng biến môi trường KSU để phân biệt nó ở tất cả những nơi bạn có thể chạy tập lệnh mô-đun (customize.sh, post-fs-data.sh, service.sh). Trong KernelSU, biến môi trường này sẽ được đặt thành true.

Dưới đây là một số khác biệt:

  • Không thể cài đặt các mô-đun KernelSU ở chế độ Recovery.
  • Các mô-đun KernelSU không có hỗ trợ tích hợp cho Zygisk (nhưng bạn có thể sử dụng các mô-đun Zygisk thông qua ZygiskNext.
  • Phương pháp thay thế hoặc xóa file trong module KernelSU hoàn toàn khác với Magisk. KernelSU không hỗ trợ phương thức .replace. Thay vào đó, bạn cần tạo một file cùng tên với mknod filename c 0 0 để xóa file tương ứng.
  • Các thư mục của BusyBox khác nhau. BusyBox tích hợp trong KernelSU nằm ở /data/adb/ksu/bin/busybox, trong khi ở Magisk nó nằm ở /data/adb/magisk/busybox. Lưu ý rằng đây là hoạt động nội bộ của KernelSU và có thể thay đổi trong tương lai!
  • KernelSU không hỗ trợ file .replace; tuy nhiên, KernelSU hỗ trợ biến REMOVEREPLACE để xóa hoặc thay thế các tệp và thư mục.
  • KernelSU thêm giai đoạn boot-completed để chạy một số script khi khởi động xong.
  • KernelSU thêm giai đoạn post-mount để chạy một số tập lệnh sau khi gắn overlayfs

Phát hành dưới giấy phép GPL3.

+ \ No newline at end of file diff --git a/vi_VN/guide/faq.html b/vi_VN/guide/faq.html index 8ca555fa1046..d2bc6ca32f14 100644 --- a/vi_VN/guide/faq.html +++ b/vi_VN/guide/faq.html @@ -5,18 +5,19 @@ FAQ | KernelSU - - + + + - - - - + + + + + -
Skip to content
On this page

FAQ

KernelSU có hỗ trợ thiết bị của tôi không?

Đầu tiên, thiết bị của bạn sẽ có thể mở khóa bootloader. Nếu không thể thì nó không được hỗ trợ.

Sau đó, cài đặt Ứng dụng KernelSU manager vào thiết bị của bạn và mở nó, nếu nó hiển thị Unsupported thì thiết bị của bạn chưa được hỗ trợ ngay, nhưng bạn có thể tạo nguồn kernel và tích hợp KernelSU để nó hoạt động hoặc sử dụng unofficially-support-devices.

KernelSU có cần mở khóa Bootloader không?

Chắc chắn có.

KernelSU có hỗ trợ các mô-đun không?

Có, nhưng đây là phiên bản đầu tiên nên có thể bị lỗi. Đợi nó ổn định nhé 😃

KernelSU có hỗ trợ Xposed không?

Có, DreamlandTaiChi hiện đã hoạt động. Đối với LSPosed, bạn có thể làm cho nó hoạt động bằng ZygiskNext

KernelSU có hỗ trợ Zygisk không?

KernelSU không có hỗ trợ Zygisk tích hợp sẵn nhưng thay vào đó, bạn có thể sử dụng ZygiskNext.

KernelSU có tương thích với Magisk không?

Hệ thống mô-đun của KernelSU xung đột với magic mount của Magisk, nếu có bất kỳ mô-đun nào được kích hoạt trong KernelSU thì toàn bộ Magisk sẽ không hoạt động.

Nhưng nếu bạn chỉ sử dụng su của KernelSU thì nó sẽ hoạt động tốt với Magisk: KernelSU sửa đổi kernel và Magisk sửa đổi ramdisk, chúng có thể hoạt động cùng nhau.

KernelSU sẽ thay thế Magisk?

Chúng tôi không nghĩ như vậy và đó không phải là mục tiêu của chúng tôi. Magisk đủ tốt cho giải pháp root userspace và nó sẽ tồn tại lâu dài. Mục tiêu của KernelSU là cung cấp giao diện kernel cho người dùng chứ không thay thế Magisk.

KernelSU có thể hỗ trợ các thiết bị không phải GKI không?

Điều đó là có thể. Nhưng bạn nên tải nguồn kernel về và tích hợp KernelSU vào source tree rồi tự biên dịch kernel.

KernelSU có thể hỗ trợ các thiết bị dưới Android 12 không?

Chính kernel của thiết bị ảnh hưởng đến khả năng tương thích của KernelSU và nó không liên quan gì đến phiên bản Android. Hạn chế duy nhất là các thiết bị chạy Android 12 phải là kernel 5.10+(thiết bị GKI). Vì thế:

  1. Các thiết bị chạy Android 12 phải được hỗ trợ.
  2. Các thiết bị có kernel cũ (Một số thiết bị Android 12 cũng là kernel cũ) tương thích (Bạn nên tự build kernel)

KernelSU có thể hỗ trợ kernel cũ không?

Có thể, KernelSU hiện đã được backport sang kernel 4.14, đối với kernel cũ hơn, bạn cần backport nó một cách cẩn thận và PR rất đáng hoan nghênh!

Làm cách nào để tích hợp KernelSU cho kernel cũ?

Vui lòng tham khảo hướng dẫn

Tại sao phiên bản Android của tôi là 13 và kernel hiển thị "android12-5.10"?

Phiên bản Kernel không liên quan gì đến phiên bản Android, nếu bạn cần flash kernel thì dùng luôn phiên bản kernel, phiên bản Android không quá quan trọng.

Đã có mount namespace --mount-master/global trên KernelSU chưa?

Hiện tại thì không (có thể có trong tương lai), nhưng có nhiều cách để chuyển sang global mount namespace một cách thủ công, chẳng hạn như:

  1. nsenter -t 1 -m sh để lấy shell trong global mount namespace.
  2. Thêm nsenter --mount=/proc/1/ns/mnt vào lệnh bạn muốn thực thi, sau đó lệnh được thực thi trong global mount namespace. KernelSU cũng sử dụng cách này

Tôi là GKI1.0, tôi có thể sử dụng cái này không?

GKI1 khác hoàn toàn với GKI2, bạn phải tự biên dịch kernel.

Phát hành dưới giấy phép GPL3.

- +
Skip to content

FAQ

KernelSU có hỗ trợ thiết bị của tôi không?

Đầu tiên, thiết bị của bạn sẽ có thể mở khóa bootloader. Nếu không thể thì nó không được hỗ trợ.

Sau đó, cài đặt Ứng dụng KernelSU manager vào thiết bị của bạn và mở nó, nếu nó hiển thị Unsupported thì thiết bị của bạn chưa được hỗ trợ ngay, nhưng bạn có thể tạo nguồn kernel và tích hợp KernelSU để nó hoạt động hoặc sử dụng unofficially-support-devices.

KernelSU có cần mở khóa Bootloader không?

Chắc chắn có.

KernelSU có hỗ trợ các mô-đun không?

Có, nhưng đây là phiên bản đầu tiên nên có thể bị lỗi. Đợi nó ổn định nhé 😃

KernelSU có hỗ trợ Xposed không?

Có, DreamlandTaiChi hiện đã hoạt động. Đối với LSPosed, bạn có thể làm cho nó hoạt động bằng ZygiskNext

KernelSU có hỗ trợ Zygisk không?

KernelSU không có hỗ trợ Zygisk tích hợp sẵn nhưng thay vào đó, bạn có thể sử dụng ZygiskNext.

KernelSU có tương thích với Magisk không?

Hệ thống mô-đun của KernelSU xung đột với magic mount của Magisk, nếu có bất kỳ mô-đun nào được kích hoạt trong KernelSU thì toàn bộ Magisk sẽ không hoạt động.

Nhưng nếu bạn chỉ sử dụng su của KernelSU thì nó sẽ hoạt động tốt với Magisk: KernelSU sửa đổi kernel và Magisk sửa đổi ramdisk, chúng có thể hoạt động cùng nhau.

KernelSU sẽ thay thế Magisk?

Chúng tôi không nghĩ như vậy và đó không phải là mục tiêu của chúng tôi. Magisk đủ tốt cho giải pháp root userspace và nó sẽ tồn tại lâu dài. Mục tiêu của KernelSU là cung cấp giao diện kernel cho người dùng chứ không thay thế Magisk.

KernelSU có thể hỗ trợ các thiết bị không phải GKI không?

Điều đó là có thể. Nhưng bạn nên tải nguồn kernel về và tích hợp KernelSU vào source tree rồi tự biên dịch kernel.

KernelSU có thể hỗ trợ các thiết bị dưới Android 12 không?

Chính kernel của thiết bị ảnh hưởng đến khả năng tương thích của KernelSU và nó không liên quan gì đến phiên bản Android. Hạn chế duy nhất là các thiết bị chạy Android 12 phải là kernel 5.10+(thiết bị GKI). Vì thế:

  1. Các thiết bị chạy Android 12 phải được hỗ trợ.
  2. Các thiết bị có kernel cũ (Một số thiết bị Android 12 cũng là kernel cũ) tương thích (Bạn nên tự build kernel)

KernelSU có thể hỗ trợ kernel cũ không?

Có thể, KernelSU hiện đã được backport sang kernel 4.14, đối với kernel cũ hơn, bạn cần backport nó một cách cẩn thận và PR rất đáng hoan nghênh!

Làm cách nào để tích hợp KernelSU cho kernel cũ?

Vui lòng tham khảo hướng dẫn

Tại sao phiên bản Android của tôi là 13 và kernel hiển thị "android12-5.10"?

Phiên bản Kernel không liên quan gì đến phiên bản Android, nếu bạn cần flash kernel thì dùng luôn phiên bản kernel, phiên bản Android không quá quan trọng.

Đã có mount namespace --mount-master/global trên KernelSU chưa?

Hiện tại thì không (có thể có trong tương lai), nhưng có nhiều cách để chuyển sang global mount namespace một cách thủ công, chẳng hạn như:

  1. nsenter -t 1 -m sh để lấy shell trong global mount namespace.
  2. Thêm nsenter --mount=/proc/1/ns/mnt vào lệnh bạn muốn thực thi, sau đó lệnh được thực thi trong global mount namespace. KernelSU cũng sử dụng cách này

Tôi là GKI1.0, tôi có thể sử dụng cái này không?

GKI1 khác hoàn toàn với GKI2, bạn phải tự biên dịch kernel.

Phát hành dưới giấy phép GPL3.

+ \ No newline at end of file diff --git a/vi_VN/guide/hidden-features.html b/vi_VN/guide/hidden-features.html index a0924bee7611..b18968be8690 100644 --- a/vi_VN/guide/hidden-features.html +++ b/vi_VN/guide/hidden-features.html @@ -5,18 +5,19 @@ Tính Năng Ẩn | KernelSU - - + + + - - - - + + + + + -
Skip to content
On this page

Tính Năng Ẩn

.ksurc

Theo mặc định, /system/bin/sh tải /system/etc/mkshrc.

Bạn có thể tạo su tải tệp rc tùy chỉnh bằng cách tạo tệp /data/adb/ksu/.ksurc.

Phát hành dưới giấy phép GPL3.

- +
Skip to content

Tính Năng Ẩn

.ksurc

Theo mặc định, /system/bin/sh tải /system/etc/mkshrc.

Bạn có thể tạo su tải tệp rc tùy chỉnh bằng cách tạo tệp /data/adb/ksu/.ksurc.

Phát hành dưới giấy phép GPL3.

+ \ No newline at end of file diff --git a/vi_VN/guide/how-to-build.html b/vi_VN/guide/how-to-build.html index 45f26fddb8ce..3437c39e0c7f 100644 --- a/vi_VN/guide/how-to-build.html +++ b/vi_VN/guide/how-to-build.html @@ -5,21 +5,25 @@ Làm thế nào để xây dựng KernelSU? | KernelSU - - + + + - - - - + + + + + -
Skip to content
On this page

Làm thế nào để xây dựng KernelSU?

Trước tiên, bạn nên đọc tài liệu chính thức của Android để xây dựng kernel:

  1. Building Kernels
  2. GKI Release Builds

WARNING

Trang này dành cho thiết bị GKI, nếu bạn sử dụng kernel cũ, vui lòng tham khảo cách tích hợp KernelSU cho kernel cũ

Build Kernel

Đồng bộ hóa mã nguồn kernel

sh
repo init -u https://android.googlesource.com/kernel/manifest
-mv <kernel_manifest.xml> .repo/manifests
-repo init -m manifest.xml
-repo sync

<kernel_manifest.xml> là một tệp kê khai có thể xác định duy nhất một bản dựng, bạn có thể sử dụng tệp kê khai đó để thực hiện một bản dựng có thể dự đoán lại. Bạn nên tải xuống tệp kê khai từ Google GKI release builds

Build

Trước tiên, vui lòng kiểm tra tài liệu chính thức.

Ví dụ: chúng ta cần xây dựng kernel image aarch64:

sh
LTO=thin BUILD_CONFIG=common/build.config.gki.aarch64 build/build.sh

Đừng quên thêm cờ LTO=thin, nếu không quá trình xây dựng có thể thất bại nếu bộ nhớ máy tính của bạn nhỏ hơn 24Gb.

Bắt đầu từ Android 13, kernel được xây dựng bởi bazel:

sh
tools/bazel build --config=fast //common:kernel_aarch64_dist

Build Kernel với KernelSU

Nếu bạn có thể build kernel thành công thì việc xây dựng KernelSU thật dễ dàng, Chọn bất kỳ một lần chạy trong thư mục gốc nguồn Kernel:

  • Thẻ mới nhất (ổn định)
sh
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -
  • nhánh chính (dev)
sh
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -s main
  • Chọn thẻ (chẳng hạn như v0.5.2)
sh
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -s v0.5.2

Và sau đó build lại kernel và bạn sẽ có được image kernel với KernelSU!

Phát hành dưới giấy phép GPL3.

- +
Skip to content

Làm thế nào để xây dựng KernelSU?

Trước tiên, bạn nên đọc tài liệu chính thức của Android để xây dựng kernel:

  1. Building Kernels
  2. GKI Release Builds

WARNING

Trang này dành cho thiết bị GKI, nếu bạn sử dụng kernel cũ, vui lòng tham khảo cách tích hợp KernelSU cho kernel cũ

Build Kernel

Đồng bộ hóa mã nguồn kernel

sh
repo init -u https://android.googlesource.com/kernel/manifest
+mv <kernel_manifest.xml> .repo/manifests
+repo init -m manifest.xml
+repo sync
repo init -u https://android.googlesource.com/kernel/manifest
+mv <kernel_manifest.xml> .repo/manifests
+repo init -m manifest.xml
+repo sync

<kernel_manifest.xml> là một tệp kê khai có thể xác định duy nhất một bản dựng, bạn có thể sử dụng tệp kê khai đó để thực hiện một bản dựng có thể dự đoán lại. Bạn nên tải xuống tệp kê khai từ Google GKI release builds

Build

Trước tiên, vui lòng kiểm tra tài liệu chính thức.

Ví dụ: chúng ta cần xây dựng kernel image aarch64:

sh
LTO=thin BUILD_CONFIG=common/build.config.gki.aarch64 build/build.sh
LTO=thin BUILD_CONFIG=common/build.config.gki.aarch64 build/build.sh

Đừng quên thêm cờ LTO=thin, nếu không quá trình xây dựng có thể thất bại nếu bộ nhớ máy tính của bạn nhỏ hơn 24Gb.

Bắt đầu từ Android 13, kernel được xây dựng bởi bazel:

sh
tools/bazel build --config=fast //common:kernel_aarch64_dist
tools/bazel build --config=fast //common:kernel_aarch64_dist

Build Kernel với KernelSU

Nếu bạn có thể build kernel thành công thì việc xây dựng KernelSU thật dễ dàng, Chọn bất kỳ một lần chạy trong thư mục gốc nguồn Kernel:

  • Thẻ mới nhất (ổn định)
sh
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -
  • nhánh chính (dev)
sh
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -s main
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -s main
  • Chọn thẻ (chẳng hạn như v0.5.2)
sh
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -s v0.5.2
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -s v0.5.2

Và sau đó build lại kernel và bạn sẽ có được image kernel với KernelSU!

Phát hành dưới giấy phép GPL3.

+ \ No newline at end of file diff --git a/vi_VN/guide/how-to-integrate-for-non-gki.html b/vi_VN/guide/how-to-integrate-for-non-gki.html index 87338255fdbf..9dfa1fff0bb9 100644 --- a/vi_VN/guide/how-to-integrate-for-non-gki.html +++ b/vi_VN/guide/how-to-integrate-for-non-gki.html @@ -5,107 +5,197 @@ Làm thế nào để tích hợp KernelSU vào thiết bị không sử dụng GKI ? | KernelSU - - + + + - - - - + + + + + -
Skip to content
On this page

Làm thế nào để tích hợp KernelSU vào thiết bị không sử dụng GKI ?

KernelSU có thể được tích hợp vào kernel không phải GKI và hiện tại nó đã được backport xuống 4.14, thậm chí cũng có thể chạy trên kernel thấp hơn 4.14.

Do các kernel không phải GKI bị phân mảnh nên chúng tôi không có cách build thống nhất, vì vậy chúng tôi không thể cung cấp các boot image không phải GKI. Nhưng bạn có thể tự build kernel với KernelSU được tích hợp vào.

Đầu tiên, bạn phải build được kernel từ nguồn có khả năng boot được , nếu kernel không có mã nguồn mở thì rất khó để chạy KernelSU cho thiết bị của bạn.

Nếu bạn có thể build kernel khởi động được, có hai cách để tích hợp KernelSU vào mã nguồn kernel:

  1. Tự động với kprobe
  2. Thủ công

Tích hợp vào kprobe

KernelSU sử dụng kprobe để thực hiện hook kernel, nếu kprobe chạy tốt trong kernel của bạn thì nên sử dụng cách này.

Đầu tiên, thêm KernelSU vào mã nguồn kernel của bạn:

  • Thẻ mới nhất (ổn định)
sh
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -
  • Nhánh chính (dev)
sh
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -s main
  • Chọn thẻ (chẳng hạn như v0.5.2)
sh
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -s v0.5.2

Sau đó, bạn nên kiểm tra xem kprobe có được bật trong config của bạn hay không, nếu không, vui lòng thêm các cấu hình sau vào:

CONFIG_KPROBES=y
-CONFIG_HAVE_KPROBES=y
-CONFIG_KPROBE_EVENTS=y

Rồi build lại kernel của bạn, KernelSU sẽ hoạt động ok.

Trong trường hợp kprobe chưa được bật, bạn có thể thêm CONFIG_MODULES=y vào kernel config. (Nếu vẫn không có hiệu lực thì hãy sử dụng make menuconfig rồi tìm các thành phần khác mà kprobe phụ thuộc).

Nhưng nếu bạn gặp bootloop khi tích hợp KernelSU thì có khả năng kprobe bị hỏng trong kernel, bạn nên fix lỗi kprobe trong mã nguồn hoặc dùng cách 2.

Chỉnh sửa mã nguồn kernel thủ công

Nếu kprobe không thể hoạt động trong kernel của bạn (có thể là lỗi do upstream hoặc kernel dưới bản 4.8), thì bạn có thể thử cách này:

Đầu tiên, thêm KernelSU vào mã nguồn kernel của bạn:

sh
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -

Sau đó, thêm lệnh gọi KernelSU vào mã nguồn kernel, đây là một patch bạn có thể tham khảo:

diff
diff --git a/fs/exec.c b/fs/exec.c
-index ac59664eaecf..bdd585e1d2cc 100644
---- a/fs/exec.c
-+++ b/fs/exec.c
-@@ -1890,11 +1890,14 @@ static int __do_execve_file(int fd, struct filename *filename,
- 	return retval;
- }
- 
-+extern bool ksu_execveat_hook __read_mostly;
-+extern int ksu_handle_execveat(int *fd, struct filename **filename_ptr, void *argv,
-+			void *envp, int *flags);
-+extern int ksu_handle_execveat_sucompat(int *fd, struct filename **filename_ptr,
-+				 void *argv, void *envp, int *flags);
- static int do_execveat_common(int fd, struct filename *filename,
- 			      struct user_arg_ptr argv,
- 			      struct user_arg_ptr envp,
- 			      int flags)
- {
-+	if (unlikely(ksu_execveat_hook))
-+		ksu_handle_execveat(&fd, &filename, &argv, &envp, &flags);
-+	else
-+		ksu_handle_execveat_sucompat(&fd, &filename, &argv, &envp, &flags);
- 	return __do_execve_file(fd, filename, argv, envp, flags, NULL);
- }
- 
-diff --git a/fs/open.c b/fs/open.c
-index 05036d819197..965b84d486b8 100644
---- a/fs/open.c
-+++ b/fs/open.c
-@@ -348,6 +348,8 @@ SYSCALL_DEFINE4(fallocate, int, fd, int, mode, loff_t, offset, loff_t, len)
- 	return ksys_fallocate(fd, mode, offset, len);
- }
- 
-+extern int ksu_handle_faccessat(int *dfd, const char __user **filename_user, int *mode,
-+			 int *flags);
- /*
-  * access() needs to use the real uid/gid, not the effective uid/gid.
-  * We do this by temporarily clearing all FS-related capabilities and
-@@ -355,6 +357,7 @@ SYSCALL_DEFINE4(fallocate, int, fd, int, mode, loff_t, offset, loff_t, len)
-  */
- long do_faccessat(int dfd, const char __user *filename, int mode)
- {
-+	ksu_handle_faccessat(&dfd, &filename, &mode, NULL);
- 	const struct cred *old_cred;
- 	struct cred *override_cred;
- 	struct path path;
-diff --git a/fs/read_write.c b/fs/read_write.c
-index 650fc7e0f3a6..55be193913b6 100644
---- a/fs/read_write.c
-+++ b/fs/read_write.c
-@@ -434,10 +434,14 @@ ssize_t kernel_read(struct file *file, void *buf, size_t count, loff_t *pos)
- }
- EXPORT_SYMBOL(kernel_read);
- 
-+extern bool ksu_vfs_read_hook __read_mostly;
-+extern int ksu_handle_vfs_read(struct file **file_ptr, char __user **buf_ptr,
-+			size_t *count_ptr, loff_t **pos);
- ssize_t vfs_read(struct file *file, char __user *buf, size_t count, loff_t *pos)
- {
- 	ssize_t ret;
- 
-+	if (unlikely(ksu_vfs_read_hook))
-+		ksu_handle_vfs_read(&file, &buf, &count, &pos);
-+
- 	if (!(file->f_mode & FMODE_READ))
- 		return -EBADF;
- 	if (!(file->f_mode & FMODE_CAN_READ))
-diff --git a/fs/stat.c b/fs/stat.c
-index 376543199b5a..82adcef03ecc 100644
---- a/fs/stat.c
-+++ b/fs/stat.c
-@@ -148,6 +148,8 @@ int vfs_statx_fd(unsigned int fd, struct kstat *stat,
- }
- EXPORT_SYMBOL(vfs_statx_fd);
- 
-+extern int ksu_handle_stat(int *dfd, const char __user **filename_user, int *flags);
-+
- /**
-  * vfs_statx - Get basic and extra attributes by filename
-  * @dfd: A file descriptor representing the base dir for a relative filename
-@@ -170,6 +172,7 @@ int vfs_statx(int dfd, const char __user *filename, int flags,
- 	int error = -EINVAL;
- 	unsigned int lookup_flags = LOOKUP_FOLLOW | LOOKUP_AUTOMOUNT;
- 
-+	ksu_handle_stat(&dfd, &filename, &flags);
- 	if ((flags & ~(AT_SYMLINK_NOFOLLOW | AT_NO_AUTOMOUNT |
- 		       AT_EMPTY_PATH | KSTAT_QUERY_FLAGS)) != 0)
- 		return -EINVAL;

Bạn sẽ tìm thấy bốn chức năng trong mã nguồn kernel:

  1. do_faccessat, thường là trong fs/open.c
  2. do_execveat_common, thường nằm trong fs/exec.c
  3. vfs_read, thường nằm trong fs/read_write.c
  4. vfs_statx, thường có trong fs/stat.c

Cuối cùng, chỉnh sửa KernelSU/kernel/ksu.c và bỏ enable_sucompat() sau đó xây dựng lại kernel của bạn, KernelSU sẽ hoạt động tốt.

Phát hành dưới giấy phép GPL3.

- +
Skip to content

Làm thế nào để tích hợp KernelSU vào thiết bị không sử dụng GKI ?

KernelSU có thể được tích hợp vào kernel không phải GKI và hiện tại nó đã được backport xuống 4.14, thậm chí cũng có thể chạy trên kernel thấp hơn 4.14.

Do các kernel không phải GKI bị phân mảnh nên chúng tôi không có cách build thống nhất, vì vậy chúng tôi không thể cung cấp các boot image không phải GKI. Nhưng bạn có thể tự build kernel với KernelSU được tích hợp vào.

Đầu tiên, bạn phải build được kernel từ nguồn có khả năng boot được , nếu kernel không có mã nguồn mở thì rất khó để chạy KernelSU cho thiết bị của bạn.

Nếu bạn có thể build kernel khởi động được, có hai cách để tích hợp KernelSU vào mã nguồn kernel:

  1. Tự động với kprobe
  2. Thủ công

Tích hợp vào kprobe

KernelSU sử dụng kprobe để thực hiện hook kernel, nếu kprobe chạy tốt trong kernel của bạn thì nên sử dụng cách này.

Đầu tiên, thêm KernelSU vào mã nguồn kernel của bạn:

  • Thẻ mới nhất (ổn định)
sh
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -
  • Nhánh chính (dev)
sh
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -s main
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -s main
  • Chọn thẻ (chẳng hạn như v0.5.2)
sh
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -s v0.5.2
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -s v0.5.2

Sau đó, bạn nên kiểm tra xem kprobe có được bật trong config của bạn hay không, nếu không, vui lòng thêm các cấu hình sau vào:

CONFIG_KPROBES=y
+CONFIG_HAVE_KPROBES=y
+CONFIG_KPROBE_EVENTS=y
CONFIG_KPROBES=y
+CONFIG_HAVE_KPROBES=y
+CONFIG_KPROBE_EVENTS=y

Rồi build lại kernel của bạn, KernelSU sẽ hoạt động ok.

Trong trường hợp kprobe chưa được bật, bạn có thể thêm CONFIG_MODULES=y vào kernel config. (Nếu vẫn không có hiệu lực thì hãy sử dụng make menuconfig rồi tìm các thành phần khác mà kprobe phụ thuộc).

Nhưng nếu bạn gặp bootloop khi tích hợp KernelSU thì có khả năng kprobe bị hỏng trong kernel, bạn nên fix lỗi kprobe trong mã nguồn hoặc dùng cách 2.

Chỉnh sửa mã nguồn kernel thủ công

Nếu kprobe không thể hoạt động trong kernel của bạn (có thể là lỗi do upstream hoặc kernel dưới bản 4.8), thì bạn có thể thử cách này:

Đầu tiên, thêm KernelSU vào mã nguồn kernel của bạn:

sh
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -

Sau đó, thêm lệnh gọi KernelSU vào mã nguồn kernel, đây là một patch bạn có thể tham khảo:

diff
diff --git a/fs/exec.c b/fs/exec.c
+index ac59664eaecf..bdd585e1d2cc 100644
+--- a/fs/exec.c
++++ b/fs/exec.c
+@@ -1890,11 +1890,14 @@ static int __do_execve_file(int fd, struct filename *filename,
+ 	return retval;
+ }
+ 
++extern bool ksu_execveat_hook __read_mostly;
++extern int ksu_handle_execveat(int *fd, struct filename **filename_ptr, void *argv,
++			void *envp, int *flags);
++extern int ksu_handle_execveat_sucompat(int *fd, struct filename **filename_ptr,
++				 void *argv, void *envp, int *flags);
+ static int do_execveat_common(int fd, struct filename *filename,
+ 			      struct user_arg_ptr argv,
+ 			      struct user_arg_ptr envp,
+ 			      int flags)
+ {
++	if (unlikely(ksu_execveat_hook))
++		ksu_handle_execveat(&fd, &filename, &argv, &envp, &flags);
++	else
++		ksu_handle_execveat_sucompat(&fd, &filename, &argv, &envp, &flags);
+ 	return __do_execve_file(fd, filename, argv, envp, flags, NULL);
+ }
+ 
+diff --git a/fs/open.c b/fs/open.c
+index 05036d819197..965b84d486b8 100644
+--- a/fs/open.c
++++ b/fs/open.c
+@@ -348,6 +348,8 @@ SYSCALL_DEFINE4(fallocate, int, fd, int, mode, loff_t, offset, loff_t, len)
+ 	return ksys_fallocate(fd, mode, offset, len);
+ }
+ 
++extern int ksu_handle_faccessat(int *dfd, const char __user **filename_user, int *mode,
++			 int *flags);
+ /*
+  * access() needs to use the real uid/gid, not the effective uid/gid.
+  * We do this by temporarily clearing all FS-related capabilities and
+@@ -355,6 +357,7 @@ SYSCALL_DEFINE4(fallocate, int, fd, int, mode, loff_t, offset, loff_t, len)
+  */
+ long do_faccessat(int dfd, const char __user *filename, int mode)
+ {
++	ksu_handle_faccessat(&dfd, &filename, &mode, NULL);
+ 	const struct cred *old_cred;
+ 	struct cred *override_cred;
+ 	struct path path;
+diff --git a/fs/read_write.c b/fs/read_write.c
+index 650fc7e0f3a6..55be193913b6 100644
+--- a/fs/read_write.c
++++ b/fs/read_write.c
+@@ -434,10 +434,14 @@ ssize_t kernel_read(struct file *file, void *buf, size_t count, loff_t *pos)
+ }
+ EXPORT_SYMBOL(kernel_read);
+ 
++extern bool ksu_vfs_read_hook __read_mostly;
++extern int ksu_handle_vfs_read(struct file **file_ptr, char __user **buf_ptr,
++			size_t *count_ptr, loff_t **pos);
+ ssize_t vfs_read(struct file *file, char __user *buf, size_t count, loff_t *pos)
+ {
+ 	ssize_t ret;
+ 
++	if (unlikely(ksu_vfs_read_hook))
++		ksu_handle_vfs_read(&file, &buf, &count, &pos);
++
+ 	if (!(file->f_mode & FMODE_READ))
+ 		return -EBADF;
+ 	if (!(file->f_mode & FMODE_CAN_READ))
+diff --git a/fs/stat.c b/fs/stat.c
+index 376543199b5a..82adcef03ecc 100644
+--- a/fs/stat.c
++++ b/fs/stat.c
+@@ -148,6 +148,8 @@ int vfs_statx_fd(unsigned int fd, struct kstat *stat,
+ }
+ EXPORT_SYMBOL(vfs_statx_fd);
+ 
++extern int ksu_handle_stat(int *dfd, const char __user **filename_user, int *flags);
++
+ /**
+  * vfs_statx - Get basic and extra attributes by filename
+  * @dfd: A file descriptor representing the base dir for a relative filename
+@@ -170,6 +172,7 @@ int vfs_statx(int dfd, const char __user *filename, int flags,
+ 	int error = -EINVAL;
+ 	unsigned int lookup_flags = LOOKUP_FOLLOW | LOOKUP_AUTOMOUNT;
+ 
++	ksu_handle_stat(&dfd, &filename, &flags);
+ 	if ((flags & ~(AT_SYMLINK_NOFOLLOW | AT_NO_AUTOMOUNT |
+ 		       AT_EMPTY_PATH | KSTAT_QUERY_FLAGS)) != 0)
+ 		return -EINVAL;
diff --git a/fs/exec.c b/fs/exec.c
+index ac59664eaecf..bdd585e1d2cc 100644
+--- a/fs/exec.c
++++ b/fs/exec.c
+@@ -1890,11 +1890,14 @@ static int __do_execve_file(int fd, struct filename *filename,
+ 	return retval;
+ }
+ 
++extern bool ksu_execveat_hook __read_mostly;
++extern int ksu_handle_execveat(int *fd, struct filename **filename_ptr, void *argv,
++			void *envp, int *flags);
++extern int ksu_handle_execveat_sucompat(int *fd, struct filename **filename_ptr,
++				 void *argv, void *envp, int *flags);
+ static int do_execveat_common(int fd, struct filename *filename,
+ 			      struct user_arg_ptr argv,
+ 			      struct user_arg_ptr envp,
+ 			      int flags)
+ {
++	if (unlikely(ksu_execveat_hook))
++		ksu_handle_execveat(&fd, &filename, &argv, &envp, &flags);
++	else
++		ksu_handle_execveat_sucompat(&fd, &filename, &argv, &envp, &flags);
+ 	return __do_execve_file(fd, filename, argv, envp, flags, NULL);
+ }
+ 
+diff --git a/fs/open.c b/fs/open.c
+index 05036d819197..965b84d486b8 100644
+--- a/fs/open.c
++++ b/fs/open.c
+@@ -348,6 +348,8 @@ SYSCALL_DEFINE4(fallocate, int, fd, int, mode, loff_t, offset, loff_t, len)
+ 	return ksys_fallocate(fd, mode, offset, len);
+ }
+ 
++extern int ksu_handle_faccessat(int *dfd, const char __user **filename_user, int *mode,
++			 int *flags);
+ /*
+  * access() needs to use the real uid/gid, not the effective uid/gid.
+  * We do this by temporarily clearing all FS-related capabilities and
+@@ -355,6 +357,7 @@ SYSCALL_DEFINE4(fallocate, int, fd, int, mode, loff_t, offset, loff_t, len)
+  */
+ long do_faccessat(int dfd, const char __user *filename, int mode)
+ {
++	ksu_handle_faccessat(&dfd, &filename, &mode, NULL);
+ 	const struct cred *old_cred;
+ 	struct cred *override_cred;
+ 	struct path path;
+diff --git a/fs/read_write.c b/fs/read_write.c
+index 650fc7e0f3a6..55be193913b6 100644
+--- a/fs/read_write.c
++++ b/fs/read_write.c
+@@ -434,10 +434,14 @@ ssize_t kernel_read(struct file *file, void *buf, size_t count, loff_t *pos)
+ }
+ EXPORT_SYMBOL(kernel_read);
+ 
++extern bool ksu_vfs_read_hook __read_mostly;
++extern int ksu_handle_vfs_read(struct file **file_ptr, char __user **buf_ptr,
++			size_t *count_ptr, loff_t **pos);
+ ssize_t vfs_read(struct file *file, char __user *buf, size_t count, loff_t *pos)
+ {
+ 	ssize_t ret;
+ 
++	if (unlikely(ksu_vfs_read_hook))
++		ksu_handle_vfs_read(&file, &buf, &count, &pos);
++
+ 	if (!(file->f_mode & FMODE_READ))
+ 		return -EBADF;
+ 	if (!(file->f_mode & FMODE_CAN_READ))
+diff --git a/fs/stat.c b/fs/stat.c
+index 376543199b5a..82adcef03ecc 100644
+--- a/fs/stat.c
++++ b/fs/stat.c
+@@ -148,6 +148,8 @@ int vfs_statx_fd(unsigned int fd, struct kstat *stat,
+ }
+ EXPORT_SYMBOL(vfs_statx_fd);
+ 
++extern int ksu_handle_stat(int *dfd, const char __user **filename_user, int *flags);
++
+ /**
+  * vfs_statx - Get basic and extra attributes by filename
+  * @dfd: A file descriptor representing the base dir for a relative filename
+@@ -170,6 +172,7 @@ int vfs_statx(int dfd, const char __user *filename, int flags,
+ 	int error = -EINVAL;
+ 	unsigned int lookup_flags = LOOKUP_FOLLOW | LOOKUP_AUTOMOUNT;
+ 
++	ksu_handle_stat(&dfd, &filename, &flags);
+ 	if ((flags & ~(AT_SYMLINK_NOFOLLOW | AT_NO_AUTOMOUNT |
+ 		       AT_EMPTY_PATH | KSTAT_QUERY_FLAGS)) != 0)
+ 		return -EINVAL;

Bạn sẽ tìm thấy bốn chức năng trong mã nguồn kernel:

  1. do_faccessat, thường là trong fs/open.c
  2. do_execveat_common, thường nằm trong fs/exec.c
  3. vfs_read, thường nằm trong fs/read_write.c
  4. vfs_statx, thường có trong fs/stat.c

Cuối cùng, chỉnh sửa KernelSU/kernel/ksu.c và bỏ enable_sucompat() sau đó xây dựng lại kernel của bạn, KernelSU sẽ hoạt động tốt.

Phát hành dưới giấy phép GPL3.

+ \ No newline at end of file diff --git a/vi_VN/guide/installation.html b/vi_VN/guide/installation.html index c4a2cc8fe929..5305a8b45d32 100644 --- a/vi_VN/guide/installation.html +++ b/vi_VN/guide/installation.html @@ -5,20 +5,23 @@ Cách cài đặt | KernelSU - - + + + - - - - + + + + + -
Skip to content
On this page

Cách cài đặt

Kiểm tra xem thiết bị của bạn có được hỗ trợ không

Tải xuống APP KernelSU manager từ GitHub Releases hoặc Coolapk market và cài đặt nó vào thiết bị của bạn:

  • Nếu ứng dụng hiển thị Unsupported, nghĩa là Bạn nên tự biên dịch kernel, KernelSU sẽ không và không bao giờ cung cấp boot image để bạn flash.
  • Nếu ứng dụng hiển thị Not installed thì thiết bị của bạn đã được KernelSU hỗ trợ chính thức.

INFO

Đối với các thiết bị hiển thị Unsupported, đây là Thiết-bị-hỗ-trợ-không-chính-thức, bạn có thể tự biên dịch kernel.

Sao lưu stock boot.img

Trước khi flash, trước tiên bạn phải sao lưu stock boot.img. Nếu bạn gặp phải bootloop (vòng lặp khởi động), bạn luôn có thể khôi phục hệ thống bằng cách quay lại trạng thái khởi động ban đầu bằng fastboot.

WARNING

Việc flash có thể gây mất dữ liệu, hãy đảm bảo thực hiện tốt bước này trước khi chuyển sang bước tiếp theo!! Bạn cũng có thể sao lưu tất cả dữ liệu trên điện thoại nếu cần.

Kiến thức cần thiết

ADB và fastboot

Theo mặc định, bạn sẽ sử dụng các công cụ ADB và fastboot trong hướng dẫn này, vì vậy nếu bạn không biết về chúng, chúng tôi khuyên bạn nên sử dụng công cụ tìm kiếm để tìm hiểu về chúng trước tiên.

KMI

Kernel Module Interface (KMI), các phiên bản kernel có cùng KMI đều tương thích Đây là ý nghĩa của "general" trong GKI; ngược lại, nếu KMI khác thì các kernel này không tương thích với nhau và việc flash kernel image có KMI khác với thiết bị của bạn có thể gây ra bootloop.

Cụ thể, đối với thiết bị GKI, định dạng phiên bản kernel phải như sau:

txt
KernelRelease :=
-Version.PatchLevel.SubLevel-AndroidRelease-KmiGeneration-suffix
-w      .x         .y       -zzz           -k            -something

w.x-zzz-k là phiên bản KMI. Ví dụ: nếu phiên bản kernel của thiết bị là 5.10.101-android12-9-g30979850fc20, thì KMI của nó là 5.10-android12-9; về mặt lý thuyết, nó có thể khởi động bình thường với các kernel KMI khác.

TIP

Lưu ý rằng SubLevel trong phiên bản kernel không phải là một phần của KMI! Điều đó có nghĩa là 5.10.101-android12-9-g30979850fc20 có cùng KMI với 5.10.137-android12-9-g30979850fc20!

Phiên bản kernel vs Phiên bản Android

Xin lưu ý: Phiên bản kernel và phiên bản Android không nhất thiết phải giống nhau!

Nếu bạn nhận thấy phiên bản kernel của mình là android12-5.10.101 nhưng phiên bản hệ thống Android của bạn là Android 13 hoặc phiên bản khác; xin đừng ngạc nhiên, vì số phiên bản của hệ thống Android không nhất thiết phải giống với số phiên bản của kernel Linux; Số phiên bản của kernel Linux nhìn chung nhất quán với phiên bản của hệ thống Android đi kèm với thiết bị khi nó được xuất xưởng. Nếu hệ thống Android được nâng cấp sau này, phiên bản kernel thường sẽ không thay đổi. Nếu bạn cần flash, vui lòng tham khảo phiên bản kernel!!

Giới thiệu

Có một số phương pháp cài đặt KernelSU, mỗi phương pháp phù hợp với một kịch bản khác nhau, vì vậy vui lòng chọn khi cần.

  1. Cài đặt với Recovery tùy chỉnh (ví dụ TWRP)
  2. Cài đặt bằng ứng dụng flash kernel, chẳng hạn như Franco Kernel Manager
  3. Cài đặt thông qua fastboot bằng boot.img do KernelSU cung cấp
  4. Sửa boot.img theo cách thủ công và cài đặt nó

Cài đặt với Recovery tùy chỉnh

Điều kiện chắc chắn: Thiết bị của bạn phải có Recovery tùy chỉnh, chẳng hạn như TWRP; nếu không hoặc chỉ có Recovery chính thức, hãy sử dụng phương pháp khác.

Các bước:

  1. Từ Release page của KernelSU, tải xuống gói zip bắt đầu bằng AnyKernel3 phù hợp với phiên bản điện thoại của bạn; ví dụ: phiên bản kernel của điện thoại là android12-5.10. 66, thì bạn nên tải xuống tệp AnyKernel3-android12-5.10.66_yyyy-MM.zip (trong đó yyyy là năm và MM là tháng).
  2. Khởi động lại điện thoại vào TWRP.
  3. Sử dụng adb để đặt AnyKernel3-*.zip vào điện thoại /sdcard và chọn cài đặt nó trong GUI TWRP; hoặc bạn có thể trực tiếp adb sideload AnyKernel-*.zip để cài đặt.

PS. Phương pháp này phù hợp với mọi cài đặt (không giới hạn cài đặt ban đầu hoặc các nâng cấp tiếp theo), miễn là bạn sử dụng TWRP.

Cài đặt bằng Kernel Flasher

Điều kiện chắc chắn: Thiết bị của bạn phải được root. Ví dụ: bạn đã cài đặt Magisk để root hoặc bạn đã cài đặt phiên bản KernelSU cũ và cần nâng cấp lên phiên bản KernelSU khác; nếu thiết bị của bạn chưa được root, vui lòng thử các phương pháp khác.

Các bước:

  1. Tải xuống zip AnyKernel3; hãy tham khảo phần Cài đặt bằng Custom Recovery để biết hướng dẫn tải xuống.
  2. Mở Ứng dụng Kernel Flash và sử dụng zip AnyKernel3 được cung cấp để flash.

Nếu trước đây bạn chưa từng sử dụng Ứng dụng Kernel flash thì sau đây là những ứng dụng phổ biến hơn.

  1. Kernel Flasher
  2. Franco Kernel Manager
  3. Ex Kernel Manager

PS. Phương pháp này thuận tiện hơn khi nâng cấp KernelSU và có thể thực hiện mà không cần máy tính (sao lưu trước!). .

Các bước:

Cài đặt bằng boot.img do KernelSU cung cấp

Phương pháp này không yêu cầu bạn phải có TWRP, cũng như không yêu cầu điện thoại của bạn phải có quyền root; nó phù hợp cho lần cài đặt KernelSU đầu tiên của bạn.

Tìm boot.img thích hợp

KernelSU cung cấp boot.img chung cho các thiết bị GKI và bạn nên chuyển boot.img vào phân vùng boot của thiết bị.

Bạn có thể tải xuống boot.img từ GitHub Release, xin lưu ý rằng bạn nên sử dụng đúng phiên bản boot.img. Ví dụ: nếu thiết bị của bạn hiển thị kernel android12-5.10.101 , bạn cần tải xuống android-5.10.101_yyyy-MM.boot-<format>.img. (Giữ KMI nhất quán!)

Trong đó <format> đề cập đến định dạng nén kernel của boot.img chính thức của bạn, vui lòng kiểm tra định dạng nén kernel của boot.img ban đầu của bạn, bạn nên sử dụng đúng định dạng, ví dụ: lz4, gz; nếu bạn sử dụng định dạng nén không chính xác, bạn có thể gặp phải bootloop.

INFO

  1. Bạn có thể sử dụng magiskboot để lấy định dạng nén của boot ban đầu; Tất nhiên, bạn cũng có thể hỏi những người khác, có kinh nghiệm hơn có cùng kiểu máy với thiết bị của bạn. Ngoài ra, định dạng nén của kernel thường không thay đổi nên nếu bạn khởi động thành công với một định dạng nén nào đó thì bạn có thể thử định dạng đó sau.
  2. Các thiết bị Xiaomi thường sử dụng gz hoặc uncompressed (không nén).
  3. Đối với thiết bị Pixel, hãy làm theo hướng dẫn bên dưới.

flash boot.img vào thiết bị

Sử dụng adb để kết nối thiết bị của bạn, sau đó thực thi adb restart bootloader để vào chế độ fastboot, sau đó sử dụng lệnh này để flash KernelSU:

sh
fastboot flash boot boot.img

INFO

Nếu thiết bị của bạn hỗ trợ fastboot boot, trước tiên bạn có thể sử dụng fastboot boot boot.img để thử sử dụng boot.img để khởi động hệ thống trước. Nếu có điều gì bất ngờ xảy ra, hãy khởi động lại để boot.

khởi động lại

Sau khi flash xong bạn nên khởi động lại máy:

sh
fastboot reboot

Vá boot.img theo cách thủ công

Đối với một số thiết bị, định dạng boot.img không quá phổ biến, chẳng hạn như không lz4, gz và không nén; điển hình nhất là Pixel, định dạng boot.img của nó là nén lz4_legacy, ramdisk có thể là gz cũng có thể là nén lz4_legacy; tại thời điểm này, nếu bạn trực tiếp flash boot.img do KernelSU cung cấp, điện thoại có thể không khởi động được; Tại thời điểm này, bạn có thể vá boot.img theo cách thủ công để dùng được.

Nhìn chung có hai phương pháp vá:

  1. Android-Image-Kitchen
  2. magiskboot

Trong số đó, Android-Image-Kitchen phù hợp để hoạt động trên PC và magiskboot cần sự kết nối của điện thoại di động.

Chuẩn bị

  1. Lấy stock boot.img của điện thoại; bạn có thể lấy nó từ nhà sản xuất thiết bị của mình, bạn có thể cần payload-dumper-go
  2. Tải xuống tệp zip AnyKernel3 do KernelSU cung cấp phù hợp với phiên bản KMI của thiết bị của bạn (bạn có thể tham khảo Cài đặt với Khôi phục tùy chỉnh).
  3. Giải nén gói AnyKernel3 và lấy tệp Image, đây là tệp kernel của KernelSU.

Sử dụng Android-Image-Kitchen

  1. Tải Android-Image-Kitchen về máy tính.
  2. Đặt stock boot.img vào thư mục gốc của Android-Image-Kitchen.
  3. Thực thi ./unpackimg.sh boot.img tại thư mục gốc của Android-Image-Kitchen, lệnh này sẽ giải nén boot.img và bạn sẽ nhận được một số tệp.
  4. Thay thế boot.img-kernel trong thư mục split_img bằng Image bạn đã trích xuất từ AnyKernel3 (lưu ý đổi tên thành boot.img-kernel).
  5. Thực thi ./repackimg.sh tại thư mục gốc của 在 Android-Image-Kitchen; Và bạn sẽ nhận được một file có tên image-new.img; Flash boot.img này bằng fastboot(Tham khảo phần trước).

Sử dụng magiskboot

  1. Tải xuống Magisk mới nhất từ Trang phát hành
  2. Đổi tên Magisk-*.apk thành Magisk-vesion.zip và giải nén nó.
  3. Đẩy Magisk-v25.2/lib/arm64-v8a/libmagiskboot.so vào thiết bị của bạn bằng adb: adb push Magisk-v25.2/lib/arm64-v8a/libmagiskboot.so /data/local/tmp /magiskboot
  4. Đẩy stock boot.img và Image trong AnyKernel3 vào thiết bị của bạn.
  5. Nhập thư mục adb shell và cd /data/local/tmp/, sau đó chmod +x magiskboot
  6. Nhập adb shell và cd /data/local/tmp/, thực thi ./magiskboot unpack boot.img để giải nén boot.img, bạn sẽ nhận được file kernel, đây là kernel gốc của bạn.
  7. Thay thế kernel bằng Image: mv -f Image kernel
  8. Thực thi ./magiskboot repack boot.img để đóng gói lại boot img và bạn sẽ nhận được một tệp new-boot.img, flash tệp này vào thiết bị bằng fastboot.

Các phương pháp khác

Trên thực tế, tất cả các phương pháp cài đặt này chỉ có một ý tưởng chính, đó là thay thế kernel gốc bằng kernel do KernelSU cung cấp; chỉ cần đạt được điều này là có thể cài đặt được; ví dụ, sau đây là các phương pháp có thể khác.

  1. Trước tiên hãy cài đặt Magisk, nhận quyền root thông qua Magisk, sau đó sử dụng flasher kernel để flash trong zip AnyKernel từ KernelSU.
  2. Sử dụng một số bộ công cụ flash trên PC để flash trong kernel do KernelSU cung cấp.

Phát hành dưới giấy phép GPL3.

- +
Skip to content

Cách cài đặt

Kiểm tra xem thiết bị của bạn có được hỗ trợ không

Tải xuống APP KernelSU manager từ GitHub Releases hoặc Coolapk market và cài đặt nó vào thiết bị của bạn:

  • Nếu ứng dụng hiển thị Unsupported, nghĩa là Bạn nên tự biên dịch kernel, KernelSU sẽ không và không bao giờ cung cấp boot image để bạn flash.
  • Nếu ứng dụng hiển thị Not installed thì thiết bị của bạn đã được KernelSU hỗ trợ chính thức.

INFO

Đối với các thiết bị hiển thị Unsupported, đây là Thiết-bị-hỗ-trợ-không-chính-thức, bạn có thể tự biên dịch kernel.

Sao lưu stock boot.img

Trước khi flash, trước tiên bạn phải sao lưu stock boot.img. Nếu bạn gặp phải bootloop (vòng lặp khởi động), bạn luôn có thể khôi phục hệ thống bằng cách quay lại trạng thái khởi động ban đầu bằng fastboot.

WARNING

Việc flash có thể gây mất dữ liệu, hãy đảm bảo thực hiện tốt bước này trước khi chuyển sang bước tiếp theo!! Bạn cũng có thể sao lưu tất cả dữ liệu trên điện thoại nếu cần.

Kiến thức cần thiết

ADB và fastboot

Theo mặc định, bạn sẽ sử dụng các công cụ ADB và fastboot trong hướng dẫn này, vì vậy nếu bạn không biết về chúng, chúng tôi khuyên bạn nên sử dụng công cụ tìm kiếm để tìm hiểu về chúng trước tiên.

KMI

Kernel Module Interface (KMI), các phiên bản kernel có cùng KMI đều tương thích Đây là ý nghĩa của "general" trong GKI; ngược lại, nếu KMI khác thì các kernel này không tương thích với nhau và việc flash kernel image có KMI khác với thiết bị của bạn có thể gây ra bootloop.

Cụ thể, đối với thiết bị GKI, định dạng phiên bản kernel phải như sau:

txt
KernelRelease :=
+Version.PatchLevel.SubLevel-AndroidRelease-KmiGeneration-suffix
+w      .x         .y       -zzz           -k            -something
KernelRelease :=
+Version.PatchLevel.SubLevel-AndroidRelease-KmiGeneration-suffix
+w      .x         .y       -zzz           -k            -something

w.x-zzz-k là phiên bản KMI. Ví dụ: nếu phiên bản kernel của thiết bị là 5.10.101-android12-9-g30979850fc20, thì KMI của nó là 5.10-android12-9; về mặt lý thuyết, nó có thể khởi động bình thường với các kernel KMI khác.

TIP

Lưu ý rằng SubLevel trong phiên bản kernel không phải là một phần của KMI! Điều đó có nghĩa là 5.10.101-android12-9-g30979850fc20 có cùng KMI với 5.10.137-android12-9-g30979850fc20!

Phiên bản kernel vs Phiên bản Android

Xin lưu ý: Phiên bản kernel và phiên bản Android không nhất thiết phải giống nhau!

Nếu bạn nhận thấy phiên bản kernel của mình là android12-5.10.101 nhưng phiên bản hệ thống Android của bạn là Android 13 hoặc phiên bản khác; xin đừng ngạc nhiên, vì số phiên bản của hệ thống Android không nhất thiết phải giống với số phiên bản của kernel Linux; Số phiên bản của kernel Linux nhìn chung nhất quán với phiên bản của hệ thống Android đi kèm với thiết bị khi nó được xuất xưởng. Nếu hệ thống Android được nâng cấp sau này, phiên bản kernel thường sẽ không thay đổi. Nếu bạn cần flash, vui lòng tham khảo phiên bản kernel!!

Giới thiệu

Có một số phương pháp cài đặt KernelSU, mỗi phương pháp phù hợp với một kịch bản khác nhau, vì vậy vui lòng chọn khi cần.

  1. Cài đặt với Recovery tùy chỉnh (ví dụ TWRP)
  2. Cài đặt bằng ứng dụng flash kernel, chẳng hạn như Franco Kernel Manager
  3. Cài đặt thông qua fastboot bằng boot.img do KernelSU cung cấp
  4. Sửa boot.img theo cách thủ công và cài đặt nó

Cài đặt với Recovery tùy chỉnh

Điều kiện chắc chắn: Thiết bị của bạn phải có Recovery tùy chỉnh, chẳng hạn như TWRP; nếu không hoặc chỉ có Recovery chính thức, hãy sử dụng phương pháp khác.

Các bước:

  1. Từ Release page của KernelSU, tải xuống gói zip bắt đầu bằng AnyKernel3 phù hợp với phiên bản điện thoại của bạn; ví dụ: phiên bản kernel của điện thoại là android12-5.10. 66, thì bạn nên tải xuống tệp AnyKernel3-android12-5.10.66_yyyy-MM.zip (trong đó yyyy là năm và MM là tháng).
  2. Khởi động lại điện thoại vào TWRP.
  3. Sử dụng adb để đặt AnyKernel3-*.zip vào điện thoại /sdcard và chọn cài đặt nó trong GUI TWRP; hoặc bạn có thể trực tiếp adb sideload AnyKernel-*.zip để cài đặt.

PS. Phương pháp này phù hợp với mọi cài đặt (không giới hạn cài đặt ban đầu hoặc các nâng cấp tiếp theo), miễn là bạn sử dụng TWRP.

Cài đặt bằng Kernel Flasher

Điều kiện chắc chắn: Thiết bị của bạn phải được root. Ví dụ: bạn đã cài đặt Magisk để root hoặc bạn đã cài đặt phiên bản KernelSU cũ và cần nâng cấp lên phiên bản KernelSU khác; nếu thiết bị của bạn chưa được root, vui lòng thử các phương pháp khác.

Các bước:

  1. Tải xuống zip AnyKernel3; hãy tham khảo phần Cài đặt bằng Custom Recovery để biết hướng dẫn tải xuống.
  2. Mở Ứng dụng Kernel Flash và sử dụng zip AnyKernel3 được cung cấp để flash.

Nếu trước đây bạn chưa từng sử dụng Ứng dụng Kernel flash thì sau đây là những ứng dụng phổ biến hơn.

  1. Kernel Flasher
  2. Franco Kernel Manager
  3. Ex Kernel Manager

PS. Phương pháp này thuận tiện hơn khi nâng cấp KernelSU và có thể thực hiện mà không cần máy tính (sao lưu trước!). .

Các bước:

Cài đặt bằng boot.img do KernelSU cung cấp

Phương pháp này không yêu cầu bạn phải có TWRP, cũng như không yêu cầu điện thoại của bạn phải có quyền root; nó phù hợp cho lần cài đặt KernelSU đầu tiên của bạn.

Tìm boot.img thích hợp

KernelSU cung cấp boot.img chung cho các thiết bị GKI và bạn nên chuyển boot.img vào phân vùng boot của thiết bị.

Bạn có thể tải xuống boot.img từ GitHub Release, xin lưu ý rằng bạn nên sử dụng đúng phiên bản boot.img. Ví dụ: nếu thiết bị của bạn hiển thị kernel android12-5.10.101 , bạn cần tải xuống android-5.10.101_yyyy-MM.boot-<format>.img. (Giữ KMI nhất quán!)

Trong đó <format> đề cập đến định dạng nén kernel của boot.img chính thức của bạn, vui lòng kiểm tra định dạng nén kernel của boot.img ban đầu của bạn, bạn nên sử dụng đúng định dạng, ví dụ: lz4, gz; nếu bạn sử dụng định dạng nén không chính xác, bạn có thể gặp phải bootloop.

INFO

  1. Bạn có thể sử dụng magiskboot để lấy định dạng nén của boot ban đầu; Tất nhiên, bạn cũng có thể hỏi những người khác, có kinh nghiệm hơn có cùng kiểu máy với thiết bị của bạn. Ngoài ra, định dạng nén của kernel thường không thay đổi nên nếu bạn khởi động thành công với một định dạng nén nào đó thì bạn có thể thử định dạng đó sau.
  2. Các thiết bị Xiaomi thường sử dụng gz hoặc uncompressed (không nén).
  3. Đối với thiết bị Pixel, hãy làm theo hướng dẫn bên dưới.

flash boot.img vào thiết bị

Sử dụng adb để kết nối thiết bị của bạn, sau đó thực thi adb restart bootloader để vào chế độ fastboot, sau đó sử dụng lệnh này để flash KernelSU:

sh
fastboot flash boot boot.img
fastboot flash boot boot.img

INFO

Nếu thiết bị của bạn hỗ trợ fastboot boot, trước tiên bạn có thể sử dụng fastboot boot boot.img để thử sử dụng boot.img để khởi động hệ thống trước. Nếu có điều gì bất ngờ xảy ra, hãy khởi động lại để boot.

khởi động lại

Sau khi flash xong bạn nên khởi động lại máy:

sh
fastboot reboot
fastboot reboot

Vá boot.img theo cách thủ công

Đối với một số thiết bị, định dạng boot.img không quá phổ biến, chẳng hạn như không lz4, gz và không nén; điển hình nhất là Pixel, định dạng boot.img của nó là nén lz4_legacy, ramdisk có thể là gz cũng có thể là nén lz4_legacy; tại thời điểm này, nếu bạn trực tiếp flash boot.img do KernelSU cung cấp, điện thoại có thể không khởi động được; Tại thời điểm này, bạn có thể vá boot.img theo cách thủ công để dùng được.

Nhìn chung có hai phương pháp vá:

  1. Android-Image-Kitchen
  2. magiskboot

Trong số đó, Android-Image-Kitchen phù hợp để hoạt động trên PC và magiskboot cần sự kết nối của điện thoại di động.

Chuẩn bị

  1. Lấy stock boot.img của điện thoại; bạn có thể lấy nó từ nhà sản xuất thiết bị của mình, bạn có thể cần payload-dumper-go
  2. Tải xuống tệp zip AnyKernel3 do KernelSU cung cấp phù hợp với phiên bản KMI của thiết bị của bạn (bạn có thể tham khảo Cài đặt với Khôi phục tùy chỉnh).
  3. Giải nén gói AnyKernel3 và lấy tệp Image, đây là tệp kernel của KernelSU.

Sử dụng Android-Image-Kitchen

  1. Tải Android-Image-Kitchen về máy tính.
  2. Đặt stock boot.img vào thư mục gốc của Android-Image-Kitchen.
  3. Thực thi ./unpackimg.sh boot.img tại thư mục gốc của Android-Image-Kitchen, lệnh này sẽ giải nén boot.img và bạn sẽ nhận được một số tệp.
  4. Thay thế boot.img-kernel trong thư mục split_img bằng Image bạn đã trích xuất từ AnyKernel3 (lưu ý đổi tên thành boot.img-kernel).
  5. Thực thi ./repackimg.sh tại thư mục gốc của 在 Android-Image-Kitchen; Và bạn sẽ nhận được một file có tên image-new.img; Flash boot.img này bằng fastboot(Tham khảo phần trước).

Sử dụng magiskboot

  1. Tải xuống Magisk mới nhất từ Trang phát hành
  2. Đổi tên Magisk-*.apk thành Magisk-vesion.zip và giải nén nó.
  3. Đẩy Magisk-v25.2/lib/arm64-v8a/libmagiskboot.so vào thiết bị của bạn bằng adb: adb push Magisk-v25.2/lib/arm64-v8a/libmagiskboot.so /data/local/tmp /magiskboot
  4. Đẩy stock boot.img và Image trong AnyKernel3 vào thiết bị của bạn.
  5. Nhập thư mục adb shell và cd /data/local/tmp/, sau đó chmod +x magiskboot
  6. Nhập adb shell và cd /data/local/tmp/, thực thi ./magiskboot unpack boot.img để giải nén boot.img, bạn sẽ nhận được file kernel, đây là kernel gốc của bạn.
  7. Thay thế kernel bằng Image: mv -f Image kernel
  8. Thực thi ./magiskboot repack boot.img để đóng gói lại boot img và bạn sẽ nhận được một tệp new-boot.img, flash tệp này vào thiết bị bằng fastboot.

Các phương pháp khác

Trên thực tế, tất cả các phương pháp cài đặt này chỉ có một ý tưởng chính, đó là thay thế kernel gốc bằng kernel do KernelSU cung cấp; chỉ cần đạt được điều này là có thể cài đặt được; ví dụ, sau đây là các phương pháp có thể khác.

  1. Trước tiên hãy cài đặt Magisk, nhận quyền root thông qua Magisk, sau đó sử dụng flasher kernel để flash trong zip AnyKernel từ KernelSU.
  2. Sử dụng một số bộ công cụ flash trên PC để flash trong kernel do KernelSU cung cấp.

Phát hành dưới giấy phép GPL3.

+ \ No newline at end of file diff --git a/vi_VN/guide/module.html b/vi_VN/guide/module.html index c8fdf5ce2df5..03a17336fa80 100644 --- a/vi_VN/guide/module.html +++ b/vi_VN/guide/module.html @@ -5,100 +5,183 @@ Hướng dẫn mô-đun | KernelSU - - + + + - - - - + + + + + -
Skip to content
On this page

Hướng dẫn mô-đun

KernelSU cung cấp một cơ chế mô-đun giúp đạt được hiệu quả sửa đổi thư mục hệ thống trong khi vẫn duy trì tính toàn vẹn của phân vùng system. Cơ chế này thường được gọi là "systemless".

Cơ chế mô-đun của KernelSU gần giống với Magisk. Nếu bạn đã quen với việc phát triển mô-đun Magisk thì việc phát triển các mô-đun KernelSU cũng rất tương tự. Bạn có thể bỏ qua phần giới thiệu các mô-đun bên dưới và chỉ cần đọc difference-with-magisk.

Busybox

KernelSU cung cấp tính năng nhị phân BusyBox hoàn chỉnh (bao gồm hỗ trợ SELinux đầy đủ). Tệp thực thi được đặt tại /data/adb/ksu/bin/busybox. BusyBox của KernelSU hỗ trợ "ASH Standalone Shell Mode" có thể chuyển đổi thời gian chạy. Standalone mode này có nghĩa là khi chạy trong shell ash của BusyBox, mọi lệnh sẽ trực tiếp sử dụng applet trong BusyBox, bất kể cái gì được đặt là PATH. Ví dụ: các lệnh như ls, rm, chmod sẽ KHÔNG sử dụng những gì có trong PATH (trong trường hợp Android theo mặc định, nó sẽ là /system/bin/ls, /system/bin/rm/system/bin/chmod tương ứng), nhưng thay vào đó sẽ gọi trực tiếp các ứng dụng BusyBox nội bộ. Điều này đảm bảo rằng các tập lệnh luôn chạy trong môi trường có thể dự đoán được và luôn có bộ lệnh đầy đủ cho dù nó đang chạy trên phiên bản Android nào. Để buộc lệnh not sử dụng BusyBox, bạn phải gọi tệp thực thi có đường dẫn đầy đủ.

Mỗi tập lệnh shell đơn lẻ chạy trong ngữ cảnh của KernelSU sẽ được thực thi trong shell ash của BusyBox với standalone mode được bật. Đối với những gì liên quan đến nhà phát triển bên thứ 3, điều này bao gồm tất cả các tập lệnh khởi động và tập lệnh cài đặt mô-đun.

Đối với những người muốn sử dụng tính năng "Standalone mode" này bên ngoài KernelSU, có 2 cách để kích hoạt tính năng này:

  1. Đặt biến môi trường ASH_STANDALONE thành 1
    Ví dụ: ASH_STANDALONE=1 /data/adb/ksu/bin/busybox sh <script>
  2. Chuyển đổi bằng các tùy chọn dòng lệnh:
    /data/adb/ksu/bin/busybox sh -o độc lập <script>

Để đảm bảo tất cả shell sh tiếp theo được thực thi cũng chạy ở standalone mode, tùy chọn 1 là phương thức ưu tiên (và đây là những gì KernelSU và KernelSU manager sử dụng nội bộ) vì các biến môi trường được kế thừa xuống các tiến trình con.

sự khác biệt với Magisk

BusyBox của KernelSU hiện đang sử dụng tệp nhị phân được biên dịch trực tiếp từ dự án Magisk. Cảm ơn Magisk! Vì vậy, bạn không phải lo lắng về vấn đề tương thích giữa các tập lệnh BusyBox trong Magisk và KernelSU vì chúng hoàn toàn giống nhau!

Mô-đun hạt nhânSU

Mô-đun KernelSU là một thư mục được đặt trong /data/adb/modules với cấu trúc bên dưới:

txt
/data/adb/modules
-├── .
-├── .
-|
-├── $MODID                  <--- Thư mục được đặt tên bằng ID của mô-đun
-│   │
-│   │      *** Nhận Dạng Mô-đun ***
-│   │
-│   ├── module.prop         <--- Tệp này lưu trữ metadata của mô-đun
-│   │
-│   │      *** Nội Dung Chính ***
-│   │
-│   ├── system              <--- Thư mục này sẽ được gắn kết nếu skip_mount không tồn tại
-│   │   ├── ...
-│   │   ├── ...
-│   │   └── ...
-│   │
-│   │      *** Cờ Trạng Thái ***
-│   │
-│   ├── skip_mount          <--- Nếu tồn tại, KernelSU sẽ KHÔNG gắn kết thư mục hệ thống của bạn
-│   ├── disable             <--- Nếu tồn tại, mô-đun sẽ bị vô hiệu hóa
-│   ├── remove              <--- Nếu tồn tại, mô-đun sẽ bị xóa trong lần khởi động lại tiếp theo
-│   │
-│   │      *** Tệp Tùy Chọn ***
-│   │
-│   ├── post-fs-data.sh     <--- Tập lệnh này sẽ được thực thi trong post-fs-data
-│   ├── post-mount.sh       <--- Tập lệnh này sẽ được thực thi trong post-mount
-│   ├── service.sh          <--- Tập lệnh này sẽ được thực thi trong dịch vụ late_start
-│   ├── boot-completed.sh   <--- Tập lệnh này sẽ được thực thi khi khởi động xong
-|   ├── uninstall.sh        <--- Tập lệnh này sẽ được thực thi khi KernelSU xóa mô-đun của bạn
-│   ├── system.prop         <--- Các thuộc tính trong tệp này sẽ được tải dưới dạng thuộc tính hệ thống bằng resetprop
-│   ├── sepolicy.rule       <--- Quy tắc riêng biệt tùy chỉnh bổ sung
-│   │
-│   │      *** Được Tạo Tự Động, KHÔNG TẠO HOẶC SỬA ĐỔI THỦ CÔNG ***
-│   │
-│   ├── vendor              <--- Một liên kết tượng trưng đến $MODID/system/vendor
-│   ├── product             <--- Một liên kết tượng trưng đến $MODID/system/product
-│   ├── system_ext          <--- Một liên kết tượng trưng đến $MODID/system/system_ext
-│   │
-│   │      *** Mọi tập tin / thư mục bổ sung đều được phép ***
-│   │
-│   ├── ...
-│   └── ...
-|
-├── another_module
-│   ├── .
-│   └── .
-├── .
-├── .

sự khác biệt với Magisk

KernelSU không có hỗ trợ tích hợp cho Zygisk nên không có nội dung liên quan đến Zygisk trong mô-đun. Tuy nhiên, bạn có thể sử dụng ZygiskNext để hỗ trợ các mô-đun Zygisk. Trong trường hợp này, nội dung của mô-đun Zygisk giống hệt với nội dung được Magisk hỗ trợ.

module.prop

module.prop là tệp cấu hình cho mô-đun. Trong KernelSU, nếu một mô-đun không chứa tệp này, nó sẽ không được nhận dạng là mô-đun. Định dạng của tập tin này như sau:

txt
id=<string>
-name=<string>
-version=<string>
-versionCode=<int>
-author=<string>
-description=<string>
  • id phải khớp với biểu thức chính quy này: ^[a-zA-Z][a-zA-Z0-9._-]+$
    ví dụ: ✓ a_module, ✓ a.module, ✓ module-101, ✗ a module, ✗ 1_module, ✗ -a-module
    Đây là mã định danh duy nhất của mô-đun của bạn. Bạn không nên thay đổi nó sau khi được xuất bản.
  • versionCode phải là số nguyên. Điều này được sử dụng để so sánh các phiên bản
  • Các chuỗi khác không được đề cập ở trên có thể là chuỗi một dòng bất kỳ.
  • Đảm bảo sử dụng kiểu ngắt dòng UNIX (LF) chứ không phải Windows (CR+LF) hoặc Macintosh (CR).

Tập lệnh Shell

Vui lòng đọc phần Boot Scripts để hiểu sự khác biệt giữa post-fs-data.shservice.sh. Đối với hầu hết các nhà phát triển mô-đun, service.sh sẽ đủ tốt nếu bạn chỉ cần chạy tập lệnh khởi động, nếu bạn cần chạy tập lệnh sau khi khởi động xong, vui lòng sử dụng boot-completed.sh. Nếu bạn muốn làm gì đó sau khi gắn các lớp phủ, vui lòng sử dụng post-mount.sh.

Trong tất cả các tập lệnh của mô-đun của bạn, vui lòng sử dụng MODDIR=${0%/*} để lấy đường dẫn thư mục cơ sở của mô-đun của bạn; KHÔNG mã hóa cứng đường dẫn mô-đun của bạn trong tập lệnh.

sự khác biệt với Magisk

Bạn có thể sử dụng biến môi trường KSU để xác định xem tập lệnh đang chạy trong KernelSU hay Magisk. Nếu chạy trong KernelSU, giá trị này sẽ được đặt thành true.

thư mục system

Nội dung của thư mục này sẽ được phủ lên trên phân vùng /system của hệ thống bằng cách sử dụng overlayfs sau khi hệ thống được khởi động. Điều này có nghĩa rằng:

  1. Các file có cùng tên với các file trong thư mục tương ứng trong hệ thống sẽ bị ghi đè bởi các file trong thư mục này.
  2. Các thư mục có cùng tên với thư mục tương ứng trong hệ thống sẽ được gộp với các thư mục trong thư mục này.

Nếu bạn muốn xóa một tập tin hoặc thư mục trong thư mục hệ thống gốc, bạn cần tạo một tập tin có cùng tên với tập tin/thư mục trong thư mục mô-đun bằng cách sử dụng mknod filename c 0 0. Bằng cách này, hệ thống lớp phủ sẽ tự động "whiteout" (Xóa trắng) tệp này như thể nó đã bị xóa (phân vùng /system không thực sự bị thay đổi).

Bạn cũng có thể khai báo một biến có tên REMOVE chứa danh sách các thư mục trong customize.sh để thực hiện các thao tác xóa và KernelSU sẽ tự động thực thi mknod <TARGET> c 0 0 trong các thư mục tương ứng của mô-đun. Ví dụ:

sh
REMOVE="
-/system/app/YouTube
-/system/app/Bloatware
-"

Danh sách trên sẽ thực thi mknod $MODPATH/system/app/YouTuBe c 0 0mknod $MODPATH/system/app/Bloatware c 0 0; và /system/app/YouTube/system/app/Bloatware sẽ bị xóa sau khi mô-đun này có hiệu lực.

Nếu bạn muốn thay thế một thư mục trong hệ thống, bạn cần tạo một thư mục có cùng đường dẫn trong thư mục mô-đun của mình, sau đó đặt thuộc tính setfattr -ntrust.overlay.opaque -v y <TARGET> cho thư mục này. Bằng cách này, hệ thống Overlayfs sẽ tự động thay thế thư mục tương ứng trong hệ thống (mà không thay đổi phân vùng /system).

Bạn có thể khai báo một biến có tên REPLACE trong tệp customize.sh của mình, bao gồm danh sách các thư mục sẽ được thay thế và KernelSU sẽ tự động thực hiện các thao tác tương ứng trong thư mục mô-đun của bạn. Ví dụ:

REPLACE=" /system/app/YouTube /system/app/Bloatware "

Danh sách này sẽ tự động tạo các thư mục $MODPATH/system/app/YouTube$MODPATH/system/app/Bloatware, sau đó thực thi setfattr -ntrusted.overlay.opaque -v y $MODPATH/system/app/ YouTubesetfattr -n Trust.overlay.opaque -v y $MODPATH/system/app/Bloatware. Sau khi mô-đun có hiệu lực, /system/app/YouTube/system/app/Bloatware sẽ được thay thế bằng các thư mục trống.

sự khác biệt với Magisk

Cơ chế không hệ thống của KernelSU được triển khai thông qua các overlayfs của kernel, trong khi Magisk hiện sử dụng magic mount (bind mount). Hai phương pháp triển khai có những khác biệt đáng kể, nhưng mục tiêu cuối cùng đều giống nhau: sửa đổi các tệp /system mà không sửa đổi vật lý phân vùng /system.

Nếu bạn quan tâm đến overlayfs, bạn nên đọc documentation on overlayfs của Kernel Linux.

system.prop

Tệp này có cùng định dạng với build.prop. Mỗi dòng bao gồm [key]=[value].

sepolicy.rule

Nếu mô-đun của bạn yêu cầu một số bản vá lỗi chính sách bổ sung, vui lòng thêm các quy tắc đó vào tệp này. Mỗi dòng trong tệp này sẽ được coi là một tuyên bố chính sách.

Trình cài đặt mô-đun

Trình cài đặt mô-đun KernelSU là mô-đun KernelSU được đóng gói trong tệp zip có thể được flash trong APP KernelSU manager. Trình cài đặt mô-đun KernelSU đơn giản chỉ là mô-đun KernelSU được đóng gói dưới dạng tệp zip.

txt
module.zip
-
-├── customize.sh                       <--- (Tùy chọn, biết thêm chi tiết sau)
-│                                           Tập lệnh này sẽ có nguồn gốc từ update-binary
-├── ...
-├── ...  /* Các tập tin còn lại của mô-đun */
-

WARNING

Mô-đun KernelSU KHÔNG được hỗ trợ để cài đặt trong khôi phục tùy chỉnh!!

Tùy chỉnh

Nếu bạn cần tùy chỉnh quá trình cài đặt mô-đun, bạn có thể tùy ý tạo một tập lệnh trong trình cài đặt có tên customize.sh. Tập lệnh này sẽ được sourced (không được thực thi!) bởi tập lệnh cài đặt mô-đun sau khi tất cả các tệp được trích xuất và các quyền mặc định cũng như văn bản thứ hai được áp dụng. Điều này rất hữu ích nếu mô-đun của bạn yêu cầu thiết lập bổ sung dựa trên ABI của thiết bị hoặc bạn cần đặt các quyền/văn bản thứ hai đặc biệt cho một số tệp mô-đun của mình.

Nếu bạn muốn kiểm soát và tùy chỉnh hoàn toàn quá trình cài đặt, hãy khai báo SKIPUNZIP=1 trong customize.sh để bỏ qua tất cả các bước cài đặt mặc định. Bằng cách đó, customize.sh của bạn sẽ chịu trách nhiệm cài đặt mọi thứ.

Tập lệnh customize.sh chạy trong shell ash BusyBox của KernelSU với "Chế độ độc lập" được bật. Có sẵn các biến và hàm sau:

Biến

  • KSU (bool): biến để đánh dấu script đang chạy trong môi trường KernelSU, và giá trị của biến này sẽ luôn đúng. Bạn có thể sử dụng nó để phân biệt giữa KernelSU và Magisk.
  • KSU_VER (chuỗi): chuỗi phiên bản của KernelSU được cài đặt hiện tại (ví dụ: v0.4.0)
  • KSU_VER_CODE (int): mã phiên bản của KernelSU được cài đặt hiện tại trong không gian người dùng (ví dụ: 10672)
  • KSU_KERNEL_VER_CODE (int): mã phiên bản của KernelSU được cài đặt hiện tại trong không gian kernel (ví dụ: 10672)
  • BOOTMODE (bool): luôn là true trong KernelSU
  • MODPATH (đường dẫn): đường dẫn nơi các tập tin mô-đun của bạn sẽ được cài đặt
  • TMPDIR (đường dẫn): nơi bạn có thể lưu trữ tạm thời các tập tin
  • ZIPFILE (đường dẫn): zip cài đặt mô-đun của bạn
  • ARCH (chuỗi): kiến trúc CPU của thiết bị. Giá trị là arm, arm64, x86 hoặc x64
  • IS64BIT (bool): true nếu $ARCHarm64 hoặc x64
  • API (int): cấp độ API (phiên bản Android) của thiết bị (ví dụ: 23 cho Android 6.0)

WARNING

Trong KernelSU, MAGISK_VER_CODE luôn là 25200 và MAGISK_VER luôn là v25.2. Vui lòng không sử dụng hai biến này để xác định xem nó có chạy trên KernelSU hay không.

Hàm

txt
ui_print <msg>
-    in <msg> ra console
-    Tránh sử dụng 'echo' vì nó sẽ không hiển thị trong console của recovery tùy chỉnh
-
-abort <msg>
-    in thông báo lỗi <msg> ra bàn điều khiển và chấm dứt cài đặt
-    Tránh sử dụng 'exit' vì nó sẽ bỏ qua các bước dọn dẹp chấm dứt
-
-set_perm <target> <owner> <group> <permission> [context]
-    nếu [context] không được đặt, mặc định là "u:object_r:system_file:s0"
-    chức năng này là một shorthand cho các lệnh sau:
-       chown owner.group target
-       chmod permission target
-       chcon context target
-
-set_perm_recursive <directory> <owner> <group> <dirpermission> <filepermission> [context]
-    nếu [context] không được đặt, mặc định là "u:object_r:system_file:s0"
-    đối với tất cả các tệp trong <directory>, nó sẽ gọi:
-       bối cảnh cấp phép tệp của nhóm chủ sở hữu tệp set_perm
-    đối với tất cả các thư mục trong <directory> (bao gồm cả chính nó), nó sẽ gọi:
-       set_perm bối cảnh phân quyền của nhóm chủ sở hữu thư mục

Tập lệnh khởi động

Trong KernelSU, tập lệnh được chia thành hai loại dựa trên chế độ chạy của chúng: chế độ post-fs-data và chế độ dịch vụ late_start:

  • chế độ post-fs-data
    • Giai đoạn này là BLOCKING. Quá trình khởi động bị tạm dừng trước khi thực thi xong hoặc đã trôi qua 10 giây.
    • Các tập lệnh chạy trước khi bất kỳ mô-đun nào được gắn kết. Điều này cho phép nhà phát triển mô-đun tự động điều chỉnh các mô-đun của họ trước khi nó được gắn kết.
    • Giai đoạn này xảy ra trước khi Zygote được khởi động, điều này gần như có ý nghĩa đối với mọi thứ trong Android
    • CẢNH BÁO: sử dụng setprop sẽ làm quá trình khởi động bị nghẽn! Thay vào đó, vui lòng sử dụng resetprop -n <prop_name> <prop_value>.
    • Chỉ chạy tập lệnh ở chế độ này nếu cần thiết.
  • chế độ dịch vụ late_start
    • Giai đoạn này là NON-BLOCKING. Tập lệnh của bạn chạy song song với phần còn lại của quá trình khởi động.
    • Đây là giai đoạn được khuyến nghị để chạy hầu hết các tập lệnh.

Trong KernelSU, tập lệnh khởi động được chia thành hai loại dựa trên vị trí lưu trữ của chúng: tập lệnh chung và tập lệnh mô-đun:

  • Kịch Bản Chung
    • Được đặt trong /data/adb/post-fs-data.d, /data/adb/service.d, /data/adb/post-mount.d hoặc /data/adb/boot- đã hoàn thành.d
    • Chỉ được thực thi nếu tập lệnh được đặt là có thể thực thi được (chmod +x script.sh)
    • Các tập lệnh trong post-fs-data.d chạy ở chế độ post-fs-data và các tập lệnh trong service.d chạy ở chế độ dịch vụ late_start.
    • Các mô-đun KHÔNG thêm các tập lệnh chung trong quá trình cài đặt
  • Tập Lệnh Mô-đun
    • Được đặt trong thư mục riêng của mô-đun
    • Chỉ thực hiện nếu mô-đun được kích hoạt
    • post-fs-data.sh chạy ở chế độ post-fs-data, service.sh chạy ở chế độ dịch vụ late_start, boot-completed.sh chạy khi khởi động xong, post-mount.sh chạy trên overlayfs được gắn kết.

Tất cả các tập lệnh khởi động sẽ chạy trong shell ash BusyBox của KernelSU với "Standalone Mode" được bật.

Phát hành dưới giấy phép GPL3.

- +
Skip to content

Hướng dẫn mô-đun

KernelSU cung cấp một cơ chế mô-đun giúp đạt được hiệu quả sửa đổi thư mục hệ thống trong khi vẫn duy trì tính toàn vẹn của phân vùng system. Cơ chế này thường được gọi là "systemless".

Cơ chế mô-đun của KernelSU gần giống với Magisk. Nếu bạn đã quen với việc phát triển mô-đun Magisk thì việc phát triển các mô-đun KernelSU cũng rất tương tự. Bạn có thể bỏ qua phần giới thiệu các mô-đun bên dưới và chỉ cần đọc difference-with-magisk.

Busybox

KernelSU cung cấp tính năng nhị phân BusyBox hoàn chỉnh (bao gồm hỗ trợ SELinux đầy đủ). Tệp thực thi được đặt tại /data/adb/ksu/bin/busybox. BusyBox của KernelSU hỗ trợ "ASH Standalone Shell Mode" có thể chuyển đổi thời gian chạy. Standalone mode này có nghĩa là khi chạy trong shell ash của BusyBox, mọi lệnh sẽ trực tiếp sử dụng applet trong BusyBox, bất kể cái gì được đặt là PATH. Ví dụ: các lệnh như ls, rm, chmod sẽ KHÔNG sử dụng những gì có trong PATH (trong trường hợp Android theo mặc định, nó sẽ là /system/bin/ls, /system/bin/rm/system/bin/chmod tương ứng), nhưng thay vào đó sẽ gọi trực tiếp các ứng dụng BusyBox nội bộ. Điều này đảm bảo rằng các tập lệnh luôn chạy trong môi trường có thể dự đoán được và luôn có bộ lệnh đầy đủ cho dù nó đang chạy trên phiên bản Android nào. Để buộc lệnh not sử dụng BusyBox, bạn phải gọi tệp thực thi có đường dẫn đầy đủ.

Mỗi tập lệnh shell đơn lẻ chạy trong ngữ cảnh của KernelSU sẽ được thực thi trong shell ash của BusyBox với standalone mode được bật. Đối với những gì liên quan đến nhà phát triển bên thứ 3, điều này bao gồm tất cả các tập lệnh khởi động và tập lệnh cài đặt mô-đun.

Đối với những người muốn sử dụng tính năng "Standalone mode" này bên ngoài KernelSU, có 2 cách để kích hoạt tính năng này:

  1. Đặt biến môi trường ASH_STANDALONE thành 1
    Ví dụ: ASH_STANDALONE=1 /data/adb/ksu/bin/busybox sh <script>
  2. Chuyển đổi bằng các tùy chọn dòng lệnh:
    /data/adb/ksu/bin/busybox sh -o độc lập <script>

Để đảm bảo tất cả shell sh tiếp theo được thực thi cũng chạy ở standalone mode, tùy chọn 1 là phương thức ưu tiên (và đây là những gì KernelSU và KernelSU manager sử dụng nội bộ) vì các biến môi trường được kế thừa xuống các tiến trình con.

sự khác biệt với Magisk

BusyBox của KernelSU hiện đang sử dụng tệp nhị phân được biên dịch trực tiếp từ dự án Magisk. Cảm ơn Magisk! Vì vậy, bạn không phải lo lắng về vấn đề tương thích giữa các tập lệnh BusyBox trong Magisk và KernelSU vì chúng hoàn toàn giống nhau!

Mô-đun hạt nhânSU

Mô-đun KernelSU là một thư mục được đặt trong /data/adb/modules với cấu trúc bên dưới:

txt
/data/adb/modules
+├── .
+├── .
+|
+├── $MODID                  <--- Thư mục được đặt tên bằng ID của mô-đun
+│   │
+│   │      *** Nhận Dạng Mô-đun ***
+│   │
+│   ├── module.prop         <--- Tệp này lưu trữ metadata của mô-đun
+│   │
+│   │      *** Nội Dung Chính ***
+│   │
+│   ├── system              <--- Thư mục này sẽ được gắn kết nếu skip_mount không tồn tại
+│   │   ├── ...
+│   │   ├── ...
+│   │   └── ...
+│   │
+│   │      *** Cờ Trạng Thái ***
+│   │
+│   ├── skip_mount          <--- Nếu tồn tại, KernelSU sẽ KHÔNG gắn kết thư mục hệ thống của bạn
+│   ├── disable             <--- Nếu tồn tại, mô-đun sẽ bị vô hiệu hóa
+│   ├── remove              <--- Nếu tồn tại, mô-đun sẽ bị xóa trong lần khởi động lại tiếp theo
+│   │
+│   │      *** Tệp Tùy Chọn ***
+│   │
+│   ├── post-fs-data.sh     <--- Tập lệnh này sẽ được thực thi trong post-fs-data
+│   ├── post-mount.sh       <--- Tập lệnh này sẽ được thực thi trong post-mount
+│   ├── service.sh          <--- Tập lệnh này sẽ được thực thi trong dịch vụ late_start
+│   ├── boot-completed.sh   <--- Tập lệnh này sẽ được thực thi khi khởi động xong
+|   ├── uninstall.sh        <--- Tập lệnh này sẽ được thực thi khi KernelSU xóa mô-đun của bạn
+│   ├── system.prop         <--- Các thuộc tính trong tệp này sẽ được tải dưới dạng thuộc tính hệ thống bằng resetprop
+│   ├── sepolicy.rule       <--- Quy tắc riêng biệt tùy chỉnh bổ sung
+│   │
+│   │      *** Được Tạo Tự Động, KHÔNG TẠO HOẶC SỬA ĐỔI THỦ CÔNG ***
+│   │
+│   ├── vendor              <--- Một liên kết tượng trưng đến $MODID/system/vendor
+│   ├── product             <--- Một liên kết tượng trưng đến $MODID/system/product
+│   ├── system_ext          <--- Một liên kết tượng trưng đến $MODID/system/system_ext
+│   │
+│   │      *** Mọi tập tin / thư mục bổ sung đều được phép ***
+│   │
+│   ├── ...
+│   └── ...
+|
+├── another_module
+│   ├── .
+│   └── .
+├── .
+├── .
/data/adb/modules
+├── .
+├── .
+|
+├── $MODID                  <--- Thư mục được đặt tên bằng ID của mô-đun
+│   │
+│   │      *** Nhận Dạng Mô-đun ***
+│   │
+│   ├── module.prop         <--- Tệp này lưu trữ metadata của mô-đun
+│   │
+│   │      *** Nội Dung Chính ***
+│   │
+│   ├── system              <--- Thư mục này sẽ được gắn kết nếu skip_mount không tồn tại
+│   │   ├── ...
+│   │   ├── ...
+│   │   └── ...
+│   │
+│   │      *** Cờ Trạng Thái ***
+│   │
+│   ├── skip_mount          <--- Nếu tồn tại, KernelSU sẽ KHÔNG gắn kết thư mục hệ thống của bạn
+│   ├── disable             <--- Nếu tồn tại, mô-đun sẽ bị vô hiệu hóa
+│   ├── remove              <--- Nếu tồn tại, mô-đun sẽ bị xóa trong lần khởi động lại tiếp theo
+│   │
+│   │      *** Tệp Tùy Chọn ***
+│   │
+│   ├── post-fs-data.sh     <--- Tập lệnh này sẽ được thực thi trong post-fs-data
+│   ├── post-mount.sh       <--- Tập lệnh này sẽ được thực thi trong post-mount
+│   ├── service.sh          <--- Tập lệnh này sẽ được thực thi trong dịch vụ late_start
+│   ├── boot-completed.sh   <--- Tập lệnh này sẽ được thực thi khi khởi động xong
+|   ├── uninstall.sh        <--- Tập lệnh này sẽ được thực thi khi KernelSU xóa mô-đun của bạn
+│   ├── system.prop         <--- Các thuộc tính trong tệp này sẽ được tải dưới dạng thuộc tính hệ thống bằng resetprop
+│   ├── sepolicy.rule       <--- Quy tắc riêng biệt tùy chỉnh bổ sung
+│   │
+│   │      *** Được Tạo Tự Động, KHÔNG TẠO HOẶC SỬA ĐỔI THỦ CÔNG ***
+│   │
+│   ├── vendor              <--- Một liên kết tượng trưng đến $MODID/system/vendor
+│   ├── product             <--- Một liên kết tượng trưng đến $MODID/system/product
+│   ├── system_ext          <--- Một liên kết tượng trưng đến $MODID/system/system_ext
+│   │
+│   │      *** Mọi tập tin / thư mục bổ sung đều được phép ***
+│   │
+│   ├── ...
+│   └── ...
+|
+├── another_module
+│   ├── .
+│   └── .
+├── .
+├── .

sự khác biệt với Magisk

KernelSU không có hỗ trợ tích hợp cho Zygisk nên không có nội dung liên quan đến Zygisk trong mô-đun. Tuy nhiên, bạn có thể sử dụng ZygiskNext để hỗ trợ các mô-đun Zygisk. Trong trường hợp này, nội dung của mô-đun Zygisk giống hệt với nội dung được Magisk hỗ trợ.

module.prop

module.prop là tệp cấu hình cho mô-đun. Trong KernelSU, nếu một mô-đun không chứa tệp này, nó sẽ không được nhận dạng là mô-đun. Định dạng của tập tin này như sau:

txt
id=<string>
+name=<string>
+version=<string>
+versionCode=<int>
+author=<string>
+description=<string>
id=<string>
+name=<string>
+version=<string>
+versionCode=<int>
+author=<string>
+description=<string>
  • id phải khớp với biểu thức chính quy này: ^[a-zA-Z][a-zA-Z0-9._-]+$
    ví dụ: ✓ a_module, ✓ a.module, ✓ module-101, ✗ a module, ✗ 1_module, ✗ -a-module
    Đây là mã định danh duy nhất của mô-đun của bạn. Bạn không nên thay đổi nó sau khi được xuất bản.
  • versionCode phải là số nguyên. Điều này được sử dụng để so sánh các phiên bản
  • Các chuỗi khác không được đề cập ở trên có thể là chuỗi một dòng bất kỳ.
  • Đảm bảo sử dụng kiểu ngắt dòng UNIX (LF) chứ không phải Windows (CR+LF) hoặc Macintosh (CR).

Tập lệnh Shell

Vui lòng đọc phần Boot Scripts để hiểu sự khác biệt giữa post-fs-data.shservice.sh. Đối với hầu hết các nhà phát triển mô-đun, service.sh sẽ đủ tốt nếu bạn chỉ cần chạy tập lệnh khởi động, nếu bạn cần chạy tập lệnh sau khi khởi động xong, vui lòng sử dụng boot-completed.sh. Nếu bạn muốn làm gì đó sau khi gắn các lớp phủ, vui lòng sử dụng post-mount.sh.

Trong tất cả các tập lệnh của mô-đun của bạn, vui lòng sử dụng MODDIR=${0%/*} để lấy đường dẫn thư mục cơ sở của mô-đun của bạn; KHÔNG mã hóa cứng đường dẫn mô-đun của bạn trong tập lệnh.

sự khác biệt với Magisk

Bạn có thể sử dụng biến môi trường KSU để xác định xem tập lệnh đang chạy trong KernelSU hay Magisk. Nếu chạy trong KernelSU, giá trị này sẽ được đặt thành true.

thư mục system

Nội dung của thư mục này sẽ được phủ lên trên phân vùng /system của hệ thống bằng cách sử dụng overlayfs sau khi hệ thống được khởi động. Điều này có nghĩa rằng:

  1. Các file có cùng tên với các file trong thư mục tương ứng trong hệ thống sẽ bị ghi đè bởi các file trong thư mục này.
  2. Các thư mục có cùng tên với thư mục tương ứng trong hệ thống sẽ được gộp với các thư mục trong thư mục này.

Nếu bạn muốn xóa một tập tin hoặc thư mục trong thư mục hệ thống gốc, bạn cần tạo một tập tin có cùng tên với tập tin/thư mục trong thư mục mô-đun bằng cách sử dụng mknod filename c 0 0. Bằng cách này, hệ thống lớp phủ sẽ tự động "whiteout" (Xóa trắng) tệp này như thể nó đã bị xóa (phân vùng /system không thực sự bị thay đổi).

Bạn cũng có thể khai báo một biến có tên REMOVE chứa danh sách các thư mục trong customize.sh để thực hiện các thao tác xóa và KernelSU sẽ tự động thực thi mknod <TARGET> c 0 0 trong các thư mục tương ứng của mô-đun. Ví dụ:

sh
REMOVE="
+/system/app/YouTube
+/system/app/Bloatware
+"
REMOVE="
+/system/app/YouTube
+/system/app/Bloatware
+"

Danh sách trên sẽ thực thi mknod $MODPATH/system/app/YouTuBe c 0 0mknod $MODPATH/system/app/Bloatware c 0 0; và /system/app/YouTube/system/app/Bloatware sẽ bị xóa sau khi mô-đun này có hiệu lực.

Nếu bạn muốn thay thế một thư mục trong hệ thống, bạn cần tạo một thư mục có cùng đường dẫn trong thư mục mô-đun của mình, sau đó đặt thuộc tính setfattr -ntrust.overlay.opaque -v y <TARGET> cho thư mục này. Bằng cách này, hệ thống Overlayfs sẽ tự động thay thế thư mục tương ứng trong hệ thống (mà không thay đổi phân vùng /system).

Bạn có thể khai báo một biến có tên REPLACE trong tệp customize.sh của mình, bao gồm danh sách các thư mục sẽ được thay thế và KernelSU sẽ tự động thực hiện các thao tác tương ứng trong thư mục mô-đun của bạn. Ví dụ:

REPLACE=" /system/app/YouTube /system/app/Bloatware "

Danh sách này sẽ tự động tạo các thư mục $MODPATH/system/app/YouTube$MODPATH/system/app/Bloatware, sau đó thực thi setfattr -ntrusted.overlay.opaque -v y $MODPATH/system/app/ YouTubesetfattr -n Trust.overlay.opaque -v y $MODPATH/system/app/Bloatware. Sau khi mô-đun có hiệu lực, /system/app/YouTube/system/app/Bloatware sẽ được thay thế bằng các thư mục trống.

sự khác biệt với Magisk

Cơ chế không hệ thống của KernelSU được triển khai thông qua các overlayfs của kernel, trong khi Magisk hiện sử dụng magic mount (bind mount). Hai phương pháp triển khai có những khác biệt đáng kể, nhưng mục tiêu cuối cùng đều giống nhau: sửa đổi các tệp /system mà không sửa đổi vật lý phân vùng /system.

Nếu bạn quan tâm đến overlayfs, bạn nên đọc documentation on overlayfs của Kernel Linux.

system.prop

Tệp này có cùng định dạng với build.prop. Mỗi dòng bao gồm [key]=[value].

sepolicy.rule

Nếu mô-đun của bạn yêu cầu một số bản vá lỗi chính sách bổ sung, vui lòng thêm các quy tắc đó vào tệp này. Mỗi dòng trong tệp này sẽ được coi là một tuyên bố chính sách.

Trình cài đặt mô-đun

Trình cài đặt mô-đun KernelSU là mô-đun KernelSU được đóng gói trong tệp zip có thể được flash trong APP KernelSU manager. Trình cài đặt mô-đun KernelSU đơn giản chỉ là mô-đun KernelSU được đóng gói dưới dạng tệp zip.

txt
module.zip
+
+├── customize.sh                       <--- (Tùy chọn, biết thêm chi tiết sau)
+│                                           Tập lệnh này sẽ có nguồn gốc từ update-binary
+├── ...
+├── ...  /* Các tập tin còn lại của mô-đun */
+
module.zip
+
+├── customize.sh                       <--- (Tùy chọn, biết thêm chi tiết sau)
+│                                           Tập lệnh này sẽ có nguồn gốc từ update-binary
+├── ...
+├── ...  /* Các tập tin còn lại của mô-đun */
+

WARNING

Mô-đun KernelSU KHÔNG được hỗ trợ để cài đặt trong khôi phục tùy chỉnh!!

Tùy chỉnh

Nếu bạn cần tùy chỉnh quá trình cài đặt mô-đun, bạn có thể tùy ý tạo một tập lệnh trong trình cài đặt có tên customize.sh. Tập lệnh này sẽ được sourced (không được thực thi!) bởi tập lệnh cài đặt mô-đun sau khi tất cả các tệp được trích xuất và các quyền mặc định cũng như văn bản thứ hai được áp dụng. Điều này rất hữu ích nếu mô-đun của bạn yêu cầu thiết lập bổ sung dựa trên ABI của thiết bị hoặc bạn cần đặt các quyền/văn bản thứ hai đặc biệt cho một số tệp mô-đun của mình.

Nếu bạn muốn kiểm soát và tùy chỉnh hoàn toàn quá trình cài đặt, hãy khai báo SKIPUNZIP=1 trong customize.sh để bỏ qua tất cả các bước cài đặt mặc định. Bằng cách đó, customize.sh của bạn sẽ chịu trách nhiệm cài đặt mọi thứ.

Tập lệnh customize.sh chạy trong shell ash BusyBox của KernelSU với "Chế độ độc lập" được bật. Có sẵn các biến và hàm sau:

Biến

  • KSU (bool): biến để đánh dấu script đang chạy trong môi trường KernelSU, và giá trị của biến này sẽ luôn đúng. Bạn có thể sử dụng nó để phân biệt giữa KernelSU và Magisk.
  • KSU_VER (chuỗi): chuỗi phiên bản của KernelSU được cài đặt hiện tại (ví dụ: v0.4.0)
  • KSU_VER_CODE (int): mã phiên bản của KernelSU được cài đặt hiện tại trong không gian người dùng (ví dụ: 10672)
  • KSU_KERNEL_VER_CODE (int): mã phiên bản của KernelSU được cài đặt hiện tại trong không gian kernel (ví dụ: 10672)
  • BOOTMODE (bool): luôn là true trong KernelSU
  • MODPATH (đường dẫn): đường dẫn nơi các tập tin mô-đun của bạn sẽ được cài đặt
  • TMPDIR (đường dẫn): nơi bạn có thể lưu trữ tạm thời các tập tin
  • ZIPFILE (đường dẫn): zip cài đặt mô-đun của bạn
  • ARCH (chuỗi): kiến trúc CPU của thiết bị. Giá trị là arm, arm64, x86 hoặc x64
  • IS64BIT (bool): true nếu $ARCHarm64 hoặc x64
  • API (int): cấp độ API (phiên bản Android) của thiết bị (ví dụ: 23 cho Android 6.0)

WARNING

Trong KernelSU, MAGISK_VER_CODE luôn là 25200 và MAGISK_VER luôn là v25.2. Vui lòng không sử dụng hai biến này để xác định xem nó có chạy trên KernelSU hay không.

Hàm

txt
ui_print <msg>
+    in <msg> ra console
+    Tránh sử dụng 'echo' vì nó sẽ không hiển thị trong console của recovery tùy chỉnh
+
+abort <msg>
+    in thông báo lỗi <msg> ra bàn điều khiển và chấm dứt cài đặt
+    Tránh sử dụng 'exit' vì nó sẽ bỏ qua các bước dọn dẹp chấm dứt
+
+set_perm <target> <owner> <group> <permission> [context]
+    nếu [context] không được đặt, mặc định là "u:object_r:system_file:s0"
+    chức năng này là một shorthand cho các lệnh sau:
+       chown owner.group target
+       chmod permission target
+       chcon context target
+
+set_perm_recursive <directory> <owner> <group> <dirpermission> <filepermission> [context]
+    nếu [context] không được đặt, mặc định là "u:object_r:system_file:s0"
+    đối với tất cả các tệp trong <directory>, nó sẽ gọi:
+       bối cảnh cấp phép tệp của nhóm chủ sở hữu tệp set_perm
+    đối với tất cả các thư mục trong <directory> (bao gồm cả chính nó), nó sẽ gọi:
+       set_perm bối cảnh phân quyền của nhóm chủ sở hữu thư mục
ui_print <msg>
+    in <msg> ra console
+    Tránh sử dụng 'echo' vì nó sẽ không hiển thị trong console của recovery tùy chỉnh
+
+abort <msg>
+    in thông báo lỗi <msg> ra bàn điều khiển và chấm dứt cài đặt
+    Tránh sử dụng 'exit' vì nó sẽ bỏ qua các bước dọn dẹp chấm dứt
+
+set_perm <target> <owner> <group> <permission> [context]
+    nếu [context] không được đặt, mặc định là "u:object_r:system_file:s0"
+    chức năng này là một shorthand cho các lệnh sau:
+       chown owner.group target
+       chmod permission target
+       chcon context target
+
+set_perm_recursive <directory> <owner> <group> <dirpermission> <filepermission> [context]
+    nếu [context] không được đặt, mặc định là "u:object_r:system_file:s0"
+    đối với tất cả các tệp trong <directory>, nó sẽ gọi:
+       bối cảnh cấp phép tệp của nhóm chủ sở hữu tệp set_perm
+    đối với tất cả các thư mục trong <directory> (bao gồm cả chính nó), nó sẽ gọi:
+       set_perm bối cảnh phân quyền của nhóm chủ sở hữu thư mục

Tập lệnh khởi động

Trong KernelSU, tập lệnh được chia thành hai loại dựa trên chế độ chạy của chúng: chế độ post-fs-data và chế độ dịch vụ late_start:

  • chế độ post-fs-data
    • Giai đoạn này là BLOCKING. Quá trình khởi động bị tạm dừng trước khi thực thi xong hoặc đã trôi qua 10 giây.
    • Các tập lệnh chạy trước khi bất kỳ mô-đun nào được gắn kết. Điều này cho phép nhà phát triển mô-đun tự động điều chỉnh các mô-đun của họ trước khi nó được gắn kết.
    • Giai đoạn này xảy ra trước khi Zygote được khởi động, điều này gần như có ý nghĩa đối với mọi thứ trong Android
    • CẢNH BÁO: sử dụng setprop sẽ làm quá trình khởi động bị nghẽn! Thay vào đó, vui lòng sử dụng resetprop -n <prop_name> <prop_value>.
    • Chỉ chạy tập lệnh ở chế độ này nếu cần thiết.
  • chế độ dịch vụ late_start
    • Giai đoạn này là NON-BLOCKING. Tập lệnh của bạn chạy song song với phần còn lại của quá trình khởi động.
    • Đây là giai đoạn được khuyến nghị để chạy hầu hết các tập lệnh.

Trong KernelSU, tập lệnh khởi động được chia thành hai loại dựa trên vị trí lưu trữ của chúng: tập lệnh chung và tập lệnh mô-đun:

  • Kịch Bản Chung
    • Được đặt trong /data/adb/post-fs-data.d, /data/adb/service.d, /data/adb/post-mount.d hoặc /data/adb/boot- đã hoàn thành.d
    • Chỉ được thực thi nếu tập lệnh được đặt là có thể thực thi được (chmod +x script.sh)
    • Các tập lệnh trong post-fs-data.d chạy ở chế độ post-fs-data và các tập lệnh trong service.d chạy ở chế độ dịch vụ late_start.
    • Các mô-đun KHÔNG thêm các tập lệnh chung trong quá trình cài đặt
  • Tập Lệnh Mô-đun
    • Được đặt trong thư mục riêng của mô-đun
    • Chỉ thực hiện nếu mô-đun được kích hoạt
    • post-fs-data.sh chạy ở chế độ post-fs-data, service.sh chạy ở chế độ dịch vụ late_start, boot-completed.sh chạy khi khởi động xong, post-mount.sh chạy trên overlayfs được gắn kết.

Tất cả các tập lệnh khởi động sẽ chạy trong shell ash BusyBox của KernelSU với "Standalone Mode" được bật.

Phát hành dưới giấy phép GPL3.

+ \ No newline at end of file diff --git a/vi_VN/guide/rescue-from-bootloop.html b/vi_VN/guide/rescue-from-bootloop.html index 7701ab39ed3f..f8d04908fc43 100644 --- a/vi_VN/guide/rescue-from-bootloop.html +++ b/vi_VN/guide/rescue-from-bootloop.html @@ -5,18 +5,19 @@ Cứu khỏi bootloop (Vòng lặp khởi động) | KernelSU - - + + + - - - - + + + + + -
Skip to content
On this page

Cứu khỏi bootloop (Vòng lặp khởi động)

Khi flash một thiết bị, chúng ta có thể gặp phải tình trạng máy "bị brick". Về lý thuyết, nếu bạn chỉ sử dụng fastboot để flash phân vùng boot hoặc cài đặt các mô-đun không phù hợp khiến máy không khởi động được thì điều này có thể được khắc phục bằng các thao tác thích hợp. Tài liệu này nhằm mục đích cung cấp một số phương pháp khẩn cấp để giúp bạn khôi phục từ thiết bị "bị brick".

Brick bởi flash vào phân vùng boot

Trong KernelSU, các tình huống sau có thể gây ra lỗi khởi động khi flash phân vùng khởi động:

  1. Bạn flash image boot sai định dạng. Ví dụ: nếu định dạng khởi động điện thoại của bạn là gz, nhưng bạn flash image định dạng lz4 thì điện thoại sẽ không thể khởi động.
  2. Điện thoại của bạn cần tắt xác minh AVB để khởi động bình thường (thường yêu cầu xóa tất cả dữ liệu trên điện thoại).
  3. Kernel của bạn có một số lỗi hoặc không phù hợp để flash điện thoại của bạn.

Bất kể tình huống thế nào, bạn có thể khôi phục bằng cách flash boot image gốc. Do đó, khi bắt đầu hướng dẫn cài đặt, chúng tôi thực sự khuyên bạn nên sao lưu boot image gốc trước khi flash. Nếu chưa sao lưu, bạn có thể lấy boot image gốc từ người dùng khác có cùng thiết bị với bạn hoặc từ chương trình cơ sở chính thức (official firmware).

Brick bởi mô-đun

Việc cài đặt mô-đun có thể là nguyên nhân phổ biến hơn khiến thiết bị của bạn bị brick, nhưng chúng tôi phải nghiêm túc cảnh báo bạn: Không cài đặt mô-đun từ các nguồn không xác định! Vì các mô-đun có đặc quyền root nên chúng có thể gây ra thiệt hại không thể khắc phục cho thiết bị của bạn!

Mô-đun bình thường

Nếu bạn đã flash một mô-đun đã được chứng minh là an toàn nhưng khiến thiết bị của bạn không khởi động được thì tình huống này có thể dễ dàng phục hồi trong KernelSU mà không phải lo lắng gì. KernelSU có các cơ chế tích hợp sẵn để giải cứu thiết bị của bạn, bao gồm:

  1. Cập nhật AB
  2. Cứu bằng cách nhấn Giảm âm lượng

Cập nhật AB

Các bản cập nhật mô-đun của KernelSU lấy cảm hứng từ cơ chế cập nhật AB của hệ thống Android được sử dụng trong các bản cập nhật OTA. Nếu bạn cài đặt một mô-đun mới hoặc cập nhật mô-đun hiện có, nó sẽ không trực tiếp sửa đổi tệp mô-đun hiện đang sử dụng. Thay vào đó, tất cả các mô-đun sẽ được tích hợp vào một hình ảnh cập nhật khác. Sau khi hệ thống được khởi động lại, nó sẽ cố gắng bắt đầu sử dụng hình ảnh cập nhật này. Nếu hệ thống Android khởi động thành công, các mô-đun sẽ được cập nhật thực sự.

Vì vậy, phương pháp đơn giản và được sử dụng phổ biến nhất để cứu thiết bị của bạn là buộc khởi động lại. Nếu bạn không thể khởi động hệ thống của mình sau khi flash một mô-đun, bạn có thể nhấn và giữ nút nguồn trong hơn 10 giây và hệ thống sẽ tự động khởi động lại; sau khi khởi động lại, nó sẽ quay trở lại trạng thái trước khi cập nhật mô-đun và các mô-đun được cập nhật trước đó sẽ tự động bị tắt.

Cứu bằng cách nhấn Giảm âm lượng

Nếu bản cập nhật AB vẫn không giải quyết được vấn đề, bạn có thể thử sử dụng Chế độ an toàn. Ở Chế độ an toàn, tất cả các mô-đun đều bị tắt.

Có hai cách để vào Chế độ an toàn:

  1. Chế Độ An Toàn tích hợp (built-in Safe Mode) của một số hệ thống; một số hệ thống có Chế độ an toàn tích hợp có thể được truy cập bằng cách nhấn và giữ nút giảm âm lượng, trong khi những hệ thống khác (chẳng hạn như MIUI) có thể bật Chế Độ An Toàn trong Recovery. Khi vào Chế Độ An Toàn của hệ thống, KernelSU cũng sẽ vào Chế Độ An Toàn và tự động tắt các mô-đun.
  2. Chế Độ An Toàn tích hợp (built-in Safe Mode) của KernelSU; phương pháp thao tác là nhấn phím giảm âm lượng liên tục hơn ba lần sau màn hình khởi động đầu tiên. Lưu ý là nhấn-thả, nhấn-thả, nhấn-thả chứ không phải nhấn giữ.

Sau khi vào chế độ an toàn, tất cả các mô-đun trên trang mô-đun của KernelSU Manager đều bị tắt nhưng bạn có thể thực hiện thao tác "gỡ cài đặt" để gỡ cài đặt bất kỳ mô-đun nào có thể gây ra sự cố.

Chế độ an toàn tích hợp được triển khai trong kernel, do đó không có khả năng thiếu các sự kiện chính do bị chặn. Tuy nhiên, đối với các hạt nhân không phải GKI, có thể cần phải tích hợp mã thủ công và bạn có thể tham khảo tài liệu chính thức để được hướng dẫn.

Mô-đun độc hại

Nếu các phương pháp trên không thể cứu được thiết bị của bạn thì rất có thể mô-đun bạn cài đặt có hoạt động độc hại hoặc đã làm hỏng thiết bị của bạn thông qua các phương tiện khác. Trong trường hợp này, chỉ có hai gợi ý:

  1. Xóa sạch dữ liệu và flash hệ thống chính thức.
  2. Tham khảo dịch vụ hậu mãi.

Phát hành dưới giấy phép GPL3.

- +
Skip to content

Cứu khỏi bootloop (Vòng lặp khởi động)

Khi flash một thiết bị, chúng ta có thể gặp phải tình trạng máy "bị brick". Về lý thuyết, nếu bạn chỉ sử dụng fastboot để flash phân vùng boot hoặc cài đặt các mô-đun không phù hợp khiến máy không khởi động được thì điều này có thể được khắc phục bằng các thao tác thích hợp. Tài liệu này nhằm mục đích cung cấp một số phương pháp khẩn cấp để giúp bạn khôi phục từ thiết bị "bị brick".

Brick bởi flash vào phân vùng boot

Trong KernelSU, các tình huống sau có thể gây ra lỗi khởi động khi flash phân vùng khởi động:

  1. Bạn flash image boot sai định dạng. Ví dụ: nếu định dạng khởi động điện thoại của bạn là gz, nhưng bạn flash image định dạng lz4 thì điện thoại sẽ không thể khởi động.
  2. Điện thoại của bạn cần tắt xác minh AVB để khởi động bình thường (thường yêu cầu xóa tất cả dữ liệu trên điện thoại).
  3. Kernel của bạn có một số lỗi hoặc không phù hợp để flash điện thoại của bạn.

Bất kể tình huống thế nào, bạn có thể khôi phục bằng cách flash boot image gốc. Do đó, khi bắt đầu hướng dẫn cài đặt, chúng tôi thực sự khuyên bạn nên sao lưu boot image gốc trước khi flash. Nếu chưa sao lưu, bạn có thể lấy boot image gốc từ người dùng khác có cùng thiết bị với bạn hoặc từ chương trình cơ sở chính thức (official firmware).

Brick bởi mô-đun

Việc cài đặt mô-đun có thể là nguyên nhân phổ biến hơn khiến thiết bị của bạn bị brick, nhưng chúng tôi phải nghiêm túc cảnh báo bạn: Không cài đặt mô-đun từ các nguồn không xác định! Vì các mô-đun có đặc quyền root nên chúng có thể gây ra thiệt hại không thể khắc phục cho thiết bị của bạn!

Mô-đun bình thường

Nếu bạn đã flash một mô-đun đã được chứng minh là an toàn nhưng khiến thiết bị của bạn không khởi động được thì tình huống này có thể dễ dàng phục hồi trong KernelSU mà không phải lo lắng gì. KernelSU có các cơ chế tích hợp sẵn để giải cứu thiết bị của bạn, bao gồm:

  1. Cập nhật AB
  2. Cứu bằng cách nhấn Giảm âm lượng

Cập nhật AB

Các bản cập nhật mô-đun của KernelSU lấy cảm hứng từ cơ chế cập nhật AB của hệ thống Android được sử dụng trong các bản cập nhật OTA. Nếu bạn cài đặt một mô-đun mới hoặc cập nhật mô-đun hiện có, nó sẽ không trực tiếp sửa đổi tệp mô-đun hiện đang sử dụng. Thay vào đó, tất cả các mô-đun sẽ được tích hợp vào một hình ảnh cập nhật khác. Sau khi hệ thống được khởi động lại, nó sẽ cố gắng bắt đầu sử dụng hình ảnh cập nhật này. Nếu hệ thống Android khởi động thành công, các mô-đun sẽ được cập nhật thực sự.

Vì vậy, phương pháp đơn giản và được sử dụng phổ biến nhất để cứu thiết bị của bạn là buộc khởi động lại. Nếu bạn không thể khởi động hệ thống của mình sau khi flash một mô-đun, bạn có thể nhấn và giữ nút nguồn trong hơn 10 giây và hệ thống sẽ tự động khởi động lại; sau khi khởi động lại, nó sẽ quay trở lại trạng thái trước khi cập nhật mô-đun và các mô-đun được cập nhật trước đó sẽ tự động bị tắt.

Cứu bằng cách nhấn Giảm âm lượng

Nếu bản cập nhật AB vẫn không giải quyết được vấn đề, bạn có thể thử sử dụng Chế độ an toàn. Ở Chế độ an toàn, tất cả các mô-đun đều bị tắt.

Có hai cách để vào Chế độ an toàn:

  1. Chế Độ An Toàn tích hợp (built-in Safe Mode) của một số hệ thống; một số hệ thống có Chế độ an toàn tích hợp có thể được truy cập bằng cách nhấn và giữ nút giảm âm lượng, trong khi những hệ thống khác (chẳng hạn như MIUI) có thể bật Chế Độ An Toàn trong Recovery. Khi vào Chế Độ An Toàn của hệ thống, KernelSU cũng sẽ vào Chế Độ An Toàn và tự động tắt các mô-đun.
  2. Chế Độ An Toàn tích hợp (built-in Safe Mode) của KernelSU; phương pháp thao tác là nhấn phím giảm âm lượng liên tục hơn ba lần sau màn hình khởi động đầu tiên. Lưu ý là nhấn-thả, nhấn-thả, nhấn-thả chứ không phải nhấn giữ.

Sau khi vào chế độ an toàn, tất cả các mô-đun trên trang mô-đun của KernelSU Manager đều bị tắt nhưng bạn có thể thực hiện thao tác "gỡ cài đặt" để gỡ cài đặt bất kỳ mô-đun nào có thể gây ra sự cố.

Chế độ an toàn tích hợp được triển khai trong kernel, do đó không có khả năng thiếu các sự kiện chính do bị chặn. Tuy nhiên, đối với các hạt nhân không phải GKI, có thể cần phải tích hợp mã thủ công và bạn có thể tham khảo tài liệu chính thức để được hướng dẫn.

Mô-đun độc hại

Nếu các phương pháp trên không thể cứu được thiết bị của bạn thì rất có thể mô-đun bạn cài đặt có hoạt động độc hại hoặc đã làm hỏng thiết bị của bạn thông qua các phương tiện khác. Trong trường hợp này, chỉ có hai gợi ý:

  1. Xóa sạch dữ liệu và flash hệ thống chính thức.
  2. Tham khảo dịch vụ hậu mãi.

Phát hành dưới giấy phép GPL3.

+ \ No newline at end of file diff --git a/vi_VN/guide/unofficially-support-devices.html b/vi_VN/guide/unofficially-support-devices.html index dd405020e7f0..c6e8e8be0235 100644 --- a/vi_VN/guide/unofficially-support-devices.html +++ b/vi_VN/guide/unofficially-support-devices.html @@ -5,19 +5,20 @@ Thiết bị hỗ trợ không chính thức | KernelSU - - + + + - - - - - + + + + + + -
Skip to content
On this page

Thiết bị hỗ trợ không chính thức

WARNING

Đây là trang liệt kê kernel cho các thiết bị không dùng GKI được hỗ trợ bởi các lập trình viên khác.

WARNING

Trang này chỉ để cho bạn tìm thấy source cho thiết bị của bạn, nó HOÀN TOÀN KHÔNG được review bởi lập trình viên của KernelSU. Vậy nên hãy chấp nhận rủi ro khi sử dụng chúng.

Người bảo trìKho lưu trữThiết bị hỗ trợ
diphonsD8G_Kernel_oxygenPoco F3: alioth | Poco F4: munch | MI10T/PRO: Apollo
diphonsD8G_Kernel_SM8150Poco X3 Pro: Vayu | Bhima
diphonsD8G_Kernel_SDM845Poco F1 | MI8 | MiMix2S
diphonsD8G_Kernel_SM8350Vili
diphonsD8G_Kernel_MarblePoco F5 | Redmi Note 12 Turbo | Marble
th1nhhdkandroid_kernel_sony_sm8250-kernelsuSony Xperia 1 II | Sony Xperia 5 II
akash07knexus_kernel_xiaomi_sm8250Poco F4: munch
HMTheBoy154Darkmatter-kernelGeneric x86_64 devices running Android-x86
Asuka-mioandroid_kernel_xiaomi_casMi 10 Ultra: cas
xiaoleGunMiku_kernel_xiaomi_waynewayne
YamazakuraHkernel_xiaomi_cepheuscepheus for pixel experience
SakuraNotStupidandroid_kernel_xiaomi_sdm710Xiaomi MI 8 SE(sirius/xmsirius)
Aquarius223(paper)android_kernel_xiaomi_msm8998MI 6 (sagit) and MIX 2 (chiron) for LineageOS
SlackerStateandroid_kernel_xiaomi_sm6150Redmi K30 4G (phoenix)
RooGhz720kernel_xiaomi_sm6150REDMI NOTE 10 PRO (sweet)
OnlyTomInSecondandroid_kernel_xiaomi_sdm845Mi 8 (dipper) for LineageOS
Rohail33RealKing KernelApollo(Redmi K30S Ultra/Mi 10T/Mi 10T Pro),Alioth(Redmi K40/POCO F3/Mi 11X),Munch(Redmi K40S/POCO F4), both MIUI and AOSP.
lateautumn233android_kernel_oneplus_sm8250OnePlus 8 Serials
Sreeshankar KNeverSettle KernelOnePlus Nord (avicii)
Molyuuneko_kernel_xiaomi_gauguinRedmi Note 9 Pro/ Mi 10T Lite/ Mi 10i
guh0613android_kernel_oppo_sm8150OPPO Reno Ace (OP4A89)
LeviMarvinandroid_kernel_xiaomi_aliothRedmi K40 / POCO F3
cibimokernel_xiaomi_raphael_ksuXiaomi Redmi K20 Pro / Mi 9T Pro (raphael)
EndCreditskernel_xiaomi_sm7250Redmi K30 5G ( picasso ) , Redmi K30i 5G ( picasso_48m)
msnxandroid-msm-coral-4.14-android13Pixel 4 XL
SoDebugkernel_xiaomi_raphaelXiaomi Redmi K20 Pro / Mi 9T Pro (raphael)
H1mJTkernel_realme_RMX1901Realme X (RMX1901/RMX1901CN)
SonalSingh18android_kernel_xiaomi_sm6250Miatoll [curtana, excalibur, gram, joyeuse]
RooGhz720kernel_xiaomi_lavenderRedmi Note 7 (Lavender)
JunASAKAkernel_google_msm-4.9_KernelSUGoogle Pixel 3a & 3a XL (sargo & bonito)
RooGhz720kernel_asus_X01BDAsus Zenfone Max Pro M1/M2
Evans Mikekernel_xiaomi_raphael_bool-xXiaomi Redmi K20 Pro / Mi 9T Pro (raphael)
easterNdayKSU_Thyme_BuildBotXiaomi 10S
tedomi2705kernel_xiaomi_sdm660Xiaomi Redmi Note 6 Pro (tulip)
RyuujiXandroid_kernel_asus_sdm660-4.19Asus Zenfone Max Pro M1 (X00TD)/ M2 (X01BD) (Linux 4.19)
liqidecgandroid_kernel_xiaomi_sdm710Mi 8 SE (sirius) (Linux 4.9)
Abdelhay-Aliandroid_kernel_huawei_hi6250Huawei P20 lite (hi6250) (Linux 4.9)
reocatandroid_kernel_nokia_sdm660_ksuNokia 6.1 (2018)
Tuan Anhkernel_nokia_msm8998Nokia 8 (Repartitioned)/8 Sirocco (NLA/A1N)
bggRGjQaUbCoEandroid_kernel_samsung_sm8250Samsung Galaxy S20+ 5G (y2q)
SOVIET-ANDROIDkernel_xiaomi_raphaelXK20 Pro Raphael DSP & A only SAR support
dabao1955android_kernel_OPPO_PEQM00MediaTek devices in the OPPO Reno series on ColorOS13.x
Aflaungosandroid_kernel_motorola_msm8998Moto G6 Plus (evert)
TheNoFacekernel_oneplus_msm8998OnePlus 5/5T (cheeseburger/dumpling)
Nipin NA (Joker-V2)Xcalibur kernel (violet)Xiaomi Redmi Note 7 Pro (violet)
Sr-Hankernel_xiaomi_mojitoRedmi Note 10 (Sunny/Mojito)
dabao1955android_kernel_OPPO_OP4ED5OPPO Reno6 on ColorOS11.x/ColorOS12.x,kernel version4.14.186+
rushiranpiseandroid_kernel_samsung_exynos9610_mintKernel 4.14.194 exynos9610 Non-GKI Device, Added KernelSu using manual method
CN-Scarspixel_experience_kernel_xiaomi_sm8250_kernelSUPixel Experience Kernel 4.19.282 for Xiaomi Redmi K40S / Xiaomi Poco F4 (munch)
exerMiyo TokuSony Tama (akari, apollo, aurora, akatsuki) (Linux 4.9)
likkaiQuicksilver KernelXiaomi 11 Lite 5G NE (lisa)
awakened1712android_kernel_samsung_exynos9820Samsung S10/N10
awakened1712android_kernel_oneplus_sm8350Oneplus 9/9Pro
siimsekLightning KernelXiaomi Redmi Note 8/8T (ginkgo/willow)
GiovanYCringekernel_a50Galaxy A50 (A505f,fn,g,gn,gt)
Asriadi Rahimandroid_kernel_google_wahooGoogle Pixel 2/2XL
Flameandroid_kernel_xiaomi_sm8250POCO F3/Redmi K40/Mi 11X (alioth)
DawfukFRkernel_oneplus_sm8250 (Stellaris-Kernel)Oneplus 8 (Instantnoodle), Oneplus 8T (Kebab), Oneplus 8 Pro (Instantnoodlep), Oneplus 9R (lemonades) [msm-4.19]
Tejas Singhkernel_xiaomi_ginkgo (Cuh Kernel)Xiaomi Redmi Note 8/8T (ginkgo/willow)
Coconutatandroid_kernel_xiaomi_ruby_expRedmi Note 12 Pro / Pro+ For MIUI 14
Coconutatandroid_kernel_xiaomi_sdm845_expXiaomi Mi 8 Pro(UD)[equuleus]/Explorer Edition[ursa]
Abdul Wahid Khankernel_xiaomi_begoniaRedmi Note 8 Pro (Begonia/Begoniain)
Abdul Wahid Khankernel_xiaomi_marbleXiaomi Poco F5 / Redmi Note 12 Turbo (Marble)
zfdx123kernel_xiaomi_aliothRedmi K40 (alioth)
wxt1221android_kernel_oneplus_sdm845Oneplus 6 (enchilada) | Oneplus 6T (fajita) with no Retrofit Dynamic Partitions
zharzinhooKernel-Oriente-GuampMoto g9 play
hc841NovaCore_kernelMoto g9 play
zlm324android_kernel_xiaomi_msm8998_ksua lineage kernel forked from LineageOS official repository, version 4.4.302, added ksu.
zlm324android_kernel_xiaomi_msm8953_ksuXiaomi 5X (tiffany)
Vincent4440android_kernel_xiaomi_sm8250Poco F4: munch
zharzinhooandroid_kernel_motorola_cebuMoto g9 power
tiandoufayaleandroid_kernel_samsung_sm8250Samsung Tab S7 WIFI(T870)
starmoeandroid_kernel_oneplus_msm8998Oneplus 5/5T, kernel version 4.4.302
supechickenkernel_xiaomi_sm8350 Xiaomi 11T Pro (vili)
Vaz15kkernelSU-A70qSamsung Galaxy A70 ( A705MN / A705FN )
whyakariandroid_kernel_xiaomi_ginkgonon-GKI ginkgo
wulan17android_kernel_xiaomi_mt6765Redmi 6/6A (cereus/cactus)
officialputuidandroid_kernel_realme_mt6785Realme 6/6i(Indian)/6s/7/Narzo/Narzo 20 Pro/Narzo 30 4G (RM6785)

Phát hành dưới giấy phép GPL3.

- +
Skip to content

Thiết bị hỗ trợ không chính thức

WARNING

Đây là trang liệt kê kernel cho các thiết bị không dùng GKI được hỗ trợ bởi các lập trình viên khác.

WARNING

Trang này chỉ để cho bạn tìm thấy source cho thiết bị của bạn, nó HOÀN TOÀN KHÔNG được review bởi lập trình viên của KernelSU. Vậy nên hãy chấp nhận rủi ro khi sử dụng chúng.

Người bảo trìKho lưu trữThiết bị hỗ trợ
diphonsD8G_Kernel_oxygenPoco F3: alioth | Poco F4: munch | MI10T/PRO: Apollo
diphonsD8G_Kernel_SM8150Poco X3 Pro: Vayu | Bhima
diphonsD8G_Kernel_SDM845Poco F1 | MI8 | MiMix2S
diphonsD8G_Kernel_SM8350Vili
diphonsD8G_Kernel_MarblePoco F5 | Redmi Note 12 Turbo | Marble
th1nhhdkandroid_kernel_sony_sm8250-kernelsuSony Xperia 1 II | Sony Xperia 5 II
akash07knexus_kernel_xiaomi_sm8250Poco F4: munch
HMTheBoy154Darkmatter-kernelGeneric x86_64 devices running Android-x86
Asuka-mioandroid_kernel_xiaomi_casMi 10 Ultra: cas
xiaoleGunMiku_kernel_xiaomi_waynewayne
YamazakuraHkernel_xiaomi_cepheuscepheus for pixel experience
SakuraNotStupidandroid_kernel_xiaomi_sdm710Xiaomi MI 8 SE(sirius/xmsirius)
Aquarius223(paper)android_kernel_xiaomi_msm8998MI 6 (sagit) and MIX 2 (chiron) for LineageOS
SlackerStateandroid_kernel_xiaomi_sm6150Redmi K30 4G (phoenix)
RooGhz720kernel_xiaomi_sm6150REDMI NOTE 10 PRO (sweet)
OnlyTomInSecondandroid_kernel_xiaomi_sdm845Mi 8 (dipper) for LineageOS
Rohail33RealKing KernelApollo(Redmi K30S Ultra/Mi 10T/Mi 10T Pro),Alioth(Redmi K40/POCO F3/Mi 11X),Munch(Redmi K40S/POCO F4), both MIUI and AOSP.
lateautumn233android_kernel_oneplus_sm8250OnePlus 8 Serials
Sreeshankar KNeverSettle KernelOnePlus Nord (avicii)
Molyuuneko_kernel_xiaomi_gauguinRedmi Note 9 Pro/ Mi 10T Lite/ Mi 10i
guh0613android_kernel_oppo_sm8150OPPO Reno Ace (OP4A89)
LeviMarvinandroid_kernel_xiaomi_aliothRedmi K40 / POCO F3
cibimokernel_xiaomi_raphael_ksuXiaomi Redmi K20 Pro / Mi 9T Pro (raphael)
EndCreditskernel_xiaomi_sm7250Redmi K30 5G ( picasso ) , Redmi K30i 5G ( picasso_48m)
msnxandroid-msm-coral-4.14-android13Pixel 4 XL
SoDebugkernel_xiaomi_raphaelXiaomi Redmi K20 Pro / Mi 9T Pro (raphael)
H1mJTkernel_realme_RMX1901Realme X (RMX1901/RMX1901CN)
SonalSingh18android_kernel_xiaomi_sm6250Miatoll [curtana, excalibur, gram, joyeuse]
RooGhz720kernel_xiaomi_lavenderRedmi Note 7 (Lavender)
JunASAKAkernel_google_msm-4.9_KernelSUGoogle Pixel 3a & 3a XL (sargo & bonito)
RooGhz720kernel_asus_X01BDAsus Zenfone Max Pro M1/M2
Evans Mikekernel_xiaomi_raphael_bool-xXiaomi Redmi K20 Pro / Mi 9T Pro (raphael)
easterNdayKSU_Thyme_BuildBotXiaomi 10S
tedomi2705kernel_xiaomi_sdm660Xiaomi Redmi Note 6 Pro (tulip)
RyuujiXandroid_kernel_asus_sdm660-4.19Asus Zenfone Max Pro M1 (X00TD)/ M2 (X01BD) (Linux 4.19)
liqidecgandroid_kernel_xiaomi_sdm710Mi 8 SE (sirius) (Linux 4.9)
Abdelhay-Aliandroid_kernel_huawei_hi6250Huawei P20 lite (hi6250) (Linux 4.9)
reocatandroid_kernel_nokia_sdm660_ksuNokia 6.1 (2018)
Tuan Anhkernel_nokia_msm8998Nokia 8 (Repartitioned)/8 Sirocco (NLA/A1N)
bggRGjQaUbCoEandroid_kernel_samsung_sm8250Samsung Galaxy S20+ 5G (y2q)
SOVIET-ANDROIDkernel_xiaomi_raphaelXK20 Pro Raphael DSP & A only SAR support
dabao1955android_kernel_OPPO_PEQM00MediaTek devices in the OPPO Reno series on ColorOS13.x
Aflaungosandroid_kernel_motorola_msm8998Moto G6 Plus (evert)
TheNoFacekernel_oneplus_msm8998OnePlus 5/5T (cheeseburger/dumpling)
Nipin NA (Joker-V2)Xcalibur kernel (violet)Xiaomi Redmi Note 7 Pro (violet)
Sr-Hankernel_xiaomi_mojitoRedmi Note 10 (Sunny/Mojito)
dabao1955android_kernel_OPPO_OP4ED5OPPO Reno6 on ColorOS11.x/ColorOS12.x,kernel version4.14.186+
rushiranpiseandroid_kernel_samsung_exynos9610_mintKernel 4.14.194 exynos9610 Non-GKI Device, Added KernelSu using manual method
CN-Scarspixel_experience_kernel_xiaomi_sm8250_kernelSUPixel Experience Kernel 4.19.282 for Xiaomi Redmi K40S / Xiaomi Poco F4 (munch)
exerMiyo TokuSony Tama (akari, apollo, aurora, akatsuki) (Linux 4.9)
likkaiQuicksilver KernelXiaomi 11 Lite 5G NE (lisa)
awakened1712android_kernel_samsung_exynos9820Samsung S10/N10
awakened1712android_kernel_oneplus_sm8350Oneplus 9/9Pro
siimsekLightning KernelXiaomi Redmi Note 8/8T (ginkgo/willow)
GiovanYCringekernel_a50Galaxy A50 (A505f,fn,g,gn,gt)
Asriadi Rahimandroid_kernel_google_wahooGoogle Pixel 2/2XL
Flameandroid_kernel_xiaomi_sm8250POCO F3/Redmi K40/Mi 11X (alioth)
DawfukFRkernel_oneplus_sm8250 (Stellaris-Kernel)Oneplus 8 (Instantnoodle), Oneplus 8T (Kebab), Oneplus 8 Pro (Instantnoodlep), Oneplus 9R (lemonades) [msm-4.19]
Tejas Singhkernel_xiaomi_ginkgo (Cuh Kernel)Xiaomi Redmi Note 8/8T (ginkgo/willow)
Coconutatandroid_kernel_xiaomi_ruby_expRedmi Note 12 Pro / Pro+ For MIUI 14
Coconutatandroid_kernel_xiaomi_sdm845_expXiaomi Mi 8 Pro(UD)[equuleus]/Explorer Edition[ursa]
Abdul Wahid Khankernel_xiaomi_begoniaRedmi Note 8 Pro (Begonia/Begoniain)
Abdul Wahid Khankernel_xiaomi_marbleXiaomi Poco F5 / Redmi Note 12 Turbo (Marble)
zfdx123kernel_xiaomi_aliothRedmi K40 (alioth)
wxt1221android_kernel_oneplus_sdm845Oneplus 6 (enchilada) | Oneplus 6T (fajita) with no Retrofit Dynamic Partitions
zharzinhooKernel-Oriente-GuampMoto g9 play
hc841NovaCore_kernelMoto g9 play
zlm324android_kernel_xiaomi_msm8998_ksua lineage kernel forked from LineageOS official repository, version 4.4.302, added ksu.
zlm324android_kernel_xiaomi_msm8953_ksuXiaomi 5X (tiffany)
Vincent4440android_kernel_xiaomi_sm8250Poco F4: munch
zharzinhooandroid_kernel_motorola_cebuMoto g9 power
tiandoufayaleandroid_kernel_samsung_sm8250Samsung Tab S7 WIFI(T870)
starmoeandroid_kernel_oneplus_msm8998Oneplus 5/5T, kernel version 4.4.302
supechickenkernel_xiaomi_sm8350 Xiaomi 11T Pro (vili)
Vaz15kkernelSU-A70qSamsung Galaxy A70 ( A705MN / A705FN )
whyakariandroid_kernel_xiaomi_ginkgonon-GKI ginkgo
wulan17android_kernel_xiaomi_mt6765Redmi 6/6A (cereus/cactus)
officialputuidandroid_kernel_realme_mt6785Realme 6/6i(Indian)/6s/7/Narzo/Narzo 20 Pro/Narzo 30 4G (RM6785)

Phát hành dưới giấy phép GPL3.

+ \ No newline at end of file diff --git a/vi_VN/guide/what-is-kernelsu.html b/vi_VN/guide/what-is-kernelsu.html index 6f24fcc9fca5..e429b58df275 100644 --- a/vi_VN/guide/what-is-kernelsu.html +++ b/vi_VN/guide/what-is-kernelsu.html @@ -5,18 +5,19 @@ KernelSU là gì? | KernelSU - - + + + - - - - + + + + + -
Skip to content
On this page

KernelSU là gì?

KernelSU là một giải pháp root cho các thiết bị Android GKI, nó hoạt động ở chế độ kernel và cấp quyền root cho ứng dụng không gian người dùng trực tiếp trong không gian kernel.

Tính năng

Tính năng chính của KernelSU là Kernel-based (dựa trên Kernel). KernelSU hoạt động ở chế độ kernel nên nó có thể cung cấp giao diện kernel mà chúng ta chưa từng có trước đây. Ví dụ: chúng ta có thể thêm điểm dừng phần cứng vào bất kỳ quy trình nào ở chế độ kernel; Chúng ta có thể truy cập bộ nhớ vật lý của bất kỳ quy trình nào mà không bị phát hiện; Chúng ta còn có thể chặn bất kỳ syscall nào trong không gian kernel; v.v.

Ngoài ra, KernelSU còn cung cấp hệ thống mô-đun thông qua lớp phủ, cho phép bạn tải plugin tùy chỉnh vào hệ thống. Nó cũng cung cấp một cơ chế để sửa đổi các tập tin trong phân vùng /system.

Hướng dẫn sử dụng

Xin hãy xem: Cách cài đặt

Cách để build

Cách để build

Thảo luận

Phát hành dưới giấy phép GPL3.

- +
Skip to content

KernelSU là gì?

KernelSU là một giải pháp root cho các thiết bị Android GKI, nó hoạt động ở chế độ kernel và cấp quyền root cho ứng dụng không gian người dùng trực tiếp trong không gian kernel.

Tính năng

Tính năng chính của KernelSU là Kernel-based (dựa trên Kernel). KernelSU hoạt động ở chế độ kernel nên nó có thể cung cấp giao diện kernel mà chúng ta chưa từng có trước đây. Ví dụ: chúng ta có thể thêm điểm dừng phần cứng vào bất kỳ quy trình nào ở chế độ kernel; Chúng ta có thể truy cập bộ nhớ vật lý của bất kỳ quy trình nào mà không bị phát hiện; Chúng ta còn có thể chặn bất kỳ syscall nào trong không gian kernel; v.v.

Ngoài ra, KernelSU còn cung cấp hệ thống mô-đun thông qua lớp phủ, cho phép bạn tải plugin tùy chỉnh vào hệ thống. Nó cũng cung cấp một cơ chế để sửa đổi các tập tin trong phân vùng /system.

Hướng dẫn sử dụng

Xin hãy xem: Cách cài đặt

Cách để build

Cách để build

Thảo luận

Phát hành dưới giấy phép GPL3.

+ \ No newline at end of file diff --git a/vi_VN/index.html b/vi_VN/index.html index b645816b3657..39e168af24b2 100644 --- a/vi_VN/index.html +++ b/vi_VN/index.html @@ -5,18 +5,19 @@ Giải pháp root dựa trên kernel dành cho Android | KernelSU - - + + + - - - - + + + + + -
Skip to content

KernelSU

Giải pháp root dựa trên kernel dành cho Android

KernelSU

Dựa trên Kernel

KernelSU đang hoạt động ở chế độ kernel Linux, nó có nhiều quyền kiểm soát hơn đối với các ứng dụng userspace.

Kiểm soát truy cập bằng whitelist

Chỉ ứng dụng được cấp quyền root mới có thể truy cập `su`, các ứng dụng khác không thể nhận được su.

Quyền root bị hạn chế

KernelSU cho phép bạn tùy chỉnh uid, gid, group, capabilities và các quy tắc SELinux của su. Giới hạn sức mạnh của root.

Mô-đun & Nguồn mở

KernelSU hỗ trợ sửa đổi /system một cách systemless bằng overlayfs và nó có nguồn mở theo GPL-3.

Phát hành dưới giấy phép GPL3.

- +
Skip to content

KernelSU

Giải pháp root dựa trên kernel dành cho Android

KernelSU

Phát hành dưới giấy phép GPL3.

+ \ No newline at end of file diff --git a/zh_CN/guide/app-profile.html b/zh_CN/guide/app-profile.html index aba39d13ef3c..8ab44fed1e85 100644 --- a/zh_CN/guide/app-profile.html +++ b/zh_CN/guide/app-profile.html @@ -5,22 +5,27 @@ App Profile | KernelSU - - + + + - - - - + + + + + -
Skip to content
On this page

App Profile

App Profile 是 KernelSU 提供的一种针对各种应用自定义其使用配置的机制。

对授予了 root 权限(也即可以使用 su)的应用来说,App Profile 也可以称之为 Root Profile,它可以自定义 suuid, gid, groups, capabilities 以及 SELinux 规则,从而限制 root 用户的权限;比如可以针对防火墙应用仅授予网络权限,而不授予文件访问权限,针对冻结类应用仅授予 shell 权限而不是直接给 root;通过最小化权限原则把权力关进笼子里

对于没有被授予 root 权限的普通应用,App Profile 可以控制内核以及模块系统对此应用的行为;比如是否需要针对此应用卸载模块造成的修改等。内核和模块系统可以通过此配置决定是否要做一些类似“隐藏痕迹”类的操作。

Root Profile

UID、GID 和 groups

Linux 系统中有用户和组两个概念。每个用户都有一个用户 ID(UID),一个用户可以属于多个组,每个组也有组 ID(GID)。该 ID 用于识别系统的用户并确定用户可以访问哪些系统资源。

UID 为 0 的用户被称之为 root 用户,GID 为 0 的组被称之为 root 组;root 用户组通常拥有系统的最高权限。

对于 Android 系统来说,每一个 App 都是一个单独的用户(不考虑 share uid 的情况),拥有一个唯一的 UID。比如 0 是 root 用户,1000system2000 是 ADB shell,10000-19999 的是普通用户。

INFO

此处的 UID 跟 Android 系统的多用户,或者说工作资料(Work Profile),不是一个概念。工作资料实际上是对 UID 进行分片实现的,比如 10000-19999 是主用户,110000-119999 是工作资料;他们中的任何一个普通应用都拥有自己独有的 UID。

每一个 App 可以有若干个组,GID 使其主要的组,通常与 UID 一致;其他的组被称之为补充组(groups)。某些权限是通过组控制的,比如网络访问,蓝牙等。

例如,如果我们在 ADB shell 中执行 id 命令,会得到如下输出:

sh
oriole:/ $ id
-uid=2000(shell) gid=2000(shell) groups=2000(shell),1004(input),1007(log),1011(adb),1015(sdcard_rw),1028(sdcard_r),1078(ext_data_rw),1079(ext_obb_rw),3001(net_bt_admin),3002(net_bt),3003(inet),3006(net_bw_stats),3009(readproc),3011(uhid),3012(readtracefs) context=u:r:shell:s0

其中,UID 为 2000,GID 也即主要组 ID 也为 2000;除此之外它还在很多补充组里面,例如 inet 组代表可以创建 AF_INETAF_INET6 的 socket(访问网络),sdcard_rw 代表可以读写 sdcard 等。

KernelSU 的 Root Profile 可以自定义执行 su 后 root 进程的 UID, GID 和 groups。例如,你可以设置某个 root 应用的 Root Profile 其 UID 为 2000,这意味着此应用在使用 su 的时候,它的实际权限是 ADB Shell 级别;你可以去掉 groups 中的 inet,这样这个 su 就无法访问网络。

注意

App Profile 仅仅是控制 root 应用使用 su 后的权限,它并非控制 App 本身的权限!如果 App 本身申请了网络访问权限,那么它即使不使用 su 也可以访问网络;为 su 去掉 inet 组仅仅是让 su 无法访问网络。

与应用通过 su 主动切换用户或者组不同,Root Profile 是在内核中强制实施的,不依赖 root 应用的自觉行为,su 权限的授予完全取决于用户而非开发者。

Capabilities

Capabilities 是 Linux 的一种分权机制。

传统的 UNIX 系统为了执行权限检查,将进程分为两类:特权进程(其有效用户 ID 为 0,称为超级用户或 root)和非特权进程(其有效 UID 为非零)。特权进程会绕过所有内核权限检查,而非特权进程则根据其凭据(通常是有效UID、有效GID和补充组列表)进行完整的权限检查。

从 Linux 2.2开始,Linux 将传统上与超级用户关联的特权分解为独立的单元,称为 Capabilities(有的也翻译为“权能”),它们可以独立启用和禁用。

每一个 Capability 代表一个或者一类权限。比如 CAP_DAC_READ_SEARCH 就代表是否有能力绕过文件读取权限检查和目录读取和执行权限检查。如果一个有效 UID 为 0 的用户(root 用户)没有 CAP_DAC_READ_SEARCH 或者更高 Capalities,这意味着即使它是 root 也不能随意读取文件。

KernelSU 的 Root Profile 可以自定义执行 su 后 root 进程的 Capabilities,从而实现只授予“部分 root 权限”。与上面介绍的 UID, GID 不同,某些 root 应用就是需要 su 后 UID 是 0,此时我们可以通过限制这个 UID 为 0 的 root 用户的 Capabilities,就可以限制它能够执行的操作。

强烈建议

Linux 系统关于 Capability 的 官方文档,解释了每一项 Capability 所代表的能力,写的非常详细,如果你想要自定义 Capabilities,请务必先阅读此文档。

SELinux

SELinux 是一种强大的强制性权限访问控制(MAC)机制。它按照默认拒绝的原则运行:任何未经明确允许的行为都会被拒绝。

SELinux 可按两种全局模式运行:

  1. 宽容模式:权限拒绝事件会被记录下来,但不会被强制执行。
  2. 强制模式:权限拒绝事件会被记录下来强制执行。

警告

现代的 Android 系统极度依赖 SELinux 来保障整个系统的安全性,我们强烈建议您不要使用任何以“宽容模式”运行的自定义系统,因为那样与裸奔没什么区别。

SELinux 的完整概念比较复杂,我们这里不打算讲解它的具体工作方式,建议你先通过以下资料来了解其工作原理:

  1. wikipedia
  2. Redhat: what-is-selinux
  3. ArchLinux: SELinux

KernelSU 的 Root Profile 可以自定义执行 su 后 root 进程的 SELinux context,并且可以针对这个 context 设置特定的访问控制规则,从而更加精细化地控制 root 权限。

通常情况下,应用执行 su 后,会将进程切换到一个 不受任何限制 的 SELinux 域,比如 u:r:su:s0,通过 Root Profile,我们可以将它切换到一个自定义的域,比如 u:r:app1:s0,然后为这个域制定一系列规则:

sh
type app1
-enforce app1
-typeattribute app1 mlstrustedsubject
-allow app1 * * *

注意:此处的 allow app1 * * * 仅仅作为演示方便而使用,实际过程中不应使用这个规则,因为它跟 permissive 区别不大。

逃逸

如果 Root Profile 的配置不合理,那么可能会发生逃逸的情况:Root Profile 的限制会意外失效。

比如,如果你为 ADB shell 用户设置允许 root 权限(这是相当常见的情况);然后你给某个普通应用允许 root 权限,但是配置它的 root profile 中的 UID 为 2000(ADB shell 用户的 UID);那么此时,这个 App 可以通过执行两次 su 来获得完整的 root 权限:

  1. 第一次执行 su,由于 App Profile 强制生效,会正常切换到 UID 为 2000(adb shell) 而非 0(root)
  2. 第二次执行 su,由于此时它 UID 是 2000,而你给 2000(adb shell) 配置了允许 root,它会获得完整的 root 权限!

注意

这是完全符合预期的行为,并非 BUG!因此我们建议:

如果你的确需要给 adb 授予 root 权限(比如你是开发者),那么不建议你在配置 Root Profile 的时候将 UID 改成 2000,用 1000(system) 会更好。

Non Root Profile

卸载模块

KernelSU 提供了一种 systemless 的方式来修改系统分区,这是通过挂载 overlayfs 来实现的。但有些情况下,App 可能会对这种行为比较敏感;因此,我们可以通过设置“卸载模块”来卸载挂载在这些 App 上的模块。

另外,KernelSU 管理器的设置界面还提供了一个“默认卸载模块”的开关,这个开关默认情况下是开启的,这意味着如果不对 App 做额外的设置,默认情况下 KernelSU 或者某些模块会对此 App 执行卸载操作。当然,如果你不喜欢这个设置或者这个设置会影响某些 App,可以有如下选择:

  1. 保持“默认卸载模块”的开关,然后针对不需要“卸载模块”的 App 进行单独的设置,在 App Profile 中关闭“卸载模块”;(相当于“白名单“)。
  2. 关闭“默认卸载模块”的开关,然后针对需要“卸载模块”的 App 进行单独的设置,在 App Profile 中开启“卸载模块”;(相当于“黑名单“)。

INFO

KernelSU 在 5.10 及以上内核上,内核会执行“卸载模块”的操作;但在 5.10 以下的设备上,这个开关仅仅是一个“配置项”,KernelSU 本身不会做任何动作,一些模块(如 Zygisksu 会通过这个模块决定是否需要卸载)

在 GPL3 许可证下发布。

- +
Skip to content

App Profile

App Profile 是 KernelSU 提供的一种针对各种应用自定义其使用配置的机制。

对授予了 root 权限(也即可以使用 su)的应用来说,App Profile 也可以称之为 Root Profile,它可以自定义 suuid, gid, groups, capabilities 以及 SELinux 规则,从而限制 root 用户的权限;比如可以针对防火墙应用仅授予网络权限,而不授予文件访问权限,针对冻结类应用仅授予 shell 权限而不是直接给 root;通过最小化权限原则把权力关进笼子里

对于没有被授予 root 权限的普通应用,App Profile 可以控制内核以及模块系统对此应用的行为;比如是否需要针对此应用卸载模块造成的修改等。内核和模块系统可以通过此配置决定是否要做一些类似“隐藏痕迹”类的操作。

Root Profile

UID、GID 和 groups

Linux 系统中有用户和组两个概念。每个用户都有一个用户 ID(UID),一个用户可以属于多个组,每个组也有组 ID(GID)。该 ID 用于识别系统的用户并确定用户可以访问哪些系统资源。

UID 为 0 的用户被称之为 root 用户,GID 为 0 的组被称之为 root 组;root 用户组通常拥有系统的最高权限。

对于 Android 系统来说,每一个 App 都是一个单独的用户(不考虑 share uid 的情况),拥有一个唯一的 UID。比如 0 是 root 用户,1000system2000 是 ADB shell,10000-19999 的是普通用户。

INFO

此处的 UID 跟 Android 系统的多用户,或者说工作资料(Work Profile),不是一个概念。工作资料实际上是对 UID 进行分片实现的,比如 10000-19999 是主用户,110000-119999 是工作资料;他们中的任何一个普通应用都拥有自己独有的 UID。

每一个 App 可以有若干个组,GID 使其主要的组,通常与 UID 一致;其他的组被称之为补充组(groups)。某些权限是通过组控制的,比如网络访问,蓝牙等。

例如,如果我们在 ADB shell 中执行 id 命令,会得到如下输出:

sh
oriole:/ $ id
+uid=2000(shell) gid=2000(shell) groups=2000(shell),1004(input),1007(log),1011(adb),1015(sdcard_rw),1028(sdcard_r),1078(ext_data_rw),1079(ext_obb_rw),3001(net_bt_admin),3002(net_bt),3003(inet),3006(net_bw_stats),3009(readproc),3011(uhid),3012(readtracefs) context=u:r:shell:s0
oriole:/ $ id
+uid=2000(shell) gid=2000(shell) groups=2000(shell),1004(input),1007(log),1011(adb),1015(sdcard_rw),1028(sdcard_r),1078(ext_data_rw),1079(ext_obb_rw),3001(net_bt_admin),3002(net_bt),3003(inet),3006(net_bw_stats),3009(readproc),3011(uhid),3012(readtracefs) context=u:r:shell:s0

其中,UID 为 2000,GID 也即主要组 ID 也为 2000;除此之外它还在很多补充组里面,例如 inet 组代表可以创建 AF_INETAF_INET6 的 socket(访问网络),sdcard_rw 代表可以读写 sdcard 等。

KernelSU 的 Root Profile 可以自定义执行 su 后 root 进程的 UID, GID 和 groups。例如,你可以设置某个 root 应用的 Root Profile 其 UID 为 2000,这意味着此应用在使用 su 的时候,它的实际权限是 ADB Shell 级别;你可以去掉 groups 中的 inet,这样这个 su 就无法访问网络。

注意

App Profile 仅仅是控制 root 应用使用 su 后的权限,它并非控制 App 本身的权限!如果 App 本身申请了网络访问权限,那么它即使不使用 su 也可以访问网络;为 su 去掉 inet 组仅仅是让 su 无法访问网络。

与应用通过 su 主动切换用户或者组不同,Root Profile 是在内核中强制实施的,不依赖 root 应用的自觉行为,su 权限的授予完全取决于用户而非开发者。

Capabilities

Capabilities 是 Linux 的一种分权机制。

传统的 UNIX 系统为了执行权限检查,将进程分为两类:特权进程(其有效用户 ID 为 0,称为超级用户或 root)和非特权进程(其有效 UID 为非零)。特权进程会绕过所有内核权限检查,而非特权进程则根据其凭据(通常是有效UID、有效GID和补充组列表)进行完整的权限检查。

从 Linux 2.2开始,Linux 将传统上与超级用户关联的特权分解为独立的单元,称为 Capabilities(有的也翻译为“权能”),它们可以独立启用和禁用。

每一个 Capability 代表一个或者一类权限。比如 CAP_DAC_READ_SEARCH 就代表是否有能力绕过文件读取权限检查和目录读取和执行权限检查。如果一个有效 UID 为 0 的用户(root 用户)没有 CAP_DAC_READ_SEARCH 或者更高 Capalities,这意味着即使它是 root 也不能随意读取文件。

KernelSU 的 Root Profile 可以自定义执行 su 后 root 进程的 Capabilities,从而实现只授予“部分 root 权限”。与上面介绍的 UID, GID 不同,某些 root 应用就是需要 su 后 UID 是 0,此时我们可以通过限制这个 UID 为 0 的 root 用户的 Capabilities,就可以限制它能够执行的操作。

强烈建议

Linux 系统关于 Capability 的 官方文档,解释了每一项 Capability 所代表的能力,写的非常详细,如果你想要自定义 Capabilities,请务必先阅读此文档。

SELinux

SELinux 是一种强大的强制性权限访问控制(MAC)机制。它按照默认拒绝的原则运行:任何未经明确允许的行为都会被拒绝。

SELinux 可按两种全局模式运行:

  1. 宽容模式:权限拒绝事件会被记录下来,但不会被强制执行。
  2. 强制模式:权限拒绝事件会被记录下来强制执行。

警告

现代的 Android 系统极度依赖 SELinux 来保障整个系统的安全性,我们强烈建议您不要使用任何以“宽容模式”运行的自定义系统,因为那样与裸奔没什么区别。

SELinux 的完整概念比较复杂,我们这里不打算讲解它的具体工作方式,建议你先通过以下资料来了解其工作原理:

  1. wikipedia
  2. Redhat: what-is-selinux
  3. ArchLinux: SELinux

KernelSU 的 Root Profile 可以自定义执行 su 后 root 进程的 SELinux context,并且可以针对这个 context 设置特定的访问控制规则,从而更加精细化地控制 root 权限。

通常情况下,应用执行 su 后,会将进程切换到一个 不受任何限制 的 SELinux 域,比如 u:r:su:s0,通过 Root Profile,我们可以将它切换到一个自定义的域,比如 u:r:app1:s0,然后为这个域制定一系列规则:

sh
type app1
+enforce app1
+typeattribute app1 mlstrustedsubject
+allow app1 * * *
type app1
+enforce app1
+typeattribute app1 mlstrustedsubject
+allow app1 * * *

注意:此处的 allow app1 * * * 仅仅作为演示方便而使用,实际过程中不应使用这个规则,因为它跟 permissive 区别不大。

逃逸

如果 Root Profile 的配置不合理,那么可能会发生逃逸的情况:Root Profile 的限制会意外失效。

比如,如果你为 ADB shell 用户设置允许 root 权限(这是相当常见的情况);然后你给某个普通应用允许 root 权限,但是配置它的 root profile 中的 UID 为 2000(ADB shell 用户的 UID);那么此时,这个 App 可以通过执行两次 su 来获得完整的 root 权限:

  1. 第一次执行 su,由于 App Profile 强制生效,会正常切换到 UID 为 2000(adb shell) 而非 0(root)
  2. 第二次执行 su,由于此时它 UID 是 2000,而你给 2000(adb shell) 配置了允许 root,它会获得完整的 root 权限!

注意

这是完全符合预期的行为,并非 BUG!因此我们建议:

如果你的确需要给 adb 授予 root 权限(比如你是开发者),那么不建议你在配置 Root Profile 的时候将 UID 改成 2000,用 1000(system) 会更好。

Non Root Profile

卸载模块

KernelSU 提供了一种 systemless 的方式来修改系统分区,这是通过挂载 overlayfs 来实现的。但有些情况下,App 可能会对这种行为比较敏感;因此,我们可以通过设置“卸载模块”来卸载挂载在这些 App 上的模块。

另外,KernelSU 管理器的设置界面还提供了一个“默认卸载模块”的开关,这个开关默认情况下是开启的,这意味着如果不对 App 做额外的设置,默认情况下 KernelSU 或者某些模块会对此 App 执行卸载操作。当然,如果你不喜欢这个设置或者这个设置会影响某些 App,可以有如下选择:

  1. 保持“默认卸载模块”的开关,然后针对不需要“卸载模块”的 App 进行单独的设置,在 App Profile 中关闭“卸载模块”;(相当于“白名单“)。
  2. 关闭“默认卸载模块”的开关,然后针对需要“卸载模块”的 App 进行单独的设置,在 App Profile 中开启“卸载模块”;(相当于“黑名单“)。

INFO

KernelSU 在 5.10 及以上内核上,内核会执行“卸载模块”的操作;但在 5.10 以下的设备上,这个开关仅仅是一个“配置项”,KernelSU 本身不会做任何动作,一些模块(如 Zygisksu 会通过这个模块决定是否需要卸载)

在 GPL3 许可证下发布。

+ \ No newline at end of file diff --git a/zh_CN/guide/difference-with-magisk.html b/zh_CN/guide/difference-with-magisk.html index 9858d66b00f2..8a456dcd1941 100644 --- a/zh_CN/guide/difference-with-magisk.html +++ b/zh_CN/guide/difference-with-magisk.html @@ -5,18 +5,19 @@ KernelSU 模块与 Magisk 的差异 | KernelSU - - + + + - - - - + + + + + -
Skip to content
On this page

KernelSU 模块与 Magisk 的差异

虽然 KernelSU 模块与 Magisk 模块有很多相似之处,但由于它们的实现机制完全不同,因此不可避免地会有一些差异;如果你希望你的模块能同时在 Magisk 与 KernelSU 中运行,那么你必须了解这些差异。

相同之处

  • 模块文件格式: 都以 zip 的方式组织模块,并且模块的格式几乎相同
  • 模块安装目录: 都在 /data/adb/modules
  • systemless: 都支持通过模块的形式以 systemless 修改 /system
  • post-fs-data.sh: 执行时机完全一致,语义也完全一致
  • service.sh: 执行时机完全一致,语义也完全一致
  • system.prop: 完全相同
  • sepolicy.rule: 完全相同
  • BusyBox:脚本都在 BusyBox 中以“独立模式”运行

不同之处

在了解不同之处之前,你需要知道如何区分你的模块是运行在 KernelSU 还是运行在 Magisk 之中;在所有你可以运行模块脚本的地方(customize.sh, post-fs-data.sh, service.sh),你都可以通过环境变量KSU 来区分,在 KernelSU 中,这个环境变量将被设置为 true

以下是一些不同之处:

  1. KernelSU 的模块不支持在 Recovery 中安装。
  2. KernelSU 的模块没有内置的 Zygisk 支持(但你可以通过 ZygiskNext 来使用 Zygisk 模块)。
  3. KernelSU 模块替换或者删除文件与 Magisk 完全不同。KernelSU 不支持 .replace 方式,相反,你需要通过 mknod filename c 0 0 创建同名文件夹来删除对应文件。
  4. BusyBox 的目录不同;KernelSU 内置的 BusyBox 在 /data/adb/ksu/bin/busybox 而 Magisk 在 /data/adb/magisk/busybox注意此为 KernelSU 内部行为,未来可能会更改!
  5. KernelSU 不支持 .replace 文件;但 KernelSU 支持 REPLACEREMOVE 变量。
  6. KernelSU 新增了一种脚本 boot-completed.sh,以便在 Android 系统启动后运行某些任务。
  7. KernelSU 新增了一种脚本 post-mount.sh,以便在 Overlayfs 挂载后运行某些任务。

在 GPL3 许可证下发布。

- +
Skip to content

KernelSU 模块与 Magisk 的差异

虽然 KernelSU 模块与 Magisk 模块有很多相似之处,但由于它们的实现机制完全不同,因此不可避免地会有一些差异;如果你希望你的模块能同时在 Magisk 与 KernelSU 中运行,那么你必须了解这些差异。

相同之处

  • 模块文件格式: 都以 zip 的方式组织模块,并且模块的格式几乎相同
  • 模块安装目录: 都在 /data/adb/modules
  • systemless: 都支持通过模块的形式以 systemless 修改 /system
  • post-fs-data.sh: 执行时机完全一致,语义也完全一致
  • service.sh: 执行时机完全一致,语义也完全一致
  • system.prop: 完全相同
  • sepolicy.rule: 完全相同
  • BusyBox:脚本都在 BusyBox 中以“独立模式”运行

不同之处

在了解不同之处之前,你需要知道如何区分你的模块是运行在 KernelSU 还是运行在 Magisk 之中;在所有你可以运行模块脚本的地方(customize.sh, post-fs-data.sh, service.sh),你都可以通过环境变量KSU 来区分,在 KernelSU 中,这个环境变量将被设置为 true

以下是一些不同之处:

  1. KernelSU 的模块不支持在 Recovery 中安装。
  2. KernelSU 的模块没有内置的 Zygisk 支持(但你可以通过 ZygiskNext 来使用 Zygisk 模块)。
  3. KernelSU 模块替换或者删除文件与 Magisk 完全不同。KernelSU 不支持 .replace 方式,相反,你需要通过 mknod filename c 0 0 创建同名文件夹来删除对应文件。
  4. BusyBox 的目录不同;KernelSU 内置的 BusyBox 在 /data/adb/ksu/bin/busybox 而 Magisk 在 /data/adb/magisk/busybox注意此为 KernelSU 内部行为,未来可能会更改!
  5. KernelSU 不支持 .replace 文件;但 KernelSU 支持 REPLACEREMOVE 变量。
  6. KernelSU 新增了一种脚本 boot-completed.sh,以便在 Android 系统启动后运行某些任务。
  7. KernelSU 新增了一种脚本 post-mount.sh,以便在 Overlayfs 挂载后运行某些任务。

在 GPL3 许可证下发布。

+ \ No newline at end of file diff --git a/zh_CN/guide/faq.html b/zh_CN/guide/faq.html index 14dd26897fd1..59dd7b607419 100644 --- a/zh_CN/guide/faq.html +++ b/zh_CN/guide/faq.html @@ -5,18 +5,19 @@ 常见问题 | KernelSU - - + + + - - - - + + + + + -
Skip to content
On this page

常见问题

KernelSU 是否支持我的设备 ?

首先,您的设备应该能够解锁 bootloader。 如果不能,则不支持。

然后在你的设备上安装 KernelSU 管理器并打开它,如果它显示 不支持 ,那么你的设备没有官方支持的开箱即用的 boot image;但你可以自己编译内核集成 KernelSU 进而使用它。

KernelSU 是否需要解锁 Bootloader ?

当然需要。

KernelSU 是否支持模块 ?

支持,但它是早期版本,可能有问题。请等待它稳定 😃

KernelSU 是否支持 Xposed ?

支持。DreamlandTaiChi 可以正常运行。LSPosed 可以在 ZygiskNext 的支持下正常运行。

KernelSU 支持 Zygisk 吗?

KernelSU 本体不支持 Zygisk,但是你可以用 ZygiskNext 来使用 Zygisk 模块。

KernelSU 与 Magisk 兼容吗 ?

KernelSU 的模块系统与 Magisk 的 magic mount 有冲突,如果 KernelSU 中启用了任何模块,那么整个 Magisk 将无法工作。

但是如果你只使用 KernelSU 的 su,那么它会和 Magisk 一起工作:KernelSU 修改 kernel 、 Magisk 修改 ramdisk,它们可以一起工作。

KernelSU 会替代 Magisk 吗?

我们不这么认为,这也不是我们的目标。Magisk 对于用户空间 root 解决方案来说已经足够好了,它会存活很久。KernelSU 的目标是为用户提供内核接口,而不是替代 Magisk。

KernelSU 可以支持非 GKI 设备吗?

可以。但是你应该下载内核源代码并将 KernelSU 集成到源代码树中并自己编译内核。

KernelSU 支持 Android 12 以下的设备吗?

影响 KernelSU 兼容性的是设备内核的版本,它与设备的 Android 版本没有直接的关系。唯一有关联的是:出厂 Android 12 的设备,一定是 5.10 或更高的内核(GKI设备);因此结论如下:

  1. 出厂 Android 12 的设备必定是支持的(GKI 设备)
  2. 旧版本内核的设备(即使是 Android 12,也可能是旧内核)是兼容的(你需要自己编译内核)

KernelSU 可以支持旧内核吗?

可以,目前最低支持到 4.14;更低的版本你需要手动移植它,欢迎 PR !

如何为旧内核集成 KernelSU?

参考教程

为什么我手机系统是 Android 13,但内核版本却是 "android12-5.10"?

内核版本与 Android 版本无关,如果你需要刷入 KernelSU,请永远使用内核版本而非 Android 版本,如果你为 "android12-5.10" 的设备刷入 Android 13 的内核,等待你的将是 bootloop.

KernelSU 支持 --mount-master/全局挂载命名空间吗?

目前没有(未来可能会支持),但实际上有很多种办法手动进入全局命名空间,无需 su 内置支持,比如:

  1. nsenter -t 1 -m sh 可以获得一个全局 mount namespace 的 shell.
  2. 在你要执行的命令之前添加 nsenter --mount=/proc/1/ns/mnt 就可以让此命令在全局 mount namespace 下执行。 KernelSU 本身也使用了 这种方法

我是 GKI1.0, 能用 KernelSU 吗?

GKI1 跟 GKI2 完全是两个东西,所以你需要自行编译内核。

在 GPL3 许可证下发布。

- +
Skip to content

常见问题

KernelSU 是否支持我的设备 ?

首先,您的设备应该能够解锁 bootloader。 如果不能,则不支持。

然后在你的设备上安装 KernelSU 管理器并打开它,如果它显示 不支持 ,那么你的设备没有官方支持的开箱即用的 boot image;但你可以自己编译内核集成 KernelSU 进而使用它。

KernelSU 是否需要解锁 Bootloader ?

当然需要。

KernelSU 是否支持模块 ?

支持,但它是早期版本,可能有问题。请等待它稳定 😃

KernelSU 是否支持 Xposed ?

支持。DreamlandTaiChi 可以正常运行。LSPosed 可以在 ZygiskNext 的支持下正常运行。

KernelSU 支持 Zygisk 吗?

KernelSU 本体不支持 Zygisk,但是你可以用 ZygiskNext 来使用 Zygisk 模块。

KernelSU 与 Magisk 兼容吗 ?

KernelSU 的模块系统与 Magisk 的 magic mount 有冲突,如果 KernelSU 中启用了任何模块,那么整个 Magisk 将无法工作。

但是如果你只使用 KernelSU 的 su,那么它会和 Magisk 一起工作:KernelSU 修改 kernel 、 Magisk 修改 ramdisk,它们可以一起工作。

KernelSU 会替代 Magisk 吗?

我们不这么认为,这也不是我们的目标。Magisk 对于用户空间 root 解决方案来说已经足够好了,它会存活很久。KernelSU 的目标是为用户提供内核接口,而不是替代 Magisk。

KernelSU 可以支持非 GKI 设备吗?

可以。但是你应该下载内核源代码并将 KernelSU 集成到源代码树中并自己编译内核。

KernelSU 支持 Android 12 以下的设备吗?

影响 KernelSU 兼容性的是设备内核的版本,它与设备的 Android 版本没有直接的关系。唯一有关联的是:出厂 Android 12 的设备,一定是 5.10 或更高的内核(GKI设备);因此结论如下:

  1. 出厂 Android 12 的设备必定是支持的(GKI 设备)
  2. 旧版本内核的设备(即使是 Android 12,也可能是旧内核)是兼容的(你需要自己编译内核)

KernelSU 可以支持旧内核吗?

可以,目前最低支持到 4.14;更低的版本你需要手动移植它,欢迎 PR !

如何为旧内核集成 KernelSU?

参考教程

为什么我手机系统是 Android 13,但内核版本却是 "android12-5.10"?

内核版本与 Android 版本无关,如果你需要刷入 KernelSU,请永远使用内核版本而非 Android 版本,如果你为 "android12-5.10" 的设备刷入 Android 13 的内核,等待你的将是 bootloop.

KernelSU 支持 --mount-master/全局挂载命名空间吗?

目前没有(未来可能会支持),但实际上有很多种办法手动进入全局命名空间,无需 su 内置支持,比如:

  1. nsenter -t 1 -m sh 可以获得一个全局 mount namespace 的 shell.
  2. 在你要执行的命令之前添加 nsenter --mount=/proc/1/ns/mnt 就可以让此命令在全局 mount namespace 下执行。 KernelSU 本身也使用了 这种方法

我是 GKI1.0, 能用 KernelSU 吗?

GKI1 跟 GKI2 完全是两个东西,所以你需要自行编译内核。

在 GPL3 许可证下发布。

+ \ No newline at end of file diff --git a/zh_CN/guide/hidden-features.html b/zh_CN/guide/hidden-features.html index 616cab1a72da..16fe6d510923 100644 --- a/zh_CN/guide/hidden-features.html +++ b/zh_CN/guide/hidden-features.html @@ -5,18 +5,19 @@ 隐藏功能 | KernelSU - - + + + - - - - + + + + + -
Skip to content
On this page

隐藏功能

ksurc

默认情况下,/system/bin/sh 会加载 /system/etc/mkshrc

可以通过创建 /data/adb/ksu/.ksurc 文件来让 su 加载该文件而不是 /system/etc/mkshrc

在 GPL3 许可证下发布。

- +
Skip to content

隐藏功能

ksurc

默认情况下,/system/bin/sh 会加载 /system/etc/mkshrc

可以通过创建 /data/adb/ksu/.ksurc 文件来让 su 加载该文件而不是 /system/etc/mkshrc

在 GPL3 许可证下发布。

+ \ No newline at end of file diff --git a/zh_CN/guide/how-to-build.html b/zh_CN/guide/how-to-build.html index b0bdf7cd8b14..8d63c80e731c 100644 --- a/zh_CN/guide/how-to-build.html +++ b/zh_CN/guide/how-to-build.html @@ -5,21 +5,25 @@ 如何构建 KernelSU? | KernelSU - - + + + - - - - + + + + + -
Skip to content
On this page

如何构建 KernelSU?

首先,您应该阅读内核构建的 Android 官方文档:

  1. 构建内核
  2. 通用内核映像 (GKI) 发布构建

WARNING

本文档适用于 GKI 设备,如果你是旧内核,请参考如何为非GKI设备集成 KernelSU

构建内核

同步内核源码

sh
repo init -u https://android.googlesource.com/kernel/manifest
-mv <kernel_manifest.xml> .repo/manifests
-repo init -m manifest.xml
-repo sync

<kernel_manifest.xml> 是一个可以唯一确定构建的清单文件,您可以使用该清单进行可重新预测的构建。 您应该从 通用内核映像 (GKI) 发布构建 下载清单文件

构建

请先查看 官方文档

例如,我们需要构建 aarch64 内核镜像:

sh
LTO=thin BUILD_CONFIG=common/build.config.gki.aarch64 build/build.sh

不要忘记添加 LTO=thin, 否则,如果您的计算机内存小于 24GB,构建可能会失败.

从 Android 13 开始,内核由 bazel 构建:

sh
tools/bazel build --config=fast //common:kernel_aarch64_dist

使用 KernelSU 构建内核

如果您可以成功构建内核,那么构建 KernelSU 就很容易,根据自己的需求在内核源代码根目录中运行以下任一命令:

  • 最新tag(稳定版本)
sh
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -
  • main分支(开发版本)
sh
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -s main
  • 指定tag(比如v0.5.2)
sh
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -s v0.5.2

然后重建内核,您将获得带有 KernelSU 的内核映像!

在 GPL3 许可证下发布。

- +
Skip to content

如何构建 KernelSU?

首先,您应该阅读内核构建的 Android 官方文档:

  1. 构建内核
  2. 通用内核映像 (GKI) 发布构建

WARNING

本文档适用于 GKI 设备,如果你是旧内核,请参考如何为非GKI设备集成 KernelSU

构建内核

同步内核源码

sh
repo init -u https://android.googlesource.com/kernel/manifest
+mv <kernel_manifest.xml> .repo/manifests
+repo init -m manifest.xml
+repo sync
repo init -u https://android.googlesource.com/kernel/manifest
+mv <kernel_manifest.xml> .repo/manifests
+repo init -m manifest.xml
+repo sync

<kernel_manifest.xml> 是一个可以唯一确定构建的清单文件,您可以使用该清单进行可重新预测的构建。 您应该从 通用内核映像 (GKI) 发布构建 下载清单文件

构建

请先查看 官方文档

例如,我们需要构建 aarch64 内核镜像:

sh
LTO=thin BUILD_CONFIG=common/build.config.gki.aarch64 build/build.sh
LTO=thin BUILD_CONFIG=common/build.config.gki.aarch64 build/build.sh

不要忘记添加 LTO=thin, 否则,如果您的计算机内存小于 24GB,构建可能会失败.

从 Android 13 开始,内核由 bazel 构建:

sh
tools/bazel build --config=fast //common:kernel_aarch64_dist
tools/bazel build --config=fast //common:kernel_aarch64_dist

使用 KernelSU 构建内核

如果您可以成功构建内核,那么构建 KernelSU 就很容易,根据自己的需求在内核源代码根目录中运行以下任一命令:

  • 最新tag(稳定版本)
sh
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -
  • main分支(开发版本)
sh
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -s main
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -s main
  • 指定tag(比如v0.5.2)
sh
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -s v0.5.2
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -s v0.5.2

然后重建内核,您将获得带有 KernelSU 的内核映像!

在 GPL3 许可证下发布。

+ \ No newline at end of file diff --git a/zh_CN/guide/how-to-integrate-for-non-gki.html b/zh_CN/guide/how-to-integrate-for-non-gki.html index 54a2e876da98..562893798253 100644 --- a/zh_CN/guide/how-to-integrate-for-non-gki.html +++ b/zh_CN/guide/how-to-integrate-for-non-gki.html @@ -5,169 +5,321 @@ 如何为非 GKI 内核集成 KernelSU | KernelSU - - + + + - - - - + + + + + -
Skip to content
On this page

如何为非 GKI 内核集成 KernelSU

KernelSU 可以被集成到非 GKI 内核中,现在它最低支持到内核 4.14 版本;理论上也可以支持更低的版本。

由于非 GKI 内核的碎片化极其严重,因此通常没有统一的方法来编译它,所以我们也无法为非 GKI 设备提供 boot 镜像。但你完全可以自己集成 KernelSU 然后编译内核使用。

首先,你必须有能力从你设备的内核源码编译出一个可以开机并且能正常使用的内核,如果内核不开源,这通常难以做到。

如果你已经做好了上述准备,那有两个方法来集成 KernelSU 到你的内核之中。

  1. 借助 kprobe 自动集成
  2. 手动修改内核源码

使用 kprobe 集成

KernelSU 使用 kprobe 机制来做内核的相关 hook,如果 kprobe 可以在你编译的内核中正常运行,那么推荐用这个方法来集成。

首先,把 KernelSU 添加到你的内核源码树,在内核的根目录执行以下命令:

  • 最新tag(稳定版本)
sh
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -
  • main分支(开发版本)
sh
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -s main
  • 指定tag(比如v0.5.2)
sh
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -s v0.5.2

然后,你需要检查你的内核是否开启了 kprobe 相关的配置,如果没有开启,需要添加以下配置:

CONFIG_KPROBES=y
-CONFIG_HAVE_KPROBES=y
-CONFIG_KPROBE_EVENTS=y

最后,重新编译你的内核即可。

如果你发现KPROBES仍未生效,很有可能是因为它的依赖项CONFIG_MODULES没有被启用(如果还是未生效请键入make menuconfig搜索KPROBES 的其它依赖并启用 )

如果你在集成 KernelSU 之后手机无法启动,那么很可能你的内核中 kprobe 工作不正常,你需要修复这个 bug 或者用第二种方法。

如何验证是否是 kprobe 的问题?

注释掉 KernelSU/kernel/ksu.cksu_enable_sucompat()ksu_enable_ksud(),如果正常开机,那么就是 kprobe 的问题;或者你可以手动尝试使用 kprobe 功能,如果不正常,手机会直接重启。

手动修改内核源码

如果 kprobe 工作不正常(通常是上游的 bug 或者内核版本过低),那你可以尝试这种方法:

首先,把 KernelSU 添加到你的内核源码树,在内核的根目录执行以下命令:

sh
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -

然后,手动修改内核源码,你可以参考下面这个 patch:

diff
diff --git a/fs/exec.c b/fs/exec.c
-index ac59664eaecf..bdd585e1d2cc 100644
---- a/fs/exec.c
-+++ b/fs/exec.c
-@@ -1890,11 +1890,14 @@ static int __do_execve_file(int fd, struct filename *filename,
- 	return retval;
- }
- 
-+extern bool ksu_execveat_hook __read_mostly;
-+extern int ksu_handle_execveat(int *fd, struct filename **filename_ptr, void *argv,
-+			void *envp, int *flags);
-+extern int ksu_handle_execveat_sucompat(int *fd, struct filename **filename_ptr,
-+				 void *argv, void *envp, int *flags);
- static int do_execveat_common(int fd, struct filename *filename,
- 			      struct user_arg_ptr argv,
- 			      struct user_arg_ptr envp,
- 			      int flags)
- {
-+	if (unlikely(ksu_execveat_hook))
-+		ksu_handle_execveat(&fd, &filename, &argv, &envp, &flags);
-+	else
-+		ksu_handle_execveat_sucompat(&fd, &filename, &argv, &envp, &flags);
- 	return __do_execve_file(fd, filename, argv, envp, flags, NULL);
- }
- 
-diff --git a/fs/open.c b/fs/open.c
-index 05036d819197..965b84d486b8 100644
---- a/fs/open.c
-+++ b/fs/open.c
-@@ -348,6 +348,8 @@ SYSCALL_DEFINE4(fallocate, int, fd, int, mode, loff_t, offset, loff_t, len)
- 	return ksys_fallocate(fd, mode, offset, len);
- }
- 
-+extern int ksu_handle_faccessat(int *dfd, const char __user **filename_user, int *mode,
-+			 int *flags);
- /*
-  * access() needs to use the real uid/gid, not the effective uid/gid.
-  * We do this by temporarily clearing all FS-related capabilities and
-@@ -355,6 +357,7 @@ SYSCALL_DEFINE4(fallocate, int, fd, int, mode, loff_t, offset, loff_t, len)
-  */
- long do_faccessat(int dfd, const char __user *filename, int mode)
- {
-+	ksu_handle_faccessat(&dfd, &filename, &mode, NULL);
- 	const struct cred *old_cred;
- 	struct cred *override_cred;
- 	struct path path;
-diff --git a/fs/read_write.c b/fs/read_write.c
-index 650fc7e0f3a6..55be193913b6 100644
---- a/fs/read_write.c
-+++ b/fs/read_write.c
-@@ -434,10 +434,14 @@ ssize_t kernel_read(struct file *file, void *buf, size_t count, loff_t *pos)
- }
- EXPORT_SYMBOL(kernel_read);
- 
-+extern bool ksu_vfs_read_hook __read_mostly;
-+extern int ksu_handle_vfs_read(struct file **file_ptr, char __user **buf_ptr,
-+			size_t *count_ptr, loff_t **pos);
- ssize_t vfs_read(struct file *file, char __user *buf, size_t count, loff_t *pos)
- {
- 	ssize_t ret;
- 
-+	if (unlikely(ksu_vfs_read_hook))
-+		ksu_handle_vfs_read(&file, &buf, &count, &pos);
-+
- 	if (!(file->f_mode & FMODE_READ))
- 		return -EBADF;
- 	if (!(file->f_mode & FMODE_CAN_READ))
-diff --git a/fs/stat.c b/fs/stat.c
-index 376543199b5a..82adcef03ecc 100644
---- a/fs/stat.c
-+++ b/fs/stat.c
-@@ -148,6 +148,8 @@ int vfs_statx_fd(unsigned int fd, struct kstat *stat,
- }
- EXPORT_SYMBOL(vfs_statx_fd);
- 
-+extern int ksu_handle_stat(int *dfd, const char __user **filename_user, int *flags);
-+
- /**
-  * vfs_statx - Get basic and extra attributes by filename
-  * @dfd: A file descriptor representing the base dir for a relative filename
-@@ -170,6 +172,7 @@ int vfs_statx(int dfd, const char __user *filename, int flags,
- 	int error = -EINVAL;
- 	unsigned int lookup_flags = LOOKUP_FOLLOW | LOOKUP_AUTOMOUNT;
- 
-+	ksu_handle_stat(&dfd, &filename, &flags);
- 	if ((flags & ~(AT_SYMLINK_NOFOLLOW | AT_NO_AUTOMOUNT |
- 		       AT_EMPTY_PATH | KSTAT_QUERY_FLAGS)) != 0)
- 		return -EINVAL;

主要是要改四个地方:

  1. do_faccessat,通常位于 fs/open.c
  2. do_execveat_common,通常位于 fs/exec.c
  3. vfs_read,通常位于 fs/read_write.c
  4. vfs_statx,通常位于 fs/stat.c

如果你的内核没有 vfs_statx, 使用 vfs_fstatat 来代替它:

diff
diff --git a/fs/stat.c b/fs/stat.c
-index 068fdbcc9e26..5348b7bb9db2 100644
---- a/fs/stat.c
-+++ b/fs/stat.c
-@@ -87,6 +87,8 @@ int vfs_fstat(unsigned int fd, struct kstat *stat)
- }
- EXPORT_SYMBOL(vfs_fstat);
- 
-+extern int ksu_handle_stat(int *dfd, const char __user **filename_user, int *flags);
-+
- int vfs_fstatat(int dfd, const char __user *filename, struct kstat *stat,
- 		int flag)
- {
-@@ -94,6 +96,8 @@ int vfs_fstatat(int dfd, const char __user *filename, struct kstat *stat,
- 	int error = -EINVAL;
- 	unsigned int lookup_flags = 0;
- 
-+	ksu_handle_stat(&dfd, &filename, &flag);
-+
- 	if ((flag & ~(AT_SYMLINK_NOFOLLOW | AT_NO_AUTOMOUNT |
- 		      AT_EMPTY_PATH)) != 0)
- 		goto out;

对于早于 4.17 的内核,如果没有 do_faccessat,可以直接找到 faccessat 系统调用的定义然后修改:

diff
diff --git a/fs/open.c b/fs/open.c
-index 2ff887661237..e758d7db7663 100644
---- a/fs/open.c
-+++ b/fs/open.c
-@@ -355,6 +355,9 @@ SYSCALL_DEFINE4(fallocate, int, fd, int, mode, loff_t, offset, loff_t, len)
- 	return error;
- }
- 
-+extern int ksu_handle_faccessat(int *dfd, const char __user **filename_user, int *mode,
-+			        int *flags);
-+
- /*
-  * access() needs to use the real uid/gid, not the effective uid/gid.
-  * We do this by temporarily clearing all FS-related capabilities and
-@@ -370,6 +373,8 @@ SYSCALL_DEFINE3(faccessat, int, dfd, const char __user *, filename, int, mode)
- 	int res;
- 	unsigned int lookup_flags = LOOKUP_FOLLOW;
- 
-+	ksu_handle_faccessat(&dfd, &filename, &mode, NULL);
-+
- 	if (mode & ~S_IRWXO)	/* where's F_OK, X_OK, W_OK, R_OK? */
- 		return -EINVAL;

要使用 KernelSU 内置的安全模式,你还需要修改 drivers/input/input.c 中的 input_handle_event 方法:

TIP

强烈建议开启此功能,对用户救砖会非常有帮助!

diff
diff --git a/drivers/input/input.c b/drivers/input/input.c
-index 45306f9ef247..815091ebfca4 100755
---- a/drivers/input/input.c
-+++ b/drivers/input/input.c
-@@ -367,10 +367,13 @@ static int input_get_disposition(struct input_dev *dev,
- 	return disposition;
- }
- 
-+extern bool ksu_input_hook __read_mostly;
-+extern int ksu_handle_input_handle_event(unsigned int *type, unsigned int *code, int *value);
-+
- static void input_handle_event(struct input_dev *dev,
- 			       unsigned int type, unsigned int code, int value)
- {
-	int disposition = input_get_disposition(dev, type, code, &value);
-+
-+	if (unlikely(ksu_input_hook))
-+		ksu_handle_input_handle_event(&type, &code, &value);
- 
- 	if (disposition != INPUT_IGNORE_EVENT && type != EV_SYN)
- 		add_input_randomness(type, code, value);

改完之后重新编译内核即可。

在 GPL3 许可证下发布。

- +
Skip to content

如何为非 GKI 内核集成 KernelSU

KernelSU 可以被集成到非 GKI 内核中,现在它最低支持到内核 4.14 版本;理论上也可以支持更低的版本。

由于非 GKI 内核的碎片化极其严重,因此通常没有统一的方法来编译它,所以我们也无法为非 GKI 设备提供 boot 镜像。但你完全可以自己集成 KernelSU 然后编译内核使用。

首先,你必须有能力从你设备的内核源码编译出一个可以开机并且能正常使用的内核,如果内核不开源,这通常难以做到。

如果你已经做好了上述准备,那有两个方法来集成 KernelSU 到你的内核之中。

  1. 借助 kprobe 自动集成
  2. 手动修改内核源码

使用 kprobe 集成

KernelSU 使用 kprobe 机制来做内核的相关 hook,如果 kprobe 可以在你编译的内核中正常运行,那么推荐用这个方法来集成。

首先,把 KernelSU 添加到你的内核源码树,在内核的根目录执行以下命令:

  • 最新tag(稳定版本)
sh
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -
  • main分支(开发版本)
sh
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -s main
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -s main
  • 指定tag(比如v0.5.2)
sh
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -s v0.5.2
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -s v0.5.2

然后,你需要检查你的内核是否开启了 kprobe 相关的配置,如果没有开启,需要添加以下配置:

CONFIG_KPROBES=y
+CONFIG_HAVE_KPROBES=y
+CONFIG_KPROBE_EVENTS=y
CONFIG_KPROBES=y
+CONFIG_HAVE_KPROBES=y
+CONFIG_KPROBE_EVENTS=y

最后,重新编译你的内核即可。

如果你发现KPROBES仍未生效,很有可能是因为它的依赖项CONFIG_MODULES没有被启用(如果还是未生效请键入make menuconfig搜索KPROBES 的其它依赖并启用 )

如果你在集成 KernelSU 之后手机无法启动,那么很可能你的内核中 kprobe 工作不正常,你需要修复这个 bug 或者用第二种方法。

如何验证是否是 kprobe 的问题?

注释掉 KernelSU/kernel/ksu.cksu_enable_sucompat()ksu_enable_ksud(),如果正常开机,那么就是 kprobe 的问题;或者你可以手动尝试使用 kprobe 功能,如果不正常,手机会直接重启。

手动修改内核源码

如果 kprobe 工作不正常(通常是上游的 bug 或者内核版本过低),那你可以尝试这种方法:

首先,把 KernelSU 添加到你的内核源码树,在内核的根目录执行以下命令:

sh
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -

然后,手动修改内核源码,你可以参考下面这个 patch:

diff
diff --git a/fs/exec.c b/fs/exec.c
+index ac59664eaecf..bdd585e1d2cc 100644
+--- a/fs/exec.c
++++ b/fs/exec.c
+@@ -1890,11 +1890,14 @@ static int __do_execve_file(int fd, struct filename *filename,
+ 	return retval;
+ }
+ 
++extern bool ksu_execveat_hook __read_mostly;
++extern int ksu_handle_execveat(int *fd, struct filename **filename_ptr, void *argv,
++			void *envp, int *flags);
++extern int ksu_handle_execveat_sucompat(int *fd, struct filename **filename_ptr,
++				 void *argv, void *envp, int *flags);
+ static int do_execveat_common(int fd, struct filename *filename,
+ 			      struct user_arg_ptr argv,
+ 			      struct user_arg_ptr envp,
+ 			      int flags)
+ {
++	if (unlikely(ksu_execveat_hook))
++		ksu_handle_execveat(&fd, &filename, &argv, &envp, &flags);
++	else
++		ksu_handle_execveat_sucompat(&fd, &filename, &argv, &envp, &flags);
+ 	return __do_execve_file(fd, filename, argv, envp, flags, NULL);
+ }
+ 
+diff --git a/fs/open.c b/fs/open.c
+index 05036d819197..965b84d486b8 100644
+--- a/fs/open.c
++++ b/fs/open.c
+@@ -348,6 +348,8 @@ SYSCALL_DEFINE4(fallocate, int, fd, int, mode, loff_t, offset, loff_t, len)
+ 	return ksys_fallocate(fd, mode, offset, len);
+ }
+ 
++extern int ksu_handle_faccessat(int *dfd, const char __user **filename_user, int *mode,
++			 int *flags);
+ /*
+  * access() needs to use the real uid/gid, not the effective uid/gid.
+  * We do this by temporarily clearing all FS-related capabilities and
+@@ -355,6 +357,7 @@ SYSCALL_DEFINE4(fallocate, int, fd, int, mode, loff_t, offset, loff_t, len)
+  */
+ long do_faccessat(int dfd, const char __user *filename, int mode)
+ {
++	ksu_handle_faccessat(&dfd, &filename, &mode, NULL);
+ 	const struct cred *old_cred;
+ 	struct cred *override_cred;
+ 	struct path path;
+diff --git a/fs/read_write.c b/fs/read_write.c
+index 650fc7e0f3a6..55be193913b6 100644
+--- a/fs/read_write.c
++++ b/fs/read_write.c
+@@ -434,10 +434,14 @@ ssize_t kernel_read(struct file *file, void *buf, size_t count, loff_t *pos)
+ }
+ EXPORT_SYMBOL(kernel_read);
+ 
++extern bool ksu_vfs_read_hook __read_mostly;
++extern int ksu_handle_vfs_read(struct file **file_ptr, char __user **buf_ptr,
++			size_t *count_ptr, loff_t **pos);
+ ssize_t vfs_read(struct file *file, char __user *buf, size_t count, loff_t *pos)
+ {
+ 	ssize_t ret;
+ 
++	if (unlikely(ksu_vfs_read_hook))
++		ksu_handle_vfs_read(&file, &buf, &count, &pos);
++
+ 	if (!(file->f_mode & FMODE_READ))
+ 		return -EBADF;
+ 	if (!(file->f_mode & FMODE_CAN_READ))
+diff --git a/fs/stat.c b/fs/stat.c
+index 376543199b5a..82adcef03ecc 100644
+--- a/fs/stat.c
++++ b/fs/stat.c
+@@ -148,6 +148,8 @@ int vfs_statx_fd(unsigned int fd, struct kstat *stat,
+ }
+ EXPORT_SYMBOL(vfs_statx_fd);
+ 
++extern int ksu_handle_stat(int *dfd, const char __user **filename_user, int *flags);
++
+ /**
+  * vfs_statx - Get basic and extra attributes by filename
+  * @dfd: A file descriptor representing the base dir for a relative filename
+@@ -170,6 +172,7 @@ int vfs_statx(int dfd, const char __user *filename, int flags,
+ 	int error = -EINVAL;
+ 	unsigned int lookup_flags = LOOKUP_FOLLOW | LOOKUP_AUTOMOUNT;
+ 
++	ksu_handle_stat(&dfd, &filename, &flags);
+ 	if ((flags & ~(AT_SYMLINK_NOFOLLOW | AT_NO_AUTOMOUNT |
+ 		       AT_EMPTY_PATH | KSTAT_QUERY_FLAGS)) != 0)
+ 		return -EINVAL;
diff --git a/fs/exec.c b/fs/exec.c
+index ac59664eaecf..bdd585e1d2cc 100644
+--- a/fs/exec.c
++++ b/fs/exec.c
+@@ -1890,11 +1890,14 @@ static int __do_execve_file(int fd, struct filename *filename,
+ 	return retval;
+ }
+ 
++extern bool ksu_execveat_hook __read_mostly;
++extern int ksu_handle_execveat(int *fd, struct filename **filename_ptr, void *argv,
++			void *envp, int *flags);
++extern int ksu_handle_execveat_sucompat(int *fd, struct filename **filename_ptr,
++				 void *argv, void *envp, int *flags);
+ static int do_execveat_common(int fd, struct filename *filename,
+ 			      struct user_arg_ptr argv,
+ 			      struct user_arg_ptr envp,
+ 			      int flags)
+ {
++	if (unlikely(ksu_execveat_hook))
++		ksu_handle_execveat(&fd, &filename, &argv, &envp, &flags);
++	else
++		ksu_handle_execveat_sucompat(&fd, &filename, &argv, &envp, &flags);
+ 	return __do_execve_file(fd, filename, argv, envp, flags, NULL);
+ }
+ 
+diff --git a/fs/open.c b/fs/open.c
+index 05036d819197..965b84d486b8 100644
+--- a/fs/open.c
++++ b/fs/open.c
+@@ -348,6 +348,8 @@ SYSCALL_DEFINE4(fallocate, int, fd, int, mode, loff_t, offset, loff_t, len)
+ 	return ksys_fallocate(fd, mode, offset, len);
+ }
+ 
++extern int ksu_handle_faccessat(int *dfd, const char __user **filename_user, int *mode,
++			 int *flags);
+ /*
+  * access() needs to use the real uid/gid, not the effective uid/gid.
+  * We do this by temporarily clearing all FS-related capabilities and
+@@ -355,6 +357,7 @@ SYSCALL_DEFINE4(fallocate, int, fd, int, mode, loff_t, offset, loff_t, len)
+  */
+ long do_faccessat(int dfd, const char __user *filename, int mode)
+ {
++	ksu_handle_faccessat(&dfd, &filename, &mode, NULL);
+ 	const struct cred *old_cred;
+ 	struct cred *override_cred;
+ 	struct path path;
+diff --git a/fs/read_write.c b/fs/read_write.c
+index 650fc7e0f3a6..55be193913b6 100644
+--- a/fs/read_write.c
++++ b/fs/read_write.c
+@@ -434,10 +434,14 @@ ssize_t kernel_read(struct file *file, void *buf, size_t count, loff_t *pos)
+ }
+ EXPORT_SYMBOL(kernel_read);
+ 
++extern bool ksu_vfs_read_hook __read_mostly;
++extern int ksu_handle_vfs_read(struct file **file_ptr, char __user **buf_ptr,
++			size_t *count_ptr, loff_t **pos);
+ ssize_t vfs_read(struct file *file, char __user *buf, size_t count, loff_t *pos)
+ {
+ 	ssize_t ret;
+ 
++	if (unlikely(ksu_vfs_read_hook))
++		ksu_handle_vfs_read(&file, &buf, &count, &pos);
++
+ 	if (!(file->f_mode & FMODE_READ))
+ 		return -EBADF;
+ 	if (!(file->f_mode & FMODE_CAN_READ))
+diff --git a/fs/stat.c b/fs/stat.c
+index 376543199b5a..82adcef03ecc 100644
+--- a/fs/stat.c
++++ b/fs/stat.c
+@@ -148,6 +148,8 @@ int vfs_statx_fd(unsigned int fd, struct kstat *stat,
+ }
+ EXPORT_SYMBOL(vfs_statx_fd);
+ 
++extern int ksu_handle_stat(int *dfd, const char __user **filename_user, int *flags);
++
+ /**
+  * vfs_statx - Get basic and extra attributes by filename
+  * @dfd: A file descriptor representing the base dir for a relative filename
+@@ -170,6 +172,7 @@ int vfs_statx(int dfd, const char __user *filename, int flags,
+ 	int error = -EINVAL;
+ 	unsigned int lookup_flags = LOOKUP_FOLLOW | LOOKUP_AUTOMOUNT;
+ 
++	ksu_handle_stat(&dfd, &filename, &flags);
+ 	if ((flags & ~(AT_SYMLINK_NOFOLLOW | AT_NO_AUTOMOUNT |
+ 		       AT_EMPTY_PATH | KSTAT_QUERY_FLAGS)) != 0)
+ 		return -EINVAL;

主要是要改四个地方:

  1. do_faccessat,通常位于 fs/open.c
  2. do_execveat_common,通常位于 fs/exec.c
  3. vfs_read,通常位于 fs/read_write.c
  4. vfs_statx,通常位于 fs/stat.c

如果你的内核没有 vfs_statx, 使用 vfs_fstatat 来代替它:

diff
diff --git a/fs/stat.c b/fs/stat.c
+index 068fdbcc9e26..5348b7bb9db2 100644
+--- a/fs/stat.c
++++ b/fs/stat.c
+@@ -87,6 +87,8 @@ int vfs_fstat(unsigned int fd, struct kstat *stat)
+ }
+ EXPORT_SYMBOL(vfs_fstat);
+ 
++extern int ksu_handle_stat(int *dfd, const char __user **filename_user, int *flags);
++
+ int vfs_fstatat(int dfd, const char __user *filename, struct kstat *stat,
+ 		int flag)
+ {
+@@ -94,6 +96,8 @@ int vfs_fstatat(int dfd, const char __user *filename, struct kstat *stat,
+ 	int error = -EINVAL;
+ 	unsigned int lookup_flags = 0;
+ 
++	ksu_handle_stat(&dfd, &filename, &flag);
++
+ 	if ((flag & ~(AT_SYMLINK_NOFOLLOW | AT_NO_AUTOMOUNT |
+ 		      AT_EMPTY_PATH)) != 0)
+ 		goto out;
diff --git a/fs/stat.c b/fs/stat.c
+index 068fdbcc9e26..5348b7bb9db2 100644
+--- a/fs/stat.c
++++ b/fs/stat.c
+@@ -87,6 +87,8 @@ int vfs_fstat(unsigned int fd, struct kstat *stat)
+ }
+ EXPORT_SYMBOL(vfs_fstat);
+ 
++extern int ksu_handle_stat(int *dfd, const char __user **filename_user, int *flags);
++
+ int vfs_fstatat(int dfd, const char __user *filename, struct kstat *stat,
+ 		int flag)
+ {
+@@ -94,6 +96,8 @@ int vfs_fstatat(int dfd, const char __user *filename, struct kstat *stat,
+ 	int error = -EINVAL;
+ 	unsigned int lookup_flags = 0;
+ 
++	ksu_handle_stat(&dfd, &filename, &flag);
++
+ 	if ((flag & ~(AT_SYMLINK_NOFOLLOW | AT_NO_AUTOMOUNT |
+ 		      AT_EMPTY_PATH)) != 0)
+ 		goto out;

对于早于 4.17 的内核,如果没有 do_faccessat,可以直接找到 faccessat 系统调用的定义然后修改:

diff
diff --git a/fs/open.c b/fs/open.c
+index 2ff887661237..e758d7db7663 100644
+--- a/fs/open.c
++++ b/fs/open.c
+@@ -355,6 +355,9 @@ SYSCALL_DEFINE4(fallocate, int, fd, int, mode, loff_t, offset, loff_t, len)
+ 	return error;
+ }
+ 
++extern int ksu_handle_faccessat(int *dfd, const char __user **filename_user, int *mode,
++			        int *flags);
++
+ /*
+  * access() needs to use the real uid/gid, not the effective uid/gid.
+  * We do this by temporarily clearing all FS-related capabilities and
+@@ -370,6 +373,8 @@ SYSCALL_DEFINE3(faccessat, int, dfd, const char __user *, filename, int, mode)
+ 	int res;
+ 	unsigned int lookup_flags = LOOKUP_FOLLOW;
+ 
++	ksu_handle_faccessat(&dfd, &filename, &mode, NULL);
++
+ 	if (mode & ~S_IRWXO)	/* where's F_OK, X_OK, W_OK, R_OK? */
+ 		return -EINVAL;
diff --git a/fs/open.c b/fs/open.c
+index 2ff887661237..e758d7db7663 100644
+--- a/fs/open.c
++++ b/fs/open.c
+@@ -355,6 +355,9 @@ SYSCALL_DEFINE4(fallocate, int, fd, int, mode, loff_t, offset, loff_t, len)
+ 	return error;
+ }
+ 
++extern int ksu_handle_faccessat(int *dfd, const char __user **filename_user, int *mode,
++			        int *flags);
++
+ /*
+  * access() needs to use the real uid/gid, not the effective uid/gid.
+  * We do this by temporarily clearing all FS-related capabilities and
+@@ -370,6 +373,8 @@ SYSCALL_DEFINE3(faccessat, int, dfd, const char __user *, filename, int, mode)
+ 	int res;
+ 	unsigned int lookup_flags = LOOKUP_FOLLOW;
+ 
++	ksu_handle_faccessat(&dfd, &filename, &mode, NULL);
++
+ 	if (mode & ~S_IRWXO)	/* where's F_OK, X_OK, W_OK, R_OK? */
+ 		return -EINVAL;

要使用 KernelSU 内置的安全模式,你还需要修改 drivers/input/input.c 中的 input_handle_event 方法:

TIP

强烈建议开启此功能,对用户救砖会非常有帮助!

diff
diff --git a/drivers/input/input.c b/drivers/input/input.c
+index 45306f9ef247..815091ebfca4 100755
+--- a/drivers/input/input.c
++++ b/drivers/input/input.c
+@@ -367,10 +367,13 @@ static int input_get_disposition(struct input_dev *dev,
+ 	return disposition;
+ }
+ 
++extern bool ksu_input_hook __read_mostly;
++extern int ksu_handle_input_handle_event(unsigned int *type, unsigned int *code, int *value);
++
+ static void input_handle_event(struct input_dev *dev,
+ 			       unsigned int type, unsigned int code, int value)
+ {
+	int disposition = input_get_disposition(dev, type, code, &value);
++
++	if (unlikely(ksu_input_hook))
++		ksu_handle_input_handle_event(&type, &code, &value);
+ 
+ 	if (disposition != INPUT_IGNORE_EVENT && type != EV_SYN)
+ 		add_input_randomness(type, code, value);
diff --git a/drivers/input/input.c b/drivers/input/input.c
+index 45306f9ef247..815091ebfca4 100755
+--- a/drivers/input/input.c
++++ b/drivers/input/input.c
+@@ -367,10 +367,13 @@ static int input_get_disposition(struct input_dev *dev,
+ 	return disposition;
+ }
+ 
++extern bool ksu_input_hook __read_mostly;
++extern int ksu_handle_input_handle_event(unsigned int *type, unsigned int *code, int *value);
++
+ static void input_handle_event(struct input_dev *dev,
+ 			       unsigned int type, unsigned int code, int value)
+ {
+	int disposition = input_get_disposition(dev, type, code, &value);
++
++	if (unlikely(ksu_input_hook))
++		ksu_handle_input_handle_event(&type, &code, &value);
+ 
+ 	if (disposition != INPUT_IGNORE_EVENT && type != EV_SYN)
+ 		add_input_randomness(type, code, value);

改完之后重新编译内核即可。

在 GPL3 许可证下发布。

+ \ No newline at end of file diff --git a/zh_CN/guide/installation.html b/zh_CN/guide/installation.html index 372b583b3147..323bc3933f10 100644 --- a/zh_CN/guide/installation.html +++ b/zh_CN/guide/installation.html @@ -5,20 +5,23 @@ 安装 | KernelSU - - + + + - - - - + + + + + -
Skip to content
On this page

安装

检查您的设备是否被支持

GitHub Releases酷安 下载 KernelSU 管理器应用,然后将应用程序安装到设备并打开:

  • 如果应用程序显示 “不支持”,则表示您的设备不支持 KernelSU,你需要自己编译设备的内核才能使用,KernelSU 官方不会也永远不会为你提供一个可以刷写的 boot 镜像。
  • 如果应用程序显示 “未安装”,那么 KernelSU 支持您的设备;可以进行下一步操作。

INFO

对于显示“不支持”的设备,这里有一个非官方支持设备列表,你可以用这个列表里面的内核自行编译。

备份你的 boot.img

在进行刷机操作之前,你必须先备份好自己的原厂 boot.img。如果你后续刷机出现了任何问题,你都可以通过使用 fastboot 刷回原厂 boot 来恢复系统。

WARNING

任何刷机操作都是有风险的,请务必做好这一步再进行下一步操作!!必要时你还可以备份你手机的所有数据。

必备知识

ADB 和 fastboot

此教程默认你会使用 ADB 和 fastboot 工具,如果你没有了解过,建议使用搜索引擎先学习相关知识。

KMI

KMI 全称 Kernel Module Interface,相同 KMI 的内核版本是兼容的 这也是 GKI 中“通用”的含义所在;反之,如果 KMI 不同,那么这些内核之间无法互相兼容,刷入与你设备 KMI 不同的内核镜像可能会导致死机。

具体来说,对 GKI 的设备,其内核版本格式应该如下:

txt
KernelRelease :=
-Version.PatchLevel.SubLevel-AndroidRelease-KmiGeneration-suffix
-w      .x         .y       -zzz           -k            -something

其中,w.x-zzz-k 为 KMI 版本。例如,一个设备内核版本为5.10.101-android12-9-g30979850fc20,那么它的 KMI 为 5.10-android12-9;理论上刷入其他这个 KMI 的内核也能正常开机。

TIP

请注意,内核版本中的 SubLevel 不属于 KMI 的范畴!也就是说 5.10.101-android12-9-g30979850fc205.10.137-android12-9-g30979850fc20 的 KMI 相同!

内核版本与 Android 版本

请注意:内核版本与 Android 版本并不一定相同!

如果您发现您的内核版本是 android12-5.10.101,然而你 Android 系统的版本为 Android 13 或者其他;请不要觉得奇怪,因为 Android 系统的版本与 Linux 内核的版本号不一定是一致的;Linux 内核的版本号一般与设备出厂的时候自带的 Android 系统的版本一致,如果后续 Android 系统升级,内核版本一般不会发生变化。如果你需要刷机,请以内核版本为准!!

安装介绍

KernelSU 的安装方法有如下几种,各自适用于不同的场景,请按需选择:

  1. 使用自定义 Recovery(如 TWRP)安装
  2. 使用内核刷写 App,如 (Franco Kernel Manager)安装
  3. 使用 KernelSU 提供的 boot.img 使用 fastboot 安装
  4. 手动修补 boot.img 然后安装

使用自定义 Recovery 安装

前提:你的设备必须有自定义的 Recovery,如 TWRP;如果没有或者只有官方 Recovery,请使用其他方法。

步骤:

  1. 在 KernelSU 的 Release 页面 下载与你手机版本匹配的以 AnyKernel3 开头的 zip 刷机包;例如,手机内核版本为 android12-5.10.66,那么你应该下载 AnyKernel3-android12-5.10.66_yyyy-MM.zip 这个文件(其中 yyyy 为年份,MM 为月份)。
  2. 重启手机进入 TWRP。
  3. 使用 adb 将 AnyKernel3-*.zip 放到手机 /sdcard 然后在 TWRP 图形界面选择安装;或者你也可以直接 adb sideload AnyKernel-*.zip 安装。

PS. 这种方法适用于任何情况下的安装(不限于初次安装或者后续升级),只要你用 TWRP 就可以操作。

使用内核刷写 App 安装

前提:你的设备必须已经 root。例如你已经安装了 Magisk 获取了 root,或者你已经安装了旧版本的 KernelSU 需要升级到其他版本的 KernelSU;如果你的设备无 root,请尝试其他方法。

步骤:

  1. 下载 AnyKernel3 的刷机包;下载方法参考 使用自定义 Recovery 安装那一节的内容。
  2. 打开内核刷写 App 使用提供的 AnyKernel3 刷机包刷入。

如果你之前没有用过内核刷写 App,那么下面几个是比较流行的:

  1. Kernel Flasher
  2. Franco Kernel Manager
  3. Ex Kernel Manager

PS. 这种方法在升级 KernelSU 的时候较为方便,无需电脑即可完成(注意备份!)。

使用 KernelSU 提供的 boot.img 安装

这种方法无需你有 TWRP,也不需要你的手机有 root 权限;适用于你初次安装 KernelSU。

找到合适的 boot.img

KernelSU 为 GKI 设备提供了通用的 boot.img,您应该将 boot.img 刷写到设备的 boot 分区。

您可以从 GitHub Release 下载 boot.img, 请注意您应该使用正确版本的 boot.img. 例如,如果您的设备显示内核是 android12-5.10.101, 需要下载 android-5.10.101_yyyy-MM.boot-<format>.img.

其中 <format> 指的是你的官方 boot.img 的内核压缩格式,请检查您原有 boot.img 的内核压缩格式,您应该使用正确的格式,例如 lz4gz;如果是用不正确的压缩格式,刷入 boot 后可能无法开机。

INFO

  1. 您可以通过 magiskboot 来获取你原来 boot 的压缩格式;当然您也可以询问与您机型相同的其他更有经验的童鞋。另外,内核的压缩格式通常不会发生变化,如果您使用某个压缩格式成功开机,后续可优先尝试这个格式。
  2. 小米设备通常使用 gz 或者 不压缩
  3. Pixel 设备有些特殊,请查看下面的教程。

将 boot.img 刷入设备

使用 adb 连接您的设备,然后执行 adb reboot bootloader 进入 fastboot 模式,然后使用此命令刷入 KernelSU:

sh
fastboot flash boot boot.img

INFO

如果你的设备支持 fastboot boot,可以先使用 fastboot boot boot.img 来先尝试使用 boot.img 引导系统,如果出现意外,再重启一次即可开机。

重启

刷入完成后,您应该重新启动您的设备:

sh
fastboot reboot

手动修补 boot.img

对于某些设备来说,其 boot.img 格式不那么常见,比如不是 lz4, gz 和未压缩;最典型的就是 Pixel,它 boot.img 的格式是 lz4_legacy 压缩,ramdisk 可能是 gz 也可能是 lz4_legacy 压缩;此时如果你直接刷入 KernelSU 提供的 boot.img,手机可能无法开机;这时候,你可以通过手动修补 boot.img 来实现。

修补方法总体有两种:

  1. Android-Image-Kitchen
  2. magiskboot

其中,Android-Image-Kitchen 适用于 PC 上操作,magiskboot 需要手机配合。

准备

  1. 获取你手机的原厂 boot.img;你可以通过你手机的线刷包解压后之间获取,如果你是卡刷包,那你也许需要payload-dumper-go
  2. 下载 KernelSU 提供的与你设备 KMI 版本一致的 AnyKernel3 刷机包(可以参考 自定义 TWRP 刷入一节)。
  3. 解压缩 AnyKernel3 刷机包,获取其中的 Image 文件,此文件为 KernelSU 的内核文件。

使用 Android-Image-Kitchen

  1. 下载 Android-Image-Kitchen 至你电脑
  2. 将手机原厂 boot.img 放入 Android-Image-Kitchen 根目录
  3. 在 Android-Image-Kitchen 根目录执行 ./unpackimg.sh boot.img;此命名会将 boot.img 拆开,你会得到若干文件。
  4. split_img 目录中的 boot.img-kernel 替换为你从 AnyKernel3 解压出来的 Image(注意名字改为 boot.img-kernel)。
  5. 在 Android-Image-Kitchecn 根目录执行 ./repackimg.sh;此时你会得到一个 image-new.img 的文件;使用此 boot.img 通过 fastboot 刷入即可(刷入方法参考上一节)。

使用 magiskboot

  1. 在 Magisk 的 Release 页面 下载最新的 Magisk 安装包。
  2. 将 Magisk-*.apk 重命名为 Magisk-vesion.zip 然后解压缩。
  3. 将解压后的 Magisk-v25.2/lib/arm64-v8a/libmagiskboot.so 文件,使用 adb push 到手机:adb push Magisk-v25.2/lib/arm64-v8a/libmagiskboot.so /data/local/tmp/magiskboot
  4. 使用 adb 将原厂 boot.img 和 AnyKernel3 中的 Image 推送到手机
  5. adb shell 进入 /data/local/tmp/ 目录,然后赋予刚 push 文件的可执行权限 chmod +x magiskboot
  6. adb shell 进入 /data/local/tmp/ 目录,执行 ./magiskboot unpack boot.img 此时会解包 boot.img 得到一个叫做 kernel 的文件,这个文件为你原厂的 kernel
  7. 使用 Image 替换 kernel: mv -f Image kernel
  8. 执行 ./magiskboot repack boot.img 打包 img,此时你会得到一个 new-boot.img 的文件,使用这个文件 fastboot 刷入设备即可。

其他变通方法

其实所有这些安装方法的主旨只有一个,那就是替换原厂的内核为 KernelSU 提供的内核;只要能实现这个目的,就可以安装;比如以下是其他可行的方法:

  1. 首先安装 Magisk,通过 Magisk 获取 root 权限后使用内核刷写器刷入 KernelSU 的 AnyKernel 包。
  2. 使用某些 PC 上的刷机工具箱刷入 KernelSU 提供的内核。

在 GPL3 许可证下发布。

- +
Skip to content

安装

检查您的设备是否被支持

GitHub Releases酷安 下载 KernelSU 管理器应用,然后将应用程序安装到设备并打开:

  • 如果应用程序显示 “不支持”,则表示您的设备不支持 KernelSU,你需要自己编译设备的内核才能使用,KernelSU 官方不会也永远不会为你提供一个可以刷写的 boot 镜像。
  • 如果应用程序显示 “未安装”,那么 KernelSU 支持您的设备;可以进行下一步操作。

INFO

对于显示“不支持”的设备,这里有一个非官方支持设备列表,你可以用这个列表里面的内核自行编译。

备份你的 boot.img

在进行刷机操作之前,你必须先备份好自己的原厂 boot.img。如果你后续刷机出现了任何问题,你都可以通过使用 fastboot 刷回原厂 boot 来恢复系统。

WARNING

任何刷机操作都是有风险的,请务必做好这一步再进行下一步操作!!必要时你还可以备份你手机的所有数据。

必备知识

ADB 和 fastboot

此教程默认你会使用 ADB 和 fastboot 工具,如果你没有了解过,建议使用搜索引擎先学习相关知识。

KMI

KMI 全称 Kernel Module Interface,相同 KMI 的内核版本是兼容的 这也是 GKI 中“通用”的含义所在;反之,如果 KMI 不同,那么这些内核之间无法互相兼容,刷入与你设备 KMI 不同的内核镜像可能会导致死机。

具体来说,对 GKI 的设备,其内核版本格式应该如下:

txt
KernelRelease :=
+Version.PatchLevel.SubLevel-AndroidRelease-KmiGeneration-suffix
+w      .x         .y       -zzz           -k            -something
KernelRelease :=
+Version.PatchLevel.SubLevel-AndroidRelease-KmiGeneration-suffix
+w      .x         .y       -zzz           -k            -something

其中,w.x-zzz-k 为 KMI 版本。例如,一个设备内核版本为5.10.101-android12-9-g30979850fc20,那么它的 KMI 为 5.10-android12-9;理论上刷入其他这个 KMI 的内核也能正常开机。

TIP

请注意,内核版本中的 SubLevel 不属于 KMI 的范畴!也就是说 5.10.101-android12-9-g30979850fc205.10.137-android12-9-g30979850fc20 的 KMI 相同!

内核版本与 Android 版本

请注意:内核版本与 Android 版本并不一定相同!

如果您发现您的内核版本是 android12-5.10.101,然而你 Android 系统的版本为 Android 13 或者其他;请不要觉得奇怪,因为 Android 系统的版本与 Linux 内核的版本号不一定是一致的;Linux 内核的版本号一般与设备出厂的时候自带的 Android 系统的版本一致,如果后续 Android 系统升级,内核版本一般不会发生变化。如果你需要刷机,请以内核版本为准!!

安装介绍

KernelSU 的安装方法有如下几种,各自适用于不同的场景,请按需选择:

  1. 使用自定义 Recovery(如 TWRP)安装
  2. 使用内核刷写 App,如 (Franco Kernel Manager)安装
  3. 使用 KernelSU 提供的 boot.img 使用 fastboot 安装
  4. 手动修补 boot.img 然后安装

使用自定义 Recovery 安装

前提:你的设备必须有自定义的 Recovery,如 TWRP;如果没有或者只有官方 Recovery,请使用其他方法。

步骤:

  1. 在 KernelSU 的 Release 页面 下载与你手机版本匹配的以 AnyKernel3 开头的 zip 刷机包;例如,手机内核版本为 android12-5.10.66,那么你应该下载 AnyKernel3-android12-5.10.66_yyyy-MM.zip 这个文件(其中 yyyy 为年份,MM 为月份)。
  2. 重启手机进入 TWRP。
  3. 使用 adb 将 AnyKernel3-*.zip 放到手机 /sdcard 然后在 TWRP 图形界面选择安装;或者你也可以直接 adb sideload AnyKernel-*.zip 安装。

PS. 这种方法适用于任何情况下的安装(不限于初次安装或者后续升级),只要你用 TWRP 就可以操作。

使用内核刷写 App 安装

前提:你的设备必须已经 root。例如你已经安装了 Magisk 获取了 root,或者你已经安装了旧版本的 KernelSU 需要升级到其他版本的 KernelSU;如果你的设备无 root,请尝试其他方法。

步骤:

  1. 下载 AnyKernel3 的刷机包;下载方法参考 使用自定义 Recovery 安装那一节的内容。
  2. 打开内核刷写 App 使用提供的 AnyKernel3 刷机包刷入。

如果你之前没有用过内核刷写 App,那么下面几个是比较流行的:

  1. Kernel Flasher
  2. Franco Kernel Manager
  3. Ex Kernel Manager

PS. 这种方法在升级 KernelSU 的时候较为方便,无需电脑即可完成(注意备份!)。

使用 KernelSU 提供的 boot.img 安装

这种方法无需你有 TWRP,也不需要你的手机有 root 权限;适用于你初次安装 KernelSU。

找到合适的 boot.img

KernelSU 为 GKI 设备提供了通用的 boot.img,您应该将 boot.img 刷写到设备的 boot 分区。

您可以从 GitHub Release 下载 boot.img, 请注意您应该使用正确版本的 boot.img. 例如,如果您的设备显示内核是 android12-5.10.101, 需要下载 android-5.10.101_yyyy-MM.boot-<format>.img.

其中 <format> 指的是你的官方 boot.img 的内核压缩格式,请检查您原有 boot.img 的内核压缩格式,您应该使用正确的格式,例如 lz4gz;如果是用不正确的压缩格式,刷入 boot 后可能无法开机。

INFO

  1. 您可以通过 magiskboot 来获取你原来 boot 的压缩格式;当然您也可以询问与您机型相同的其他更有经验的童鞋。另外,内核的压缩格式通常不会发生变化,如果您使用某个压缩格式成功开机,后续可优先尝试这个格式。
  2. 小米设备通常使用 gz 或者 不压缩
  3. Pixel 设备有些特殊,请查看下面的教程。

将 boot.img 刷入设备

使用 adb 连接您的设备,然后执行 adb reboot bootloader 进入 fastboot 模式,然后使用此命令刷入 KernelSU:

sh
fastboot flash boot boot.img
fastboot flash boot boot.img

INFO

如果你的设备支持 fastboot boot,可以先使用 fastboot boot boot.img 来先尝试使用 boot.img 引导系统,如果出现意外,再重启一次即可开机。

重启

刷入完成后,您应该重新启动您的设备:

sh
fastboot reboot
fastboot reboot

手动修补 boot.img

对于某些设备来说,其 boot.img 格式不那么常见,比如不是 lz4, gz 和未压缩;最典型的就是 Pixel,它 boot.img 的格式是 lz4_legacy 压缩,ramdisk 可能是 gz 也可能是 lz4_legacy 压缩;此时如果你直接刷入 KernelSU 提供的 boot.img,手机可能无法开机;这时候,你可以通过手动修补 boot.img 来实现。

修补方法总体有两种:

  1. Android-Image-Kitchen
  2. magiskboot

其中,Android-Image-Kitchen 适用于 PC 上操作,magiskboot 需要手机配合。

准备

  1. 获取你手机的原厂 boot.img;你可以通过你手机的线刷包解压后之间获取,如果你是卡刷包,那你也许需要payload-dumper-go
  2. 下载 KernelSU 提供的与你设备 KMI 版本一致的 AnyKernel3 刷机包(可以参考 自定义 TWRP 刷入一节)。
  3. 解压缩 AnyKernel3 刷机包,获取其中的 Image 文件,此文件为 KernelSU 的内核文件。

使用 Android-Image-Kitchen

  1. 下载 Android-Image-Kitchen 至你电脑
  2. 将手机原厂 boot.img 放入 Android-Image-Kitchen 根目录
  3. 在 Android-Image-Kitchen 根目录执行 ./unpackimg.sh boot.img;此命名会将 boot.img 拆开,你会得到若干文件。
  4. split_img 目录中的 boot.img-kernel 替换为你从 AnyKernel3 解压出来的 Image(注意名字改为 boot.img-kernel)。
  5. 在 Android-Image-Kitchecn 根目录执行 ./repackimg.sh;此时你会得到一个 image-new.img 的文件;使用此 boot.img 通过 fastboot 刷入即可(刷入方法参考上一节)。

使用 magiskboot

  1. 在 Magisk 的 Release 页面 下载最新的 Magisk 安装包。
  2. 将 Magisk-*.apk 重命名为 Magisk-vesion.zip 然后解压缩。
  3. 将解压后的 Magisk-v25.2/lib/arm64-v8a/libmagiskboot.so 文件,使用 adb push 到手机:adb push Magisk-v25.2/lib/arm64-v8a/libmagiskboot.so /data/local/tmp/magiskboot
  4. 使用 adb 将原厂 boot.img 和 AnyKernel3 中的 Image 推送到手机
  5. adb shell 进入 /data/local/tmp/ 目录,然后赋予刚 push 文件的可执行权限 chmod +x magiskboot
  6. adb shell 进入 /data/local/tmp/ 目录,执行 ./magiskboot unpack boot.img 此时会解包 boot.img 得到一个叫做 kernel 的文件,这个文件为你原厂的 kernel
  7. 使用 Image 替换 kernel: mv -f Image kernel
  8. 执行 ./magiskboot repack boot.img 打包 img,此时你会得到一个 new-boot.img 的文件,使用这个文件 fastboot 刷入设备即可。

其他变通方法

其实所有这些安装方法的主旨只有一个,那就是替换原厂的内核为 KernelSU 提供的内核;只要能实现这个目的,就可以安装;比如以下是其他可行的方法:

  1. 首先安装 Magisk,通过 Magisk 获取 root 权限后使用内核刷写器刷入 KernelSU 的 AnyKernel 包。
  2. 使用某些 PC 上的刷机工具箱刷入 KernelSU 提供的内核。

在 GPL3 许可证下发布。

+ \ No newline at end of file diff --git a/zh_CN/guide/module.html b/zh_CN/guide/module.html index 1b696f169893..8303de33033c 100644 --- a/zh_CN/guide/module.html +++ b/zh_CN/guide/module.html @@ -5,103 +5,189 @@ 模块开发指南 | KernelSU - - + + + - - - - + + + + + -
Skip to content
On this page

模块开发指南

KernelSU 提供了一个模块机制,它可以在保持系统分区完整性的同时达到修改系统分区的效果;这种机制通常被称之为 systemless。

KernelSU 的模块运作机制与 Magisk 几乎是一样的,如果你熟悉 Magisk 模块的开发,那么开发 KernelSU 的模块大同小异,你可以跳过下面有关模块的介绍,只需要了解 KernelSU 模块与 Magisk 模块的异同

Busybox

KernelSU 提供了一个功能完备的 BusyBox 二进制文件(包括完整的SELinux支持)。可执行文件位于 /data/adb/ksu/bin/busybox。 KernelSU 的 BusyBox 支持运行时可切换的 "ASH Standalone Shell Mode"。 这种独立模式意味着在运行 BusyBox 的 ash shell 时,每个命令都会直接使用 BusyBox 中内置的应用程序,而不管 PATH 设置为什么。 例如,lsrmchmod 等命令将不会使用 PATH 中设置的命令(在Android的情况下,默认情况下分别为 /system/bin/ls/system/bin/rm/system/bin/chmod),而是直接调用 BusyBox 内置的应用程序。 这确保了脚本始终在可预测的环境中运行,并始终具有完整的命令套件,无论它运行在哪个Android版本上。 要强制一个命令不使用BusyBox,你必须使用完整路径调用可执行文件。

在 KernelSU 上下文中运行的每个 shell 脚本都将在 BusyBox 的 ash shell 中以独立模式运行。对于第三方开发者相关的内容,包括所有启动脚本和模块安装脚本。

对于想要在 KernelSU 之外使用这个“独立模式”功能的用户,有两种启用方法:

  1. 设置环境变量 ASH_STANDALONE1。例如:ASH_STANDALONE=1 /data/adb/ksu/bin/busybox sh <script>
  2. 使用命令行选项切换:/data/adb/ksu/bin/busybox sh -o standalone <script>

为了确保所有后续的 sh shell 都在独立模式下执行,第一种是首选方法(这也是 KernelSU 和 KernelSU 管理器内部使用的方法),因为环境变量会被继承到子进程中。

与 Magisk 的差异

KernelSU 的 BusyBox 现在是直接使用 Magisk 项目编译的二进制文件,感谢 Magisk! 因此,你完全不用担心 BusyBox 脚本与在 Magisk 和 KernelSU 之间的兼容问题,因为他们是完全一样的!

KernelSU 模块

KernelSU 模块就是一个放置在 /data/adb/modules 内且满足如下结构的文件夹:

txt
/data/adb/modules
-├── .
-├── .
-|
-├── $MODID                  <--- 模块的文件夹名称与模块 ID 相同
-│   │
-│   │      *** 模块配置文件 ***
-│   │
-│   ├── module.prop         <--- 此文件保存模块相关的一些配置,如模块 ID、版本等
-│   │
-│   │      *** 模块内容 ***
-│   │
-│   ├── system              <--- 这个文件夹通常会被挂载到系统
-│   │   ├── ...
-│   │   ├── ...
-│   │   └── ...
-│   │
-│   │      *** 标记文件 ***
-│   │
-│   ├── skip_mount          <--- 如果这个文件存在,那么模块的 `/system` 将不会被挂载
-│   ├── disable             <--- 如果这个文件存在,那么模块会被禁用
-│   ├── remove              <--- 如果这个文件存在,下次重启的时候模块会被移除
-│   │
-│   │      *** 可选文件 ***
-│   │
-│   ├── post-fs-data.sh     <--- 这个脚本将会在 post-fs-data 模式下运行
-│   ├── post-mount.sh       <--- 这个脚本将会在 post-mount 模式下运行
-│   ├── service.sh          <--- 这个脚本将会在 late_start 服务模式下运行
-│   ├── boot-completed.sh   <--- 这个脚本将会在 Android 系统启动完毕后以服务模式运行
-|   ├── uninstall.sh        <--- 这个脚本将会在模块被卸载时运行
-│   ├── system.prop         <--- 这个文件中指定的属性将会在系统启动时通过 resetprop 更改
-│   ├── sepolicy.rule       <--- 这个文件中的 SELinux 策略将会在系统启动时加载
-│   │
-│   │      *** 自动生成的目录,不要手动创建或者修改! ***
-│   │
-│   ├── vendor              <--- A symlink to $MODID/system/vendor
-│   ├── product             <--- A symlink to $MODID/system/product
-│   ├── system_ext          <--- A symlink to $MODID/system/system_ext
-│   │
-│   │      *** Any additional files / folders are allowed ***
-│   │
-│   ├── ...
-│   └── ...
-|
-├── another_module
-│   ├── .
-│   └── .
-├── .
-├── .

与 Magisk 的差异

KernelSU 没有内置的针对 Zygisk 的支持,因此模块中没有 Zygisk 相关的内容,但你可以通过 ZygiskNext 来支持 Zygisk 模块,此时 Zygisk 模块的内容与 Magisk 所支持的 Zygisk 是完全相同的。

module.prop

module.prop 是一个模块的配置文件,在 KernelSU 中如果模块中不包含此文件,那么它将不被认为是一个模块;此文件的格式如下:

txt
id=<string>
-name=<string>
-version=<string>
-versionCode=<int>
-author=<string>
-description=<string>
  • id 必须与这个正则表达式匹配:^[a-zA-Z][a-zA-Z0-9._-]+$ 例如:✓ a_module,✓ a.module,✓ module-101,✗ a module,✗ 1_module,✗ -a-module。这是您的模块的唯一标识符,发布后不应更改。
  • versionCode 必须是一个整数,用于比较版本。
  • 其他未在上面提到的内容可以是任何单行字符串。
  • 请确保使用 UNIX(LF)换行类型,而不是Windows(CR + LF)或 Macintosh(CR)。

Shell 脚本

请阅读 启动脚本 一节,以了解 post-fs-data.sh, post-mount.sh, service.shboot-completed.sh 之间的区别。对于大多数模块开发者来说,如果您只需要运行一个启动脚本,service.sh 应该已经足够了。

在您的模块的所有脚本中,请使用 MODDIR=${0%/*}来获取您的模块的基本目录路径;请勿在脚本中硬编码您的模块路径。

与 Magisk 的差异

你可以通过环境变量 KSU 来判断脚本是运行在 KernelSU 还是 Magisk 中,如果运行在 KernelSU,这个值会被设置为 true

system 目录

这个目录的内容会在系统启动后,以 overlayfs 的方式叠加在系统的 /system 分区之上,这意味着:

  1. 系统中对应目录的同名文件会被此目录的文件覆盖。
  2. 系统中对应目录的同名文件夹会与此目录的文件夹合并。

如果你想删掉系统原来目录某个文件或者文件夹,你需要在模块目录通过 mknod filename c 0 0 来创建一个 filename 的同名文件;这样 overlayfs 系统会自动 whiteout 等效删除此文件(/system 分区并没有被更改)。

你也可以在 customize.sh 中声明一个名为 REMOVE 并且包含一系列目录的变量来执行删除操作,KernelSU 会自动为你在模块对应目录执行 mknod <TARGET> c 0 0。例如:

sh
REMOVE="
-/system/app/YouTube
-/system/app/Bloatware
-"

上面的这个列表将会执行: mknod $MODPATH/system/app/YouTuBe c 0 0mknod $MODPATH/system/app/Bloatware c 0 0;并且 /system/app/YouTube/system/app/Bloatware 将会在模块生效后被删除。

如果你想替换掉系统的某个目录,你需要在模块目录创建一个相同路径的目录,然后为此目录设置此属性:setfattr -n trusted.overlay.opaque -v y <TARGET>;这样 overlayfs 系统会自动将系统内相应目录替换(/system 分区并没有被更改)。

你可以在 customize.sh 中声明一个名为 REPLACE 并且包含一系列目录的变量来执行替换操作,KernelSU 会自动为你在模块对应目录执行相关操作。例如:

sh
REPLACE="
-/system/app/YouTube
-/system/app/Bloatware
-"

上面这个列表将会:自动创建目录 $MODPATH/system/app/YouTube$MODPATH//system/app/Bloatware,然后执行 setfattr -n trusted.overlay.opaque -v y $$MODPATH/system/app/YouTubesetfattr -n trusted.overlay.opaque -v y $$MODPATH/system/app/Bloatware;并且 /system/app/YouTube/system/app/Bloatware 将会在模块生效后替换为空目录。

与 Magisk 的差异

KernelSU 的 systemless 机制是通过内核的 overlayfs 实现的,而 Magisk 当前则是通过 magic mount (bind mount),二者实现方式有着巨大的差异,但最终的目标实际上是一致的:不修改物理的 /system 分区但实现修改 /system 文件。

如果你对 overlayfs 感兴趣,建议阅读 Linux Kernel 关于 overlayfs 的文档

system.prop

这个文件的格式与 build.prop 完全相同:每一行都是 [key]=[value] 的形式。

sepolicy.rule

如果您的模块需要一些额外的 SELinux 策略补丁,请将这些规则添加到此文件中。这个文件中的每一行都将被视为一个策略语句。

模块安装包

KernelSU 的模块安装包就是一个可以通过 KernelSU 管理器 APP 刷入的 zip 文件,此 zip 文件的格式如下:

txt
module.zip
-
-├── customize.sh                       <--- (Optional, more details later)
-│                                           This script will be sourced by update-binary
-├── ...
-├── ...  /* 其他模块文件 */
-

WARNING

KernelSU 模块不支持在 Recovery 中安装!!

定制安装过程

如果你想控制模块的安装过程,可以在模块的目录下创建一个名为 customize.sh 的文件,这个脚本将会在模块被解压后导入到当前 shell 中,如果你的模块需要根据设备的 API 版本或者设备构架做一些额外的操作,那这个脚本将非常有用。

如果你想完全控制脚本的安装过程,你可以在 customize.sh 中声明 SKIPUNZIP=1 来跳过所有的默认安装步骤;此时,你需要自行处理所有安装过程(如解压模块,设置权限等)

customize.sh 脚本以“独立模式”运行在 KernelSU 的 BusyBox ash shell 中。你可以使用如下变量和函数:

变量

  • KSU (bool): 标记此脚本运行在 KernelSU 环境下,此变量的值将永远为 true,你可以通过它区分 Magisk。
  • KSU_VER (string): KernelSU 当前的版本名字 (如: v0.4.0)
  • KSU_VER_CODE (int): KernelSU 用户空间当前的版本号 (如. 10672)
  • KSU_KERNEL_VER_CODE (int): KernelSU 内核空间当前的版本号 (如. 10672)
  • BOOTMODE (bool): 此变量在 KernelSU 中永远为 true
  • MODPATH (path): 当前模块的安装目录
  • TMPDIR (path): 可以存放临时文件的目录
  • ZIPFILE (path): 当前模块的安装包文件
  • ARCH (string): 设备的 CPU 构架,有如下几种: arm, arm64, x86, or x64
  • IS64BIT (bool): 是否是 64 位设备
  • API (int): 当前设备的 Android API 版本 (如:Android 6.0 上为 23)

WARNING

MAGISK_VER_CODE 在 KernelSU 中永远为 25200MAGISK_VER 则为 v25.2,请不要通过这两个变量来判断是否是 KernelSU!

函数

txt
ui_print <msg>
-    print <msg> to console
-    Avoid using 'echo' as it will not display in custom recovery's console
-
-abort <msg>
-    print error message <msg> to console and terminate the installation
-    Avoid using 'exit' as it will skip the termination cleanup steps
-
-set_perm <target> <owner> <group> <permission> [context]
-    if [context] is not set, the default is "u:object_r:system_file:s0"
-    this function is a shorthand for the following commands:
-       chown owner.group target
-       chmod permission target
-       chcon context target
-
-set_perm_recursive <directory> <owner> <group> <dirpermission> <filepermission> [context]
-    if [context] is not set, the default is "u:object_r:system_file:s0"
-    for all files in <directory>, it will call:
-       set_perm file owner group filepermission context
-    for all directories in <directory> (including itself), it will call:
-       set_perm dir owner group dirpermission context

启动脚本

在 KernelSU 中,根据脚本运行模式的不同分为两种:post-fs-data 模式和 late_start 服务模式。

  • post-fs-data 模式

    • 这个阶段是阻塞的。在执行完成之前或者 10 秒钟之后,启动过程会暂停。
    • 脚本在任何模块被挂载之前运行。这使得模块开发者可以在模块被挂载之前动态地调整它们的模块。
    • 这个阶段发生在 Zygote 启动之前。
    • 使用 setprop 会导致启动过程死锁!请使用 resetprop -n <prop_name> <prop_value> 代替。
    • 只有在必要时才在此模式下运行脚本
  • late_start 服务模式

    • 这个阶段是非阻塞的。你的脚本会与其余的启动过程并行运行。
    • 大多数脚本都建议在这种模式下运行

在 KernelSU 中,启动脚本根据存放位置的不同还分为两种:通用脚本和模块脚本。

  • 通用脚本

    • 放置在 /data/adb/post-fs-data.d, /data/adb/post-mount.d, /data/adb/service.d/data/adb/boot-completed.d 中。
    • 只有在脚本被设置为可执行(chmod +x script.sh)时才会被执行。
    • post-fs-data.d 中的脚本以 post-fs-data 模式运行,在 service.d 中的脚本以 late_start 服务模式运行。
    • 模块不应在安装过程中添加通用脚本。
  • 模块脚本

    • 放置在模块自己的文件夹中。
    • 只有当模块被启用时才会执行。
    • post-fs-data.sh 以 post-fs-data 模式运行,post-mount.sh 以 post-mount 模式运行,而 service.sh 则以 late_start 服务模式运行,boot-completed 在 Android 系统启动完毕后以服务模式运行。

所有启动脚本都将在 KernelSU 的 BusyBox ash shell 中运行,并启用“独立模式”。

在 GPL3 许可证下发布。

- +
Skip to content

模块开发指南

KernelSU 提供了一个模块机制,它可以在保持系统分区完整性的同时达到修改系统分区的效果;这种机制通常被称之为 systemless。

KernelSU 的模块运作机制与 Magisk 几乎是一样的,如果你熟悉 Magisk 模块的开发,那么开发 KernelSU 的模块大同小异,你可以跳过下面有关模块的介绍,只需要了解 KernelSU 模块与 Magisk 模块的异同

Busybox

KernelSU 提供了一个功能完备的 BusyBox 二进制文件(包括完整的SELinux支持)。可执行文件位于 /data/adb/ksu/bin/busybox。 KernelSU 的 BusyBox 支持运行时可切换的 "ASH Standalone Shell Mode"。 这种独立模式意味着在运行 BusyBox 的 ash shell 时,每个命令都会直接使用 BusyBox 中内置的应用程序,而不管 PATH 设置为什么。 例如,lsrmchmod 等命令将不会使用 PATH 中设置的命令(在Android的情况下,默认情况下分别为 /system/bin/ls/system/bin/rm/system/bin/chmod),而是直接调用 BusyBox 内置的应用程序。 这确保了脚本始终在可预测的环境中运行,并始终具有完整的命令套件,无论它运行在哪个Android版本上。 要强制一个命令不使用BusyBox,你必须使用完整路径调用可执行文件。

在 KernelSU 上下文中运行的每个 shell 脚本都将在 BusyBox 的 ash shell 中以独立模式运行。对于第三方开发者相关的内容,包括所有启动脚本和模块安装脚本。

对于想要在 KernelSU 之外使用这个“独立模式”功能的用户,有两种启用方法:

  1. 设置环境变量 ASH_STANDALONE1。例如:ASH_STANDALONE=1 /data/adb/ksu/bin/busybox sh <script>
  2. 使用命令行选项切换:/data/adb/ksu/bin/busybox sh -o standalone <script>

为了确保所有后续的 sh shell 都在独立模式下执行,第一种是首选方法(这也是 KernelSU 和 KernelSU 管理器内部使用的方法),因为环境变量会被继承到子进程中。

与 Magisk 的差异

KernelSU 的 BusyBox 现在是直接使用 Magisk 项目编译的二进制文件,感谢 Magisk! 因此,你完全不用担心 BusyBox 脚本与在 Magisk 和 KernelSU 之间的兼容问题,因为他们是完全一样的!

KernelSU 模块

KernelSU 模块就是一个放置在 /data/adb/modules 内且满足如下结构的文件夹:

txt
/data/adb/modules
+├── .
+├── .
+|
+├── $MODID                  <--- 模块的文件夹名称与模块 ID 相同
+│   │
+│   │      *** 模块配置文件 ***
+│   │
+│   ├── module.prop         <--- 此文件保存模块相关的一些配置,如模块 ID、版本等
+│   │
+│   │      *** 模块内容 ***
+│   │
+│   ├── system              <--- 这个文件夹通常会被挂载到系统
+│   │   ├── ...
+│   │   ├── ...
+│   │   └── ...
+│   │
+│   │      *** 标记文件 ***
+│   │
+│   ├── skip_mount          <--- 如果这个文件存在,那么模块的 `/system` 将不会被挂载
+│   ├── disable             <--- 如果这个文件存在,那么模块会被禁用
+│   ├── remove              <--- 如果这个文件存在,下次重启的时候模块会被移除
+│   │
+│   │      *** 可选文件 ***
+│   │
+│   ├── post-fs-data.sh     <--- 这个脚本将会在 post-fs-data 模式下运行
+│   ├── post-mount.sh       <--- 这个脚本将会在 post-mount 模式下运行
+│   ├── service.sh          <--- 这个脚本将会在 late_start 服务模式下运行
+│   ├── boot-completed.sh   <--- 这个脚本将会在 Android 系统启动完毕后以服务模式运行
+|   ├── uninstall.sh        <--- 这个脚本将会在模块被卸载时运行
+│   ├── system.prop         <--- 这个文件中指定的属性将会在系统启动时通过 resetprop 更改
+│   ├── sepolicy.rule       <--- 这个文件中的 SELinux 策略将会在系统启动时加载
+│   │
+│   │      *** 自动生成的目录,不要手动创建或者修改! ***
+│   │
+│   ├── vendor              <--- A symlink to $MODID/system/vendor
+│   ├── product             <--- A symlink to $MODID/system/product
+│   ├── system_ext          <--- A symlink to $MODID/system/system_ext
+│   │
+│   │      *** Any additional files / folders are allowed ***
+│   │
+│   ├── ...
+│   └── ...
+|
+├── another_module
+│   ├── .
+│   └── .
+├── .
+├── .
/data/adb/modules
+├── .
+├── .
+|
+├── $MODID                  <--- 模块的文件夹名称与模块 ID 相同
+│   │
+│   │      *** 模块配置文件 ***
+│   │
+│   ├── module.prop         <--- 此文件保存模块相关的一些配置,如模块 ID、版本等
+│   │
+│   │      *** 模块内容 ***
+│   │
+│   ├── system              <--- 这个文件夹通常会被挂载到系统
+│   │   ├── ...
+│   │   ├── ...
+│   │   └── ...
+│   │
+│   │      *** 标记文件 ***
+│   │
+│   ├── skip_mount          <--- 如果这个文件存在,那么模块的 `/system` 将不会被挂载
+│   ├── disable             <--- 如果这个文件存在,那么模块会被禁用
+│   ├── remove              <--- 如果这个文件存在,下次重启的时候模块会被移除
+│   │
+│   │      *** 可选文件 ***
+│   │
+│   ├── post-fs-data.sh     <--- 这个脚本将会在 post-fs-data 模式下运行
+│   ├── post-mount.sh       <--- 这个脚本将会在 post-mount 模式下运行
+│   ├── service.sh          <--- 这个脚本将会在 late_start 服务模式下运行
+│   ├── boot-completed.sh   <--- 这个脚本将会在 Android 系统启动完毕后以服务模式运行
+|   ├── uninstall.sh        <--- 这个脚本将会在模块被卸载时运行
+│   ├── system.prop         <--- 这个文件中指定的属性将会在系统启动时通过 resetprop 更改
+│   ├── sepolicy.rule       <--- 这个文件中的 SELinux 策略将会在系统启动时加载
+│   │
+│   │      *** 自动生成的目录,不要手动创建或者修改! ***
+│   │
+│   ├── vendor              <--- A symlink to $MODID/system/vendor
+│   ├── product             <--- A symlink to $MODID/system/product
+│   ├── system_ext          <--- A symlink to $MODID/system/system_ext
+│   │
+│   │      *** Any additional files / folders are allowed ***
+│   │
+│   ├── ...
+│   └── ...
+|
+├── another_module
+│   ├── .
+│   └── .
+├── .
+├── .

与 Magisk 的差异

KernelSU 没有内置的针对 Zygisk 的支持,因此模块中没有 Zygisk 相关的内容,但你可以通过 ZygiskNext 来支持 Zygisk 模块,此时 Zygisk 模块的内容与 Magisk 所支持的 Zygisk 是完全相同的。

module.prop

module.prop 是一个模块的配置文件,在 KernelSU 中如果模块中不包含此文件,那么它将不被认为是一个模块;此文件的格式如下:

txt
id=<string>
+name=<string>
+version=<string>
+versionCode=<int>
+author=<string>
+description=<string>
id=<string>
+name=<string>
+version=<string>
+versionCode=<int>
+author=<string>
+description=<string>
  • id 必须与这个正则表达式匹配:^[a-zA-Z][a-zA-Z0-9._-]+$ 例如:✓ a_module,✓ a.module,✓ module-101,✗ a module,✗ 1_module,✗ -a-module。这是您的模块的唯一标识符,发布后不应更改。
  • versionCode 必须是一个整数,用于比较版本。
  • 其他未在上面提到的内容可以是任何单行字符串。
  • 请确保使用 UNIX(LF)换行类型,而不是Windows(CR + LF)或 Macintosh(CR)。

Shell 脚本

请阅读 启动脚本 一节,以了解 post-fs-data.sh, post-mount.sh, service.shboot-completed.sh 之间的区别。对于大多数模块开发者来说,如果您只需要运行一个启动脚本,service.sh 应该已经足够了。

在您的模块的所有脚本中,请使用 MODDIR=${0%/*}来获取您的模块的基本目录路径;请勿在脚本中硬编码您的模块路径。

与 Magisk 的差异

你可以通过环境变量 KSU 来判断脚本是运行在 KernelSU 还是 Magisk 中,如果运行在 KernelSU,这个值会被设置为 true

system 目录

这个目录的内容会在系统启动后,以 overlayfs 的方式叠加在系统的 /system 分区之上,这意味着:

  1. 系统中对应目录的同名文件会被此目录的文件覆盖。
  2. 系统中对应目录的同名文件夹会与此目录的文件夹合并。

如果你想删掉系统原来目录某个文件或者文件夹,你需要在模块目录通过 mknod filename c 0 0 来创建一个 filename 的同名文件;这样 overlayfs 系统会自动 whiteout 等效删除此文件(/system 分区并没有被更改)。

你也可以在 customize.sh 中声明一个名为 REMOVE 并且包含一系列目录的变量来执行删除操作,KernelSU 会自动为你在模块对应目录执行 mknod <TARGET> c 0 0。例如:

sh
REMOVE="
+/system/app/YouTube
+/system/app/Bloatware
+"
REMOVE="
+/system/app/YouTube
+/system/app/Bloatware
+"

上面的这个列表将会执行: mknod $MODPATH/system/app/YouTuBe c 0 0mknod $MODPATH/system/app/Bloatware c 0 0;并且 /system/app/YouTube/system/app/Bloatware 将会在模块生效后被删除。

如果你想替换掉系统的某个目录,你需要在模块目录创建一个相同路径的目录,然后为此目录设置此属性:setfattr -n trusted.overlay.opaque -v y <TARGET>;这样 overlayfs 系统会自动将系统内相应目录替换(/system 分区并没有被更改)。

你可以在 customize.sh 中声明一个名为 REPLACE 并且包含一系列目录的变量来执行替换操作,KernelSU 会自动为你在模块对应目录执行相关操作。例如:

sh
REPLACE="
+/system/app/YouTube
+/system/app/Bloatware
+"
REPLACE="
+/system/app/YouTube
+/system/app/Bloatware
+"

上面这个列表将会:自动创建目录 $MODPATH/system/app/YouTube$MODPATH//system/app/Bloatware,然后执行 setfattr -n trusted.overlay.opaque -v y $$MODPATH/system/app/YouTubesetfattr -n trusted.overlay.opaque -v y $$MODPATH/system/app/Bloatware;并且 /system/app/YouTube/system/app/Bloatware 将会在模块生效后替换为空目录。

与 Magisk 的差异

KernelSU 的 systemless 机制是通过内核的 overlayfs 实现的,而 Magisk 当前则是通过 magic mount (bind mount),二者实现方式有着巨大的差异,但最终的目标实际上是一致的:不修改物理的 /system 分区但实现修改 /system 文件。

如果你对 overlayfs 感兴趣,建议阅读 Linux Kernel 关于 overlayfs 的文档

system.prop

这个文件的格式与 build.prop 完全相同:每一行都是 [key]=[value] 的形式。

sepolicy.rule

如果您的模块需要一些额外的 SELinux 策略补丁,请将这些规则添加到此文件中。这个文件中的每一行都将被视为一个策略语句。

模块安装包

KernelSU 的模块安装包就是一个可以通过 KernelSU 管理器 APP 刷入的 zip 文件,此 zip 文件的格式如下:

txt
module.zip
+
+├── customize.sh                       <--- (Optional, more details later)
+│                                           This script will be sourced by update-binary
+├── ...
+├── ...  /* 其他模块文件 */
+
module.zip
+
+├── customize.sh                       <--- (Optional, more details later)
+│                                           This script will be sourced by update-binary
+├── ...
+├── ...  /* 其他模块文件 */
+

WARNING

KernelSU 模块不支持在 Recovery 中安装!!

定制安装过程

如果你想控制模块的安装过程,可以在模块的目录下创建一个名为 customize.sh 的文件,这个脚本将会在模块被解压后导入到当前 shell 中,如果你的模块需要根据设备的 API 版本或者设备构架做一些额外的操作,那这个脚本将非常有用。

如果你想完全控制脚本的安装过程,你可以在 customize.sh 中声明 SKIPUNZIP=1 来跳过所有的默认安装步骤;此时,你需要自行处理所有安装过程(如解压模块,设置权限等)

customize.sh 脚本以“独立模式”运行在 KernelSU 的 BusyBox ash shell 中。你可以使用如下变量和函数:

变量

  • KSU (bool): 标记此脚本运行在 KernelSU 环境下,此变量的值将永远为 true,你可以通过它区分 Magisk。
  • KSU_VER (string): KernelSU 当前的版本名字 (如: v0.4.0)
  • KSU_VER_CODE (int): KernelSU 用户空间当前的版本号 (如. 10672)
  • KSU_KERNEL_VER_CODE (int): KernelSU 内核空间当前的版本号 (如. 10672)
  • BOOTMODE (bool): 此变量在 KernelSU 中永远为 true
  • MODPATH (path): 当前模块的安装目录
  • TMPDIR (path): 可以存放临时文件的目录
  • ZIPFILE (path): 当前模块的安装包文件
  • ARCH (string): 设备的 CPU 构架,有如下几种: arm, arm64, x86, or x64
  • IS64BIT (bool): 是否是 64 位设备
  • API (int): 当前设备的 Android API 版本 (如:Android 6.0 上为 23)

WARNING

MAGISK_VER_CODE 在 KernelSU 中永远为 25200MAGISK_VER 则为 v25.2,请不要通过这两个变量来判断是否是 KernelSU!

函数

txt
ui_print <msg>
+    print <msg> to console
+    Avoid using 'echo' as it will not display in custom recovery's console
+
+abort <msg>
+    print error message <msg> to console and terminate the installation
+    Avoid using 'exit' as it will skip the termination cleanup steps
+
+set_perm <target> <owner> <group> <permission> [context]
+    if [context] is not set, the default is "u:object_r:system_file:s0"
+    this function is a shorthand for the following commands:
+       chown owner.group target
+       chmod permission target
+       chcon context target
+
+set_perm_recursive <directory> <owner> <group> <dirpermission> <filepermission> [context]
+    if [context] is not set, the default is "u:object_r:system_file:s0"
+    for all files in <directory>, it will call:
+       set_perm file owner group filepermission context
+    for all directories in <directory> (including itself), it will call:
+       set_perm dir owner group dirpermission context
ui_print <msg>
+    print <msg> to console
+    Avoid using 'echo' as it will not display in custom recovery's console
+
+abort <msg>
+    print error message <msg> to console and terminate the installation
+    Avoid using 'exit' as it will skip the termination cleanup steps
+
+set_perm <target> <owner> <group> <permission> [context]
+    if [context] is not set, the default is "u:object_r:system_file:s0"
+    this function is a shorthand for the following commands:
+       chown owner.group target
+       chmod permission target
+       chcon context target
+
+set_perm_recursive <directory> <owner> <group> <dirpermission> <filepermission> [context]
+    if [context] is not set, the default is "u:object_r:system_file:s0"
+    for all files in <directory>, it will call:
+       set_perm file owner group filepermission context
+    for all directories in <directory> (including itself), it will call:
+       set_perm dir owner group dirpermission context

启动脚本

在 KernelSU 中,根据脚本运行模式的不同分为两种:post-fs-data 模式和 late_start 服务模式。

  • post-fs-data 模式

    • 这个阶段是阻塞的。在执行完成之前或者 10 秒钟之后,启动过程会暂停。
    • 脚本在任何模块被挂载之前运行。这使得模块开发者可以在模块被挂载之前动态地调整它们的模块。
    • 这个阶段发生在 Zygote 启动之前。
    • 使用 setprop 会导致启动过程死锁!请使用 resetprop -n <prop_name> <prop_value> 代替。
    • 只有在必要时才在此模式下运行脚本
  • late_start 服务模式

    • 这个阶段是非阻塞的。你的脚本会与其余的启动过程并行运行。
    • 大多数脚本都建议在这种模式下运行

在 KernelSU 中,启动脚本根据存放位置的不同还分为两种:通用脚本和模块脚本。

  • 通用脚本

    • 放置在 /data/adb/post-fs-data.d, /data/adb/post-mount.d, /data/adb/service.d/data/adb/boot-completed.d 中。
    • 只有在脚本被设置为可执行(chmod +x script.sh)时才会被执行。
    • post-fs-data.d 中的脚本以 post-fs-data 模式运行,在 service.d 中的脚本以 late_start 服务模式运行。
    • 模块不应在安装过程中添加通用脚本。
  • 模块脚本

    • 放置在模块自己的文件夹中。
    • 只有当模块被启用时才会执行。
    • post-fs-data.sh 以 post-fs-data 模式运行,post-mount.sh 以 post-mount 模式运行,而 service.sh 则以 late_start 服务模式运行,boot-completed 在 Android 系统启动完毕后以服务模式运行。

所有启动脚本都将在 KernelSU 的 BusyBox ash shell 中运行,并启用“独立模式”。

在 GPL3 许可证下发布。

+ \ No newline at end of file diff --git a/zh_CN/guide/rescue-from-bootloop.html b/zh_CN/guide/rescue-from-bootloop.html index ca0aa03b1151..0f81d9246e78 100644 --- a/zh_CN/guide/rescue-from-bootloop.html +++ b/zh_CN/guide/rescue-from-bootloop.html @@ -5,18 +5,19 @@ 救砖 | KernelSU - - + + + - - - - + + + + + -
Skip to content
On this page

救砖

在刷机的时候我们可能会遇到设备“变砖”的情况,理论上讲,如果你只是使用 fastboot 刷入 boot 分区或者安装不合适的模块导致设备无法启动,那么这都可以通过合适的操作恢复手机;本文档旨在提供一些急救方法让你可以在“变砖”中恢复。

刷入 boot 变砖

在 KernelSU 中,刷入 boot 变砖有如下可能:

  1. 你刷入了错误格式的 boot 镜像。比如你的手机 boot 格式是 gz 的,但你刷入了 lz4 格式的镜像,那么此时手机无法启动。
  2. 你的手机需要关闭 avb 验证才能正常启动(注意这通常意味着需要清除手机所有数据)。
  3. 你的 kernel 有某些 bug 或者你的 kernel 不适合你这个手机刷入。

无论哪种情况,你都可以通过刷入原厂 boot恢复;因此,在安装教程最开始,我们已经强烈建议大家,在刷机之前备份自己的原厂 boot!如果你没有备份,那么你可以通过其他跟你相同设备的童鞋或者官方固件包获取原厂 boot。

刷入模块变砖

刷入模块变砖可能是大家遇到更常见的情况,但是这里必须郑重告诉大家:请勿刷入来路不明的模块!!。因为模块其实是有 root 权限的,它完全可能导致你的设备发生不可逆的损坏!

普通模块变砖

如果大家刷入某些开源的或者被证明是安全的模块使得手机无法启动,那么这种情况在 KernelSU 中非常容易恢复,完全无需担心。KernelSU 内置了如下两种机制来救砖:

  1. AB 更新
  2. 音量键救砖

AB 更新

KernelSU 的模块更新借鉴了 Android 系统 OTA 更新时的 AB 更新机制,如果你安装了新模块或者对已有模块有更新操作,不会直接操作当前使用的模块文件,而是会把所有模块构建成另外一个 update 镜像;系统重启之后,会使用这个 update 镜像尝试启动一次,如果 Android 系统成功启动,才会真正更新模块。

因此,最简单最常用的救砖方法就是:强制重启一次。如果你在刷某个模块之后系统无法启动,你可以长按电源键超过 10 秒,系统会自动重启;重启之后会回滚到更新模块之前的状态,之前更新的模块会被自动禁用。

音量键救砖

如果 AB 更新依然无法解决,你可以尝试使用安全模式。进入安全模式之后,所有的模块都会被禁用。

进入安全模式的方法有两种:

  1. 某些系统自带的安全模式;有些系统是长按音量下,有些系统(比如MIUI)可以在 Recovery 中开启安全模式。进入系统的安全模式后,KernelSU 也会进入安全模式,自动禁用模块。
  2. KernelSU 内置的安全模式;操作方法:开机第一屏后,连续按音量下键超过三次。注意是按下-松开、按下-松开、按下-松开,不是按着不动。

进入安全模式以后,KernelSU 管理器的模块页面所有模块都被禁用,但你可以执行“卸载”操作,卸载可能会有问题的模块。

内置的安全模式是在内核里面实现的,因此不会出现按键事件被拦截导致捕获不到的情况。不过对于非 GKI 内核,可能需要手动集成代码,可以参考官网教程。

格机或其他病毒模块变砖

如果以上方法无法拯救你的设备,那么很有可能你装的模块有恶意操作或者通过其他方式损坏了你的设备,这种情况下,只有两个建议:

  1. 清除数据后刷入完整刷入官方系统。
  2. 咨询售后服务。

在 GPL3 许可证下发布。

- +
Skip to content

救砖

在刷机的时候我们可能会遇到设备“变砖”的情况,理论上讲,如果你只是使用 fastboot 刷入 boot 分区或者安装不合适的模块导致设备无法启动,那么这都可以通过合适的操作恢复手机;本文档旨在提供一些急救方法让你可以在“变砖”中恢复。

刷入 boot 变砖

在 KernelSU 中,刷入 boot 变砖有如下可能:

  1. 你刷入了错误格式的 boot 镜像。比如你的手机 boot 格式是 gz 的,但你刷入了 lz4 格式的镜像,那么此时手机无法启动。
  2. 你的手机需要关闭 avb 验证才能正常启动(注意这通常意味着需要清除手机所有数据)。
  3. 你的 kernel 有某些 bug 或者你的 kernel 不适合你这个手机刷入。

无论哪种情况,你都可以通过刷入原厂 boot恢复;因此,在安装教程最开始,我们已经强烈建议大家,在刷机之前备份自己的原厂 boot!如果你没有备份,那么你可以通过其他跟你相同设备的童鞋或者官方固件包获取原厂 boot。

刷入模块变砖

刷入模块变砖可能是大家遇到更常见的情况,但是这里必须郑重告诉大家:请勿刷入来路不明的模块!!。因为模块其实是有 root 权限的,它完全可能导致你的设备发生不可逆的损坏!

普通模块变砖

如果大家刷入某些开源的或者被证明是安全的模块使得手机无法启动,那么这种情况在 KernelSU 中非常容易恢复,完全无需担心。KernelSU 内置了如下两种机制来救砖:

  1. AB 更新
  2. 音量键救砖

AB 更新

KernelSU 的模块更新借鉴了 Android 系统 OTA 更新时的 AB 更新机制,如果你安装了新模块或者对已有模块有更新操作,不会直接操作当前使用的模块文件,而是会把所有模块构建成另外一个 update 镜像;系统重启之后,会使用这个 update 镜像尝试启动一次,如果 Android 系统成功启动,才会真正更新模块。

因此,最简单最常用的救砖方法就是:强制重启一次。如果你在刷某个模块之后系统无法启动,你可以长按电源键超过 10 秒,系统会自动重启;重启之后会回滚到更新模块之前的状态,之前更新的模块会被自动禁用。

音量键救砖

如果 AB 更新依然无法解决,你可以尝试使用安全模式。进入安全模式之后,所有的模块都会被禁用。

进入安全模式的方法有两种:

  1. 某些系统自带的安全模式;有些系统是长按音量下,有些系统(比如MIUI)可以在 Recovery 中开启安全模式。进入系统的安全模式后,KernelSU 也会进入安全模式,自动禁用模块。
  2. KernelSU 内置的安全模式;操作方法:开机第一屏后,连续按音量下键超过三次。注意是按下-松开、按下-松开、按下-松开,不是按着不动。

进入安全模式以后,KernelSU 管理器的模块页面所有模块都被禁用,但你可以执行“卸载”操作,卸载可能会有问题的模块。

内置的安全模式是在内核里面实现的,因此不会出现按键事件被拦截导致捕获不到的情况。不过对于非 GKI 内核,可能需要手动集成代码,可以参考官网教程。

格机或其他病毒模块变砖

如果以上方法无法拯救你的设备,那么很有可能你装的模块有恶意操作或者通过其他方式损坏了你的设备,这种情况下,只有两个建议:

  1. 清除数据后刷入完整刷入官方系统。
  2. 咨询售后服务。

在 GPL3 许可证下发布。

+ \ No newline at end of file diff --git a/zh_CN/guide/unofficially-support-devices.html b/zh_CN/guide/unofficially-support-devices.html index 90ff07d57917..fe44f20e9fc2 100644 --- a/zh_CN/guide/unofficially-support-devices.html +++ b/zh_CN/guide/unofficially-support-devices.html @@ -5,19 +5,20 @@ 非官方支持设备 | KernelSU - - + + + - - - - - + + + + + + -
Skip to content
On this page

非官方支持设备

WARNING

本文档列出由其他开发者维护的支持 KernelSU 的非 GKI 设备内核

WARNING

本文档仅方便查找设备对应源码,这并不意味该源码 KernelSU 开发者审查,你应自行承担使用风险。

维护者仓库地址支持设备
diphonsD8G_Kernel_oxygenPoco F3: alioth | Poco F4: munch | MI10T/PRO: Apollo
diphonsD8G_Kernel_SM8150Poco X3 Pro: Vayu | Bhima
diphonsD8G_Kernel_SDM845Poco F1 | MI8 | MiMix2S
diphonsD8G_Kernel_SM8350Vili
diphonsD8G_Kernel_MarblePoco F5 | Redmi Note 12 Turbo | Marble
th1nhhdkandroid_kernel_sony_sm8250-kernelsuSony Xperia 1 II | Sony Xperia 5 II
akash07knexus_kernel_xiaomi_sm8250Poco F4: munch
HMTheBoy154Darkmatter-kernelGeneric x86_64 devices running Android-x86
Asuka-mioandroid_kernel_xiaomi_casMi 10 Ultra: cas
xiaoleGunMiku_kernel_xiaomi_waynewayne
YamazakuraHkernel_xiaomi_cepheuscepheus for pixel experience
SakuraNotStupidandroid_kernel_xiaomi_sdm710Xiaomi MI 8 SE(sirius/xmsirius)
Aquarius223(paper)android_kernel_xiaomi_msm8998MI 6 (sagit) and MIX 2 (chiron) for LineageOS
SlackerStateandroid_kernel_xiaomi_sm6150Redmi K30 4G (phoenix)
RooGhz720kernel_xiaomi_sm6150REDMI NOTE 10 PRO (sweet)
OnlyTomInSecondandroid_kernel_xiaomi_sdm845Mi 8 (dipper) for LineageOS
Rohail33RealKing KernelApollo(Redmi K30S Ultra/Mi 10T/Mi 10T Pro),Alioth(Redmi K40/POCO F3/Mi 11X),Munch(Redmi K40S/POCO F4), both MIUI and AOSP.
lateautumn233android_kernel_oneplus_sm8250OnePlus 8 Serials
Sreeshankar KNeverSettle KernelOnePlus Nord (avicii)
Molyuuneko_kernel_xiaomi_gauguinRedmi Note 9 Pro/ Mi 10T Lite/ Mi 10i
guh0613android_kernel_oppo_sm8150OPPO Reno Ace (OP4A89)
LeviMarvinandroid_kernel_xiaomi_aliothRedmi K40 / POCO F3
cibimokernel_xiaomi_raphael_ksuXiaomi Redmi K20 Pro / Mi 9T Pro (raphael)
EndCreditskernel_xiaomi_sm7250Redmi K30 5G ( picasso ) , Redmi K30i 5G ( picasso_48m)
msnxandroid-msm-coral-4.14-android13Pixel 4 XL
SoDebugkernel_xiaomi_raphaelXiaomi Redmi K20 Pro / Mi 9T Pro (raphael)
H1mJTkernel_realme_RMX1901Realme X (RMX1901/RMX1901CN)
SonalSingh18android_kernel_xiaomi_sm6250Miatoll [curtana, excalibur, gram, joyeuse]
RooGhz720kernel_xiaomi_lavenderRedmi Note 7 (Lavender)
JunASAKAkernel_google_msm-4.9_KernelSUGoogle Pixel 3a & 3a XL (sargo & bonito)
RooGhz720kernel_asus_X01BDAsus Zenfone Max Pro M1/M2
Evans Mikekernel_xiaomi_raphael_bool-xXiaomi Redmi K20 Pro / Mi 9T Pro (raphael)
easterNdayKSU_Thyme_BuildBotXiaomi 10S
tedomi2705kernel_xiaomi_sdm660Xiaomi Redmi Note 6 Pro (tulip)
RyuujiXandroid_kernel_asus_sdm660-4.19Asus Zenfone Max Pro M1 (X00TD)/ M2 (X01BD) (Linux 4.19)
liqidecgandroid_kernel_xiaomi_sdm710Mi 8 SE (sirius) (Linux 4.9)
Abdelhay-Aliandroid_kernel_huawei_hi6250Huawei P20 lite (hi6250) (Linux 4.9)
reocatandroid_kernel_nokia_sdm660_ksuNokia 6.1 (2018)
Tuan Anhkernel_nokia_msm8998Nokia 8 (Repartitioned)/8 Sirocco (NLA/A1N)
bggRGjQaUbCoEandroid_kernel_samsung_sm8250Samsung Galaxy S20+ 5G (y2q)
SOVIET-ANDROIDkernel_xiaomi_raphaelXK20 Pro Raphael DSP & A only SAR support
dabao1955android_kernel_OPPO_PEQM00MediaTek devices in the OPPO Reno series on ColorOS13.x
Aflaungosandroid_kernel_motorola_msm8998Moto G6 Plus (evert)
TheNoFacekernel_oneplus_msm8998OnePlus 5/5T (cheeseburger/dumpling)
Nipin NA (Joker-V2)Xcalibur kernel (violet)Xiaomi Redmi Note 7 Pro (violet)
Sr-Hankernel_xiaomi_mojitoRedmi Note 10 (Sunny/Mojito)
dabao1955android_kernel_OPPO_OP4ED5OPPO Reno6 on ColorOS11.x/ColorOS12.x,kernel version4.14.186+
rushiranpiseandroid_kernel_samsung_exynos9610_mintKernel 4.14.194 exynos9610 Non-GKI Device, Added KernelSu using manual method
CN-Scarspixel_experience_kernel_xiaomi_sm8250_kernelSUPixel Experience Kernel 4.19.282 for Xiaomi Redmi K40S / Xiaomi Poco F4 (munch)
exerMiyo TokuSony Tama (akari, apollo, aurora, akatsuki) (Linux 4.9)
likkaiQuicksilver KernelXiaomi 11 Lite 5G NE (lisa)
awakened1712android_kernel_samsung_exynos9820Samsung S10/N10
awakened1712android_kernel_oneplus_sm8350Oneplus 9/9Pro
siimsekLightning KernelXiaomi Redmi Note 8/8T (ginkgo/willow)
GiovanYCringekernel_a50Galaxy A50 (A505f,fn,g,gn,gt)
Asriadi Rahimandroid_kernel_google_wahooGoogle Pixel 2/2XL
Flameandroid_kernel_xiaomi_sm8250POCO F3/Redmi K40/Mi 11X (alioth)
DawfukFRkernel_oneplus_sm8250 (Stellaris-Kernel)Oneplus 8 (Instantnoodle), Oneplus 8T (Kebab), Oneplus 8 Pro (Instantnoodlep), Oneplus 9R (lemonades) [msm-4.19]
Tejas Singhkernel_xiaomi_ginkgo (Cuh Kernel)Xiaomi Redmi Note 8/8T (ginkgo/willow)
Coconutatandroid_kernel_xiaomi_ruby_expRedmi Note 12 Pro / Pro+ For MIUI 14
Coconutatandroid_kernel_xiaomi_sdm845_expXiaomi Mi 8 Pro(UD)[equuleus]/Explorer Edition[ursa]
Abdul Wahid Khankernel_xiaomi_begoniaRedmi Note 8 Pro (Begonia/Begoniain)
Abdul Wahid Khankernel_xiaomi_marbleXiaomi Poco F5 / Redmi Note 12 Turbo (Marble)
zfdx123kernel_xiaomi_aliothRedmi K40 (alioth)
wxt1221android_kernel_oneplus_sdm845Oneplus 6 (enchilada) | Oneplus 6T (fajita) with no Retrofit Dynamic Partitions
zharzinhooKernel-Oriente-GuampMoto g9 play
hc841NovaCore_kernelMoto g9 play
zlm324android_kernel_xiaomi_msm8998_ksua lineage kernel forked from LineageOS official repository, version 4.4.302, added ksu.
zlm324android_kernel_xiaomi_msm8953_ksuXiaomi 5X (tiffany)
Vincent4440android_kernel_xiaomi_sm8250Poco F4: munch
zharzinhooandroid_kernel_motorola_cebuMoto g9 power
tiandoufayaleandroid_kernel_samsung_sm8250Samsung Tab S7 WIFI(T870)
starmoeandroid_kernel_oneplus_msm8998Oneplus 5/5T, kernel version 4.4.302
supechickenkernel_xiaomi_sm8350 Xiaomi 11T Pro (vili)
Vaz15kkernelSU-A70qSamsung Galaxy A70 ( A705MN / A705FN )
whyakariandroid_kernel_xiaomi_ginkgonon-GKI ginkgo
wulan17android_kernel_xiaomi_mt6765Redmi 6/6A (cereus/cactus)
officialputuidandroid_kernel_realme_mt6785Realme 6/6i(Indian)/6s/7/Narzo/Narzo 20 Pro/Narzo 30 4G (RM6785)

在 GPL3 许可证下发布。

- +
Skip to content

非官方支持设备

WARNING

本文档列出由其他开发者维护的支持 KernelSU 的非 GKI 设备内核

WARNING

本文档仅方便查找设备对应源码,这并不意味该源码 KernelSU 开发者审查,你应自行承担使用风险。

维护者仓库地址支持设备
diphonsD8G_Kernel_oxygenPoco F3: alioth | Poco F4: munch | MI10T/PRO: Apollo
diphonsD8G_Kernel_SM8150Poco X3 Pro: Vayu | Bhima
diphonsD8G_Kernel_SDM845Poco F1 | MI8 | MiMix2S
diphonsD8G_Kernel_SM8350Vili
diphonsD8G_Kernel_MarblePoco F5 | Redmi Note 12 Turbo | Marble
th1nhhdkandroid_kernel_sony_sm8250-kernelsuSony Xperia 1 II | Sony Xperia 5 II
akash07knexus_kernel_xiaomi_sm8250Poco F4: munch
HMTheBoy154Darkmatter-kernelGeneric x86_64 devices running Android-x86
Asuka-mioandroid_kernel_xiaomi_casMi 10 Ultra: cas
xiaoleGunMiku_kernel_xiaomi_waynewayne
YamazakuraHkernel_xiaomi_cepheuscepheus for pixel experience
SakuraNotStupidandroid_kernel_xiaomi_sdm710Xiaomi MI 8 SE(sirius/xmsirius)
Aquarius223(paper)android_kernel_xiaomi_msm8998MI 6 (sagit) and MIX 2 (chiron) for LineageOS
SlackerStateandroid_kernel_xiaomi_sm6150Redmi K30 4G (phoenix)
RooGhz720kernel_xiaomi_sm6150REDMI NOTE 10 PRO (sweet)
OnlyTomInSecondandroid_kernel_xiaomi_sdm845Mi 8 (dipper) for LineageOS
Rohail33RealKing KernelApollo(Redmi K30S Ultra/Mi 10T/Mi 10T Pro),Alioth(Redmi K40/POCO F3/Mi 11X),Munch(Redmi K40S/POCO F4), both MIUI and AOSP.
lateautumn233android_kernel_oneplus_sm8250OnePlus 8 Serials
Sreeshankar KNeverSettle KernelOnePlus Nord (avicii)
Molyuuneko_kernel_xiaomi_gauguinRedmi Note 9 Pro/ Mi 10T Lite/ Mi 10i
guh0613android_kernel_oppo_sm8150OPPO Reno Ace (OP4A89)
LeviMarvinandroid_kernel_xiaomi_aliothRedmi K40 / POCO F3
cibimokernel_xiaomi_raphael_ksuXiaomi Redmi K20 Pro / Mi 9T Pro (raphael)
EndCreditskernel_xiaomi_sm7250Redmi K30 5G ( picasso ) , Redmi K30i 5G ( picasso_48m)
msnxandroid-msm-coral-4.14-android13Pixel 4 XL
SoDebugkernel_xiaomi_raphaelXiaomi Redmi K20 Pro / Mi 9T Pro (raphael)
H1mJTkernel_realme_RMX1901Realme X (RMX1901/RMX1901CN)
SonalSingh18android_kernel_xiaomi_sm6250Miatoll [curtana, excalibur, gram, joyeuse]
RooGhz720kernel_xiaomi_lavenderRedmi Note 7 (Lavender)
JunASAKAkernel_google_msm-4.9_KernelSUGoogle Pixel 3a & 3a XL (sargo & bonito)
RooGhz720kernel_asus_X01BDAsus Zenfone Max Pro M1/M2
Evans Mikekernel_xiaomi_raphael_bool-xXiaomi Redmi K20 Pro / Mi 9T Pro (raphael)
easterNdayKSU_Thyme_BuildBotXiaomi 10S
tedomi2705kernel_xiaomi_sdm660Xiaomi Redmi Note 6 Pro (tulip)
RyuujiXandroid_kernel_asus_sdm660-4.19Asus Zenfone Max Pro M1 (X00TD)/ M2 (X01BD) (Linux 4.19)
liqidecgandroid_kernel_xiaomi_sdm710Mi 8 SE (sirius) (Linux 4.9)
Abdelhay-Aliandroid_kernel_huawei_hi6250Huawei P20 lite (hi6250) (Linux 4.9)
reocatandroid_kernel_nokia_sdm660_ksuNokia 6.1 (2018)
Tuan Anhkernel_nokia_msm8998Nokia 8 (Repartitioned)/8 Sirocco (NLA/A1N)
bggRGjQaUbCoEandroid_kernel_samsung_sm8250Samsung Galaxy S20+ 5G (y2q)
SOVIET-ANDROIDkernel_xiaomi_raphaelXK20 Pro Raphael DSP & A only SAR support
dabao1955android_kernel_OPPO_PEQM00MediaTek devices in the OPPO Reno series on ColorOS13.x
Aflaungosandroid_kernel_motorola_msm8998Moto G6 Plus (evert)
TheNoFacekernel_oneplus_msm8998OnePlus 5/5T (cheeseburger/dumpling)
Nipin NA (Joker-V2)Xcalibur kernel (violet)Xiaomi Redmi Note 7 Pro (violet)
Sr-Hankernel_xiaomi_mojitoRedmi Note 10 (Sunny/Mojito)
dabao1955android_kernel_OPPO_OP4ED5OPPO Reno6 on ColorOS11.x/ColorOS12.x,kernel version4.14.186+
rushiranpiseandroid_kernel_samsung_exynos9610_mintKernel 4.14.194 exynos9610 Non-GKI Device, Added KernelSu using manual method
CN-Scarspixel_experience_kernel_xiaomi_sm8250_kernelSUPixel Experience Kernel 4.19.282 for Xiaomi Redmi K40S / Xiaomi Poco F4 (munch)
exerMiyo TokuSony Tama (akari, apollo, aurora, akatsuki) (Linux 4.9)
likkaiQuicksilver KernelXiaomi 11 Lite 5G NE (lisa)
awakened1712android_kernel_samsung_exynos9820Samsung S10/N10
awakened1712android_kernel_oneplus_sm8350Oneplus 9/9Pro
siimsekLightning KernelXiaomi Redmi Note 8/8T (ginkgo/willow)
GiovanYCringekernel_a50Galaxy A50 (A505f,fn,g,gn,gt)
Asriadi Rahimandroid_kernel_google_wahooGoogle Pixel 2/2XL
Flameandroid_kernel_xiaomi_sm8250POCO F3/Redmi K40/Mi 11X (alioth)
DawfukFRkernel_oneplus_sm8250 (Stellaris-Kernel)Oneplus 8 (Instantnoodle), Oneplus 8T (Kebab), Oneplus 8 Pro (Instantnoodlep), Oneplus 9R (lemonades) [msm-4.19]
Tejas Singhkernel_xiaomi_ginkgo (Cuh Kernel)Xiaomi Redmi Note 8/8T (ginkgo/willow)
Coconutatandroid_kernel_xiaomi_ruby_expRedmi Note 12 Pro / Pro+ For MIUI 14
Coconutatandroid_kernel_xiaomi_sdm845_expXiaomi Mi 8 Pro(UD)[equuleus]/Explorer Edition[ursa]
Abdul Wahid Khankernel_xiaomi_begoniaRedmi Note 8 Pro (Begonia/Begoniain)
Abdul Wahid Khankernel_xiaomi_marbleXiaomi Poco F5 / Redmi Note 12 Turbo (Marble)
zfdx123kernel_xiaomi_aliothRedmi K40 (alioth)
wxt1221android_kernel_oneplus_sdm845Oneplus 6 (enchilada) | Oneplus 6T (fajita) with no Retrofit Dynamic Partitions
zharzinhooKernel-Oriente-GuampMoto g9 play
hc841NovaCore_kernelMoto g9 play
zlm324android_kernel_xiaomi_msm8998_ksua lineage kernel forked from LineageOS official repository, version 4.4.302, added ksu.
zlm324android_kernel_xiaomi_msm8953_ksuXiaomi 5X (tiffany)
Vincent4440android_kernel_xiaomi_sm8250Poco F4: munch
zharzinhooandroid_kernel_motorola_cebuMoto g9 power
tiandoufayaleandroid_kernel_samsung_sm8250Samsung Tab S7 WIFI(T870)
starmoeandroid_kernel_oneplus_msm8998Oneplus 5/5T, kernel version 4.4.302
supechickenkernel_xiaomi_sm8350 Xiaomi 11T Pro (vili)
Vaz15kkernelSU-A70qSamsung Galaxy A70 ( A705MN / A705FN )
whyakariandroid_kernel_xiaomi_ginkgonon-GKI ginkgo
wulan17android_kernel_xiaomi_mt6765Redmi 6/6A (cereus/cactus)
officialputuidandroid_kernel_realme_mt6785Realme 6/6i(Indian)/6s/7/Narzo/Narzo 20 Pro/Narzo 30 4G (RM6785)

在 GPL3 许可证下发布。

+ \ No newline at end of file diff --git a/zh_CN/guide/what-is-kernelsu.html b/zh_CN/guide/what-is-kernelsu.html index 920776e4764b..fd6ce7af3fa2 100644 --- a/zh_CN/guide/what-is-kernelsu.html +++ b/zh_CN/guide/what-is-kernelsu.html @@ -5,18 +5,19 @@ 什么是 KernelSU? | KernelSU - - + + + - - - - + + + + + -
Skip to content
On this page

什么是 KernelSU?

KernelSU 是 Android GKI 设备的 root 解决方案,它工作在内核模式,并直接在内核空间中为用户空间应用程序授予 root 权限。

功能

KernelSU 的主要特点是它是基于内核的。 KernelSU 运行在内核空间, 所以它可以提供我们以前从未有过的内核接口。 例如,我们可以在内核模式下为任何进程添加硬件断点;我们可以在任何进程的物理内存中访问,而无人知晓;我们可以在内核空间拦截任何系统调用; 等等。

KernelSU 还提供了一个基于 overlayfs 的模块系统,允许您加载自定义插件到系统中。它还提供了一种修改 /system 分区中文件的机制。

如何使用

请参考: 安装

如何构建

请参考: 如何构建

讨论

在 GPL3 许可证下发布。

- +
Skip to content

什么是 KernelSU?

KernelSU 是 Android GKI 设备的 root 解决方案,它工作在内核模式,并直接在内核空间中为用户空间应用程序授予 root 权限。

功能

KernelSU 的主要特点是它是基于内核的。 KernelSU 运行在内核空间, 所以它可以提供我们以前从未有过的内核接口。 例如,我们可以在内核模式下为任何进程添加硬件断点;我们可以在任何进程的物理内存中访问,而无人知晓;我们可以在内核空间拦截任何系统调用; 等等。

KernelSU 还提供了一个基于 overlayfs 的模块系统,允许您加载自定义插件到系统中。它还提供了一种修改 /system 分区中文件的机制。

如何使用

请参考: 安装

如何构建

请参考: 如何构建

讨论

在 GPL3 许可证下发布。

+ \ No newline at end of file diff --git a/zh_CN/index.html b/zh_CN/index.html index a5306b494393..fbd677ed98e4 100644 --- a/zh_CN/index.html +++ b/zh_CN/index.html @@ -5,18 +5,19 @@ Android 上的内核级的 root 方案 | KernelSU - - + + + - - - - + + + + + -
Skip to content

KernelSU

Android 上的内核级的 root 方案

KernelSU

基于内核

KernelSU 运行在内核空间,对用户空间应用有更强的掌控。

白名单访问控制

只有被授权的 App 才可以访问 `su`,而其他 App 无法感知其存在。

受限制的 root 权限

KernelSU 可以自定义 `su` 的 uid, gid, groups, capabilities 和 SELinux 规则:把 root 权限关进笼子里。

模块系统 & 开源

KernelSU 支持通过 overlayfs 修改 /system,并且是 GPL-3 许可下的开源项目。

在 GPL3 许可证下发布。

- +
Skip to content

KernelSU

Android 上的内核级的 root 方案

KernelSU

在 GPL3 许可证下发布。

+ \ No newline at end of file diff --git a/zh_TW/guide/difference-with-magisk.html b/zh_TW/guide/difference-with-magisk.html index f327360827c3..cc6d18c30c2f 100644 --- a/zh_TW/guide/difference-with-magisk.html +++ b/zh_TW/guide/difference-with-magisk.html @@ -5,18 +5,19 @@ KernelSU 模組與 Magisk 的差異 | KernelSU - - + + + - - - - + + + + + -
Skip to content
On this page

KernelSU 模組與 Magisk 的差異

儘管 KernelSU 模組和 Magisk 模組之間有許多相似之處,但由於它們完全不同的實作機制,不可避免地存在一些差異;如果您想讓您的模組同時在 Magisk 和 KernelSU 上運作,那麼您必須瞭解這些差異。

相同之處

  • 模組檔案格式:都以 Zip 的格式組織模組,並且模組的格式幾乎相同
  • 模組安裝目錄:都位於 /data/adb/modules
  • Systemless:都支援透過模組的形式以 systemless 修改 /system
  • post-fs-data.sh:執行時間和語義完全相同
  • service.sh:執行時間和語義完全相同
  • system.prop:完全相同
  • sepolicy.rule:完全相同
  • BusyBox:指令碼在 BusyBox 中以「獨立模式」執行

不同之處

在瞭解不同之處之前,您需要知道如何區分您的模組是在 KernelSU 還是 Magisk 中執行;在所有可以執行模組指令碼的位置 (customize.sh, post-fs-data.sh, service.sh),您都可以使用環境變數 KSU 來區分,在 KernelSU 中,這個環境變數將被設定為 true

以下是一些不同之處:

  1. KernelSU 的模組不支援在 Recovery 中安裝。
  2. KernelSU 的模組沒有內建的 Zygisk 支援 (但您可以透過 ZygiskNext 來使用 Zygisk 模組)。
  3. KernelSU 模組取代或刪除檔案與 Magisk 完全不同。KernelSU 不支援 .replace 方法,相反,您需要透過 mknod filename c 0 0 建立相同名稱的資料夾以刪除對應檔案。
  4. BusyBox 的目錄不同;KernelSU 內建的 BusyBox 在 /data/adb/ksu/bin/busybox 而 Magisk 在 /data/adb/magisk/busybox注意此為 KernelSU 內部行為,未來可能會變更!
  5. KernelSU 不支援 .replace 檔案;但 KernelSU 支援 REPLACEREMOVE 變數以移除或取代檔案 (資料夾)。

係依據 GPL3 授權發行。

- +
Skip to content

KernelSU 模組與 Magisk 的差異

儘管 KernelSU 模組和 Magisk 模組之間有許多相似之處,但由於它們完全不同的實作機制,不可避免地存在一些差異;如果您想讓您的模組同時在 Magisk 和 KernelSU 上運作,那麼您必須瞭解這些差異。

相同之處

  • 模組檔案格式:都以 Zip 的格式組織模組,並且模組的格式幾乎相同
  • 模組安裝目錄:都位於 /data/adb/modules
  • Systemless:都支援透過模組的形式以 systemless 修改 /system
  • post-fs-data.sh:執行時間和語義完全相同
  • service.sh:執行時間和語義完全相同
  • system.prop:完全相同
  • sepolicy.rule:完全相同
  • BusyBox:指令碼在 BusyBox 中以「獨立模式」執行

不同之處

在瞭解不同之處之前,您需要知道如何區分您的模組是在 KernelSU 還是 Magisk 中執行;在所有可以執行模組指令碼的位置 (customize.sh, post-fs-data.sh, service.sh),您都可以使用環境變數 KSU 來區分,在 KernelSU 中,這個環境變數將被設定為 true

以下是一些不同之處:

  1. KernelSU 的模組不支援在 Recovery 中安裝。
  2. KernelSU 的模組沒有內建的 Zygisk 支援 (但您可以透過 ZygiskNext 來使用 Zygisk 模組)。
  3. KernelSU 模組取代或刪除檔案與 Magisk 完全不同。KernelSU 不支援 .replace 方法,相反,您需要透過 mknod filename c 0 0 建立相同名稱的資料夾以刪除對應檔案。
  4. BusyBox 的目錄不同;KernelSU 內建的 BusyBox 在 /data/adb/ksu/bin/busybox 而 Magisk 在 /data/adb/magisk/busybox注意此為 KernelSU 內部行為,未來可能會變更!
  5. KernelSU 不支援 .replace 檔案;但 KernelSU 支援 REPLACEREMOVE 變數以移除或取代檔案 (資料夾)。

係依據 GPL3 授權發行。

+ \ No newline at end of file diff --git a/zh_TW/guide/faq.html b/zh_TW/guide/faq.html index 2bb27ec21235..34828933597e 100644 --- a/zh_TW/guide/faq.html +++ b/zh_TW/guide/faq.html @@ -5,18 +5,19 @@ 常見問題 | KernelSU - - + + + - - - - + + + + + -
Skip to content
On this page

常見問題

KernelSU 是否支援我的裝置?

首先,您的裝置應該能解鎖 Bootloader。如果不能,則不支援。

然後在您的裝置上安裝 KernelSU 管理員並開啟它,如果它顯示 不支援,那麼您的裝置沒有官方支援的開箱即用的 Boot 映像;但您可以自行建置核心來源並整合 KernelSU 以繼續使用。

KernelSU 是否需要解鎖 Bootloader?

當然需要。

KernelSU 是否支援模組?

支援,但它是早期版本,可能存在問題。請等候它逐漸穩定 😃

KernelSU 是否支援 Xposed ?

支援。DreamlandTaiChi 可以正常運作。LSPosed 可以在 ZygiskNext 的支援下正常運作。

KernelSU 支援 Zygisk 嗎?

KernelSU 沒有內建 Zygisk 支援,但是您可以用 ZygiskNext 來使用 Zygisk 模組。

KernelSU 與 Magisk 相容嗎?

KernelSU 的模組系統與 Magisk 的 magic mount 存在衝突,如果在 KernelSU 中啟用了任何模組,那麼整個 Magisk 將無法正常運作。

但是如果您只使用 KernelSU 的 su,那么它會和 Magisk 一同運作:KernelSU 修改 kernel 、 Magisk 修改 ramdisk,它們可以搭配使用。

KernelSU 会取代 Magisk 嗎?

我們不這樣認為,這也不是我們的目標。Magisk 對於使用者空間 Root 解決方案來說已經足夠優秀了,它會存在很長一段時間。KernelSU 的目標是為使用者提供核心介面,而非取代 Magisk。

KernelSU 可以支援非 GKI 裝置嗎?

可以。但是您應該下載核心來源並整合 KernelSU 至來源樹狀結構並自行編譯核心。

KernelSU 支援 Android 12 以下的裝置嗎?

影響 KernelSU 相容性的是裝置的核心版本,它與 Android 版本並無直接關係。唯一有關聯的是:原廠 Android 12 的裝置,一定是 5.10 或更高的核心 (GKI 裝置);因此結論如下:

  1. 原廠 Android 12 的裝置必定支援 (GKI 裝置)
  2. 舊版核心的裝置 (即使是 Android 12,也可能是舊版核心) 是相容的 (您需要自行建置核心)

KernelSU 可以支援舊版核心嗎?

可以,目前最低支援到 4.14;更低的版本您需要手動移植它,歡迎 PR!

如何為舊版核心整合 KernelSU?

請參閱指南

為何我的 Android 版本為 13,但核心版本卻是 "android12-5.10"?

核心版本與 Android 版本無關,如果您要刷新 KernelSU,請一律使用核心版本而非 Android 版本,如果你為 "android12-5.10" 的裝置刷新 Android 13 的核心,等候您的將會是開機迴圈。

KernelSU 支援 --mount-master/全域掛接命名空間嗎?

目前沒有 (未來可能會支援),但實際上有很多種方法手動進入全域命名空間,無需 Su 內建支援,比如:

  1. nsenter -t 1 -m sh 可以取得一個全域 mount namespace 的 shell.
  2. 在您要執行的命令前新增 nsenter --mount=/proc/1/ns/mnt 即可使此命令在全域 mount namespace 下執行。KernelSU 本身也使用了 這種方法

我是 GKI1.0,能用 KernelSU 嗎?

GKI1 與 GKI2 完全不同,所以您需要自行編譯核心。

係依據 GPL3 授權發行。

- +
Skip to content

常見問題

KernelSU 是否支援我的裝置?

首先,您的裝置應該能解鎖 Bootloader。如果不能,則不支援。

然後在您的裝置上安裝 KernelSU 管理員並開啟它,如果它顯示 不支援,那麼您的裝置沒有官方支援的開箱即用的 Boot 映像;但您可以自行建置核心來源並整合 KernelSU 以繼續使用。

KernelSU 是否需要解鎖 Bootloader?

當然需要。

KernelSU 是否支援模組?

支援,但它是早期版本,可能存在問題。請等候它逐漸穩定 😃

KernelSU 是否支援 Xposed ?

支援。DreamlandTaiChi 可以正常運作。LSPosed 可以在 ZygiskNext 的支援下正常運作。

KernelSU 支援 Zygisk 嗎?

KernelSU 沒有內建 Zygisk 支援,但是您可以用 ZygiskNext 來使用 Zygisk 模組。

KernelSU 與 Magisk 相容嗎?

KernelSU 的模組系統與 Magisk 的 magic mount 存在衝突,如果在 KernelSU 中啟用了任何模組,那麼整個 Magisk 將無法正常運作。

但是如果您只使用 KernelSU 的 su,那么它會和 Magisk 一同運作:KernelSU 修改 kernel 、 Magisk 修改 ramdisk,它們可以搭配使用。

KernelSU 会取代 Magisk 嗎?

我們不這樣認為,這也不是我們的目標。Magisk 對於使用者空間 Root 解決方案來說已經足夠優秀了,它會存在很長一段時間。KernelSU 的目標是為使用者提供核心介面,而非取代 Magisk。

KernelSU 可以支援非 GKI 裝置嗎?

可以。但是您應該下載核心來源並整合 KernelSU 至來源樹狀結構並自行編譯核心。

KernelSU 支援 Android 12 以下的裝置嗎?

影響 KernelSU 相容性的是裝置的核心版本,它與 Android 版本並無直接關係。唯一有關聯的是:原廠 Android 12 的裝置,一定是 5.10 或更高的核心 (GKI 裝置);因此結論如下:

  1. 原廠 Android 12 的裝置必定支援 (GKI 裝置)
  2. 舊版核心的裝置 (即使是 Android 12,也可能是舊版核心) 是相容的 (您需要自行建置核心)

KernelSU 可以支援舊版核心嗎?

可以,目前最低支援到 4.14;更低的版本您需要手動移植它,歡迎 PR!

如何為舊版核心整合 KernelSU?

請參閱指南

為何我的 Android 版本為 13,但核心版本卻是 "android12-5.10"?

核心版本與 Android 版本無關,如果您要刷新 KernelSU,請一律使用核心版本而非 Android 版本,如果你為 "android12-5.10" 的裝置刷新 Android 13 的核心,等候您的將會是開機迴圈。

KernelSU 支援 --mount-master/全域掛接命名空間嗎?

目前沒有 (未來可能會支援),但實際上有很多種方法手動進入全域命名空間,無需 Su 內建支援,比如:

  1. nsenter -t 1 -m sh 可以取得一個全域 mount namespace 的 shell.
  2. 在您要執行的命令前新增 nsenter --mount=/proc/1/ns/mnt 即可使此命令在全域 mount namespace 下執行。KernelSU 本身也使用了 這種方法

我是 GKI1.0,能用 KernelSU 嗎?

GKI1 與 GKI2 完全不同,所以您需要自行編譯核心。

係依據 GPL3 授權發行。

+ \ No newline at end of file diff --git a/zh_TW/guide/hidden-features.html b/zh_TW/guide/hidden-features.html index 40f0dd6dfa1b..ea63427c4b29 100644 --- a/zh_TW/guide/hidden-features.html +++ b/zh_TW/guide/hidden-features.html @@ -5,18 +5,19 @@ 隱藏功能 | KernelSU - - + + + - - - - + + + + + -
Skip to content
On this page

隱藏功能

ksurc

預設狀況下,/system/bin/sh 會載入 /system/etc/mkshrc

可以透過建立 /data/adb/ksu/.ksurc 檔案來讓 Su 載入此檔案而非 /system/etc/mkshrc

係依據 GPL3 授權發行。

- +
Skip to content

隱藏功能

ksurc

預設狀況下,/system/bin/sh 會載入 /system/etc/mkshrc

可以透過建立 /data/adb/ksu/.ksurc 檔案來讓 Su 載入此檔案而非 /system/etc/mkshrc

係依據 GPL3 授權發行。

+ \ No newline at end of file diff --git a/zh_TW/guide/how-to-build.html b/zh_TW/guide/how-to-build.html index 48387c0c0ed0..aaa60baade78 100644 --- a/zh_TW/guide/how-to-build.html +++ b/zh_TW/guide/how-to-build.html @@ -5,21 +5,25 @@ 如何建置 KernelSU? | KernelSU - - + + + - - - - + + + + + -
Skip to content
On this page

如何建置 KernelSU?

首先,您需要閱讀核心建置的 Android 官方文件:

  1. 建置核心
  2. 標準核心映像 (GKI) 發行組建

WARNING

此文件適用於 GKI 裝置,如果您是舊版核心,請參閱如何為非 GKI 裝置整合 KernelSU

建置核心

同步核心原始碼

sh
repo init -u https://android.googlesource.com/kernel/manifest
-mv <kernel_manifest.xml> .repo/manifests
-repo init -m manifest.xml
-repo sync

<kernel_manifest.xml> 是一個可以唯一確定組建的資訊清單檔案,您可以使用這個資訊清單進行可重新預測的組建。您需要從標準核心映像 (GKI) 發行組建 下載資訊清單檔案

建置

請先查看官方文件

例如,我們需要建置 aarch64 核心映像:

sh
LTO=thin BUILD_CONFIG=common/build.config.gki.aarch64 build/build.sh

不要忘記新增 LTO=thin,否則,如果您的電腦記憶體小於 24GB,建置可能會失敗。

從 Android 13 開始,核心由 bazel 建置:

sh
tools/bazel build --config=fast //common:kernel_aarch64_dist

使用 KernelSU 建置核心

如果您可以成功建置核心,那麼建置 KernelSU 就會非常輕鬆,依自己的需求在核心原始碼根目錄中執行以下任一命令:

  • 最新 tag (穩定版本)
sh
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -
  • main 分支 (開發版本)
sh
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -s main
  • 選取 tag (例如 v0.5.2)
sh
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -s v0.5.2

然後重新建置核心,您將會得到一個帶有 KernelSU 的核心映像!

係依據 GPL3 授權發行。

- +
Skip to content

如何建置 KernelSU?

首先,您需要閱讀核心建置的 Android 官方文件:

  1. 建置核心
  2. 標準核心映像 (GKI) 發行組建

WARNING

此文件適用於 GKI 裝置,如果您是舊版核心,請參閱如何為非 GKI 裝置整合 KernelSU

建置核心

同步核心原始碼

sh
repo init -u https://android.googlesource.com/kernel/manifest
+mv <kernel_manifest.xml> .repo/manifests
+repo init -m manifest.xml
+repo sync
repo init -u https://android.googlesource.com/kernel/manifest
+mv <kernel_manifest.xml> .repo/manifests
+repo init -m manifest.xml
+repo sync

<kernel_manifest.xml> 是一個可以唯一確定組建的資訊清單檔案,您可以使用這個資訊清單進行可重新預測的組建。您需要從標準核心映像 (GKI) 發行組建 下載資訊清單檔案

建置

請先查看官方文件

例如,我們需要建置 aarch64 核心映像:

sh
LTO=thin BUILD_CONFIG=common/build.config.gki.aarch64 build/build.sh
LTO=thin BUILD_CONFIG=common/build.config.gki.aarch64 build/build.sh

不要忘記新增 LTO=thin,否則,如果您的電腦記憶體小於 24GB,建置可能會失敗。

從 Android 13 開始,核心由 bazel 建置:

sh
tools/bazel build --config=fast //common:kernel_aarch64_dist
tools/bazel build --config=fast //common:kernel_aarch64_dist

使用 KernelSU 建置核心

如果您可以成功建置核心,那麼建置 KernelSU 就會非常輕鬆,依自己的需求在核心原始碼根目錄中執行以下任一命令:

  • 最新 tag (穩定版本)
sh
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -
  • main 分支 (開發版本)
sh
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -s main
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -s main
  • 選取 tag (例如 v0.5.2)
sh
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -s v0.5.2
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -s v0.5.2

然後重新建置核心,您將會得到一個帶有 KernelSU 的核心映像!

係依據 GPL3 授權發行。

+ \ No newline at end of file diff --git a/zh_TW/guide/how-to-integrate-for-non-gki.html b/zh_TW/guide/how-to-integrate-for-non-gki.html index 684d89dd1f3b..a77d84d27bdc 100644 --- a/zh_TW/guide/how-to-integrate-for-non-gki.html +++ b/zh_TW/guide/how-to-integrate-for-non-gki.html @@ -5,169 +5,321 @@ 如何為非 GKI 核心整合 KernelSU | KernelSU - - + + + - - - - + + + + + -
Skip to content
On this page

如何為非 GKI 核心整合 KernelSU

KernelSU 可以被整合到非 GKI 核心中,現在它最低支援到核心 4.14 版本;理論上也可以支援更低的版本。

由於非 GKI 核心的片段化極其嚴重,因此通常沒有統一的方法來建置它,所以我們也無法為非 GKI 裝置提供 Boot 映像。但您完全可以自行整合 KernelSU 並建置核心以繼續使用。

首先,您必須有能力從您裝置的核心原始碼建置出一個可以開機並且能夠正常使用的核心,如果核心並非開放原始碼,這通常難以做到。

如果您已經做好了上述準備,那有兩個方法來將 KernelSU 整合至您的核心之中。

  1. 藉助 kprobe 自動整合
  2. 手動修改核心原始碼

使用 kprobe 整合

KernelSU 使用 kprobe 機制來處理核心的相關 hook,如果 kprobe 可以在您建置的核心中正常運作,那麼建議使用這個方法進行整合。

首先,把 KernelSU 新增至您的核心來源樹狀結構,再核心的根目錄執行以下命令:

  • 最新 tag (稳定版本)
sh
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -
  • main 分支(開發版本)
sh
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -s main
  • 選取 tag (例如 v0.5.2)
sh
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -s v0.5.2

然後,您需要檢查您的核心是否啟用 kprobe 相關組態,如果未啟用,則需要新增以下組態:

CONFIG_KPROBES=y
-CONFIG_HAVE_KPROBES=y
-CONFIG_KPROBE_EVENTS=y

最後,重新建置您的核心即可。

如果您發現 KPROBES 仍未生效,很有可能是因為它的相依性 CONFIG_MODULES 並未被啟用 (如果還是未生效請輸入 make menuconfig 搜尋 KPROBES 的其他相依性並啟用)

如果您在整合 KernelSU 之後手機無法啟動,那麼很可能您的核心中 kprobe 無法正常運作,您需要修正這個錯誤,或者使用第二種方法。

如何檢查 kprobe 是否損毀?

KernelSU/kernel/ksu.c 中的 ksu_enable_sucompat()ksu_enable_ksud() 取消註解,如果正常開機,即 kprobe 已損毀;或者您可以手動嘗試使用 kprobe 功能,如果不正常,手機會直接重新啟動。

手動修改核心原始碼

如果 kprobe 無法正常運作 (可能是上游的錯誤或核心版本過低),那您可以嘗試這種方法:

首先,將 KernelSU 新增至您的原始碼樹狀結構,再核心的根目錄執行以下命令:

sh
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -

然後,手動修改核心原始碼,您可以參閱下方的 patch:

diff
diff --git a/fs/exec.c b/fs/exec.c
-index ac59664eaecf..bdd585e1d2cc 100644
---- a/fs/exec.c
-+++ b/fs/exec.c
-@@ -1890,11 +1890,14 @@ static int __do_execve_file(int fd, struct filename *filename,
- 	return retval;
- }
- 
-+extern bool ksu_execveat_hook __read_mostly;
-+extern int ksu_handle_execveat(int *fd, struct filename **filename_ptr, void *argv,
-+			void *envp, int *flags);
-+extern int ksu_handle_execveat_sucompat(int *fd, struct filename **filename_ptr,
-+				 void *argv, void *envp, int *flags);
- static int do_execveat_common(int fd, struct filename *filename,
- 			      struct user_arg_ptr argv,
- 			      struct user_arg_ptr envp,
- 			      int flags)
- {
-+	if (unlikely(ksu_execveat_hook))
-+		ksu_handle_execveat(&fd, &filename, &argv, &envp, &flags);
-+	else
-+		ksu_handle_execveat_sucompat(&fd, &filename, &argv, &envp, &flags);
- 	return __do_execve_file(fd, filename, argv, envp, flags, NULL);
- }
- 
-diff --git a/fs/open.c b/fs/open.c
-index 05036d819197..965b84d486b8 100644
---- a/fs/open.c
-+++ b/fs/open.c
-@@ -348,6 +348,8 @@ SYSCALL_DEFINE4(fallocate, int, fd, int, mode, loff_t, offset, loff_t, len)
- 	return ksys_fallocate(fd, mode, offset, len);
- }
- 
-+extern int ksu_handle_faccessat(int *dfd, const char __user **filename_user, int *mode,
-+			 int *flags);
- /*
-  * access() needs to use the real uid/gid, not the effective uid/gid.
-  * We do this by temporarily clearing all FS-related capabilities and
-@@ -355,6 +357,7 @@ SYSCALL_DEFINE4(fallocate, int, fd, int, mode, loff_t, offset, loff_t, len)
-  */
- long do_faccessat(int dfd, const char __user *filename, int mode)
- {
-+	ksu_handle_faccessat(&dfd, &filename, &mode, NULL);
- 	const struct cred *old_cred;
- 	struct cred *override_cred;
- 	struct path path;
-diff --git a/fs/read_write.c b/fs/read_write.c
-index 650fc7e0f3a6..55be193913b6 100644
---- a/fs/read_write.c
-+++ b/fs/read_write.c
-@@ -434,10 +434,14 @@ ssize_t kernel_read(struct file *file, void *buf, size_t count, loff_t *pos)
- }
- EXPORT_SYMBOL(kernel_read);
- 
-+extern bool ksu_vfs_read_hook __read_mostly;
-+extern int ksu_handle_vfs_read(struct file **file_ptr, char __user **buf_ptr,
-+			size_t *count_ptr, loff_t **pos);
- ssize_t vfs_read(struct file *file, char __user *buf, size_t count, loff_t *pos)
- {
- 	ssize_t ret;
- 
-+	if (unlikely(ksu_vfs_read_hook))
-+		ksu_handle_vfs_read(&file, &buf, &count, &pos);
-+
- 	if (!(file->f_mode & FMODE_READ))
- 		return -EBADF;
- 	if (!(file->f_mode & FMODE_CAN_READ))
-diff --git a/fs/stat.c b/fs/stat.c
-index 376543199b5a..82adcef03ecc 100644
---- a/fs/stat.c
-+++ b/fs/stat.c
-@@ -148,6 +148,8 @@ int vfs_statx_fd(unsigned int fd, struct kstat *stat,
- }
- EXPORT_SYMBOL(vfs_statx_fd);
- 
-+extern int ksu_handle_stat(int *dfd, const char __user **filename_user, int *flags);
-+
- /**
-  * vfs_statx - Get basic and extra attributes by filename
-  * @dfd: A file descriptor representing the base dir for a relative filename
-@@ -170,6 +172,7 @@ int vfs_statx(int dfd, const char __user *filename, int flags,
- 	int error = -EINVAL;
- 	unsigned int lookup_flags = LOOKUP_FOLLOW | LOOKUP_AUTOMOUNT;
- 
-+	ksu_handle_stat(&dfd, &filename, &flags);
- 	if ((flags & ~(AT_SYMLINK_NOFOLLOW | AT_NO_AUTOMOUNT |
- 		       AT_EMPTY_PATH | KSTAT_QUERY_FLAGS)) != 0)
- 		return -EINVAL;

主要修改四個項目:

  1. do_faccessat,通常位於 fs/open.c
  2. do_execveat_common,通常位於 fs/exec.c
  3. vfs_read,通常位於 fs/read_write.c
  4. vfs_statx,通常位於 fs/stat.c

如果您的核心沒有 vfs_statx,使用 vfs_fstatat 將其取代:

diff
diff --git a/fs/stat.c b/fs/stat.c
-index 068fdbcc9e26..5348b7bb9db2 100644
---- a/fs/stat.c
-+++ b/fs/stat.c
-@@ -87,6 +87,8 @@ int vfs_fstat(unsigned int fd, struct kstat *stat)
- }
- EXPORT_SYMBOL(vfs_fstat);
- 
-+extern int ksu_handle_stat(int *dfd, const char __user **filename_user, int *flags);
-+
- int vfs_fstatat(int dfd, const char __user *filename, struct kstat *stat,
- 		int flag)
- {
-@@ -94,6 +96,8 @@ int vfs_fstatat(int dfd, const char __user *filename, struct kstat *stat,
- 	int error = -EINVAL;
- 	unsigned int lookup_flags = 0;
- 
-+	ksu_handle_stat(&dfd, &filename, &flag);
-+
- 	if ((flag & ~(AT_SYMLINK_NOFOLLOW | AT_NO_AUTOMOUNT |
- 		      AT_EMPTY_PATH)) != 0)
- 		goto out;

對於早於 4.17 的核心,如果沒有 do_faccessat,可以直接找到 faccessat 系統呼叫的定義並進行修改:

diff
diff --git a/fs/open.c b/fs/open.c
-index 2ff887661237..e758d7db7663 100644
---- a/fs/open.c
-+++ b/fs/open.c
-@@ -355,6 +355,9 @@ SYSCALL_DEFINE4(fallocate, int, fd, int, mode, loff_t, offset, loff_t, len)
- 	return error;
- }
- 
-+extern int ksu_handle_faccessat(int *dfd, const char __user **filename_user, int *mode,
-+			        int *flags);
-+
- /*
-  * access() needs to use the real uid/gid, not the effective uid/gid.
-  * We do this by temporarily clearing all FS-related capabilities and
-@@ -370,6 +373,8 @@ SYSCALL_DEFINE3(faccessat, int, dfd, const char __user *, filename, int, mode)
- 	int res;
- 	unsigned int lookup_flags = LOOKUP_FOLLOW;
- 
-+	ksu_handle_faccessat(&dfd, &filename, &mode, NULL);
-+
- 	if (mode & ~S_IRWXO)	/* where's F_OK, X_OK, W_OK, R_OK? */
- 		return -EINVAL;

若要啟用 KernelSU 內建的安全模式,您還需要修改 drivers/input/input.c 中的 input_handle_event 方法:

TIP

強烈建議啟用此功能,如果遇到開機迴圈,這將會非常有用!

diff
diff --git a/drivers/input/input.c b/drivers/input/input.c
-index 45306f9ef247..815091ebfca4 100755
---- a/drivers/input/input.c
-+++ b/drivers/input/input.c
-@@ -367,10 +367,13 @@ static int input_get_disposition(struct input_dev *dev,
- 	return disposition;
- }
- 
-+extern bool ksu_input_hook __read_mostly;
-+extern int ksu_handle_input_handle_event(unsigned int *type, unsigned int *code, int *value);
-+
- static void input_handle_event(struct input_dev *dev,
- 			       unsigned int type, unsigned int code, int value)
- {
-	int disposition = input_get_disposition(dev, type, code, &value);
-+
-+	if (unlikely(ksu_input_hook))
-+		ksu_handle_input_handle_event(&type, &code, &value);
- 
- 	if (disposition != INPUT_IGNORE_EVENT && type != EV_SYN)
- 		add_input_randomness(type, code, value);

最後,再次建置您的核心,KernelSU 將會如期運作。

係依據 GPL3 授權發行。

- +
Skip to content

如何為非 GKI 核心整合 KernelSU

KernelSU 可以被整合到非 GKI 核心中,現在它最低支援到核心 4.14 版本;理論上也可以支援更低的版本。

由於非 GKI 核心的片段化極其嚴重,因此通常沒有統一的方法來建置它,所以我們也無法為非 GKI 裝置提供 Boot 映像。但您完全可以自行整合 KernelSU 並建置核心以繼續使用。

首先,您必須有能力從您裝置的核心原始碼建置出一個可以開機並且能夠正常使用的核心,如果核心並非開放原始碼,這通常難以做到。

如果您已經做好了上述準備,那有兩個方法來將 KernelSU 整合至您的核心之中。

  1. 藉助 kprobe 自動整合
  2. 手動修改核心原始碼

使用 kprobe 整合

KernelSU 使用 kprobe 機制來處理核心的相關 hook,如果 kprobe 可以在您建置的核心中正常運作,那麼建議使用這個方法進行整合。

首先,把 KernelSU 新增至您的核心來源樹狀結構,再核心的根目錄執行以下命令:

  • 最新 tag (稳定版本)
sh
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -
  • main 分支(開發版本)
sh
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -s main
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -s main
  • 選取 tag (例如 v0.5.2)
sh
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -s v0.5.2
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -s v0.5.2

然後,您需要檢查您的核心是否啟用 kprobe 相關組態,如果未啟用,則需要新增以下組態:

CONFIG_KPROBES=y
+CONFIG_HAVE_KPROBES=y
+CONFIG_KPROBE_EVENTS=y
CONFIG_KPROBES=y
+CONFIG_HAVE_KPROBES=y
+CONFIG_KPROBE_EVENTS=y

最後,重新建置您的核心即可。

如果您發現 KPROBES 仍未生效,很有可能是因為它的相依性 CONFIG_MODULES 並未被啟用 (如果還是未生效請輸入 make menuconfig 搜尋 KPROBES 的其他相依性並啟用)

如果您在整合 KernelSU 之後手機無法啟動,那麼很可能您的核心中 kprobe 無法正常運作,您需要修正這個錯誤,或者使用第二種方法。

如何檢查 kprobe 是否損毀?

KernelSU/kernel/ksu.c 中的 ksu_enable_sucompat()ksu_enable_ksud() 取消註解,如果正常開機,即 kprobe 已損毀;或者您可以手動嘗試使用 kprobe 功能,如果不正常,手機會直接重新啟動。

手動修改核心原始碼

如果 kprobe 無法正常運作 (可能是上游的錯誤或核心版本過低),那您可以嘗試這種方法:

首先,將 KernelSU 新增至您的原始碼樹狀結構,再核心的根目錄執行以下命令:

sh
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -
curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -

然後,手動修改核心原始碼,您可以參閱下方的 patch:

diff
diff --git a/fs/exec.c b/fs/exec.c
+index ac59664eaecf..bdd585e1d2cc 100644
+--- a/fs/exec.c
++++ b/fs/exec.c
+@@ -1890,11 +1890,14 @@ static int __do_execve_file(int fd, struct filename *filename,
+ 	return retval;
+ }
+ 
++extern bool ksu_execveat_hook __read_mostly;
++extern int ksu_handle_execveat(int *fd, struct filename **filename_ptr, void *argv,
++			void *envp, int *flags);
++extern int ksu_handle_execveat_sucompat(int *fd, struct filename **filename_ptr,
++				 void *argv, void *envp, int *flags);
+ static int do_execveat_common(int fd, struct filename *filename,
+ 			      struct user_arg_ptr argv,
+ 			      struct user_arg_ptr envp,
+ 			      int flags)
+ {
++	if (unlikely(ksu_execveat_hook))
++		ksu_handle_execveat(&fd, &filename, &argv, &envp, &flags);
++	else
++		ksu_handle_execveat_sucompat(&fd, &filename, &argv, &envp, &flags);
+ 	return __do_execve_file(fd, filename, argv, envp, flags, NULL);
+ }
+ 
+diff --git a/fs/open.c b/fs/open.c
+index 05036d819197..965b84d486b8 100644
+--- a/fs/open.c
++++ b/fs/open.c
+@@ -348,6 +348,8 @@ SYSCALL_DEFINE4(fallocate, int, fd, int, mode, loff_t, offset, loff_t, len)
+ 	return ksys_fallocate(fd, mode, offset, len);
+ }
+ 
++extern int ksu_handle_faccessat(int *dfd, const char __user **filename_user, int *mode,
++			 int *flags);
+ /*
+  * access() needs to use the real uid/gid, not the effective uid/gid.
+  * We do this by temporarily clearing all FS-related capabilities and
+@@ -355,6 +357,7 @@ SYSCALL_DEFINE4(fallocate, int, fd, int, mode, loff_t, offset, loff_t, len)
+  */
+ long do_faccessat(int dfd, const char __user *filename, int mode)
+ {
++	ksu_handle_faccessat(&dfd, &filename, &mode, NULL);
+ 	const struct cred *old_cred;
+ 	struct cred *override_cred;
+ 	struct path path;
+diff --git a/fs/read_write.c b/fs/read_write.c
+index 650fc7e0f3a6..55be193913b6 100644
+--- a/fs/read_write.c
++++ b/fs/read_write.c
+@@ -434,10 +434,14 @@ ssize_t kernel_read(struct file *file, void *buf, size_t count, loff_t *pos)
+ }
+ EXPORT_SYMBOL(kernel_read);
+ 
++extern bool ksu_vfs_read_hook __read_mostly;
++extern int ksu_handle_vfs_read(struct file **file_ptr, char __user **buf_ptr,
++			size_t *count_ptr, loff_t **pos);
+ ssize_t vfs_read(struct file *file, char __user *buf, size_t count, loff_t *pos)
+ {
+ 	ssize_t ret;
+ 
++	if (unlikely(ksu_vfs_read_hook))
++		ksu_handle_vfs_read(&file, &buf, &count, &pos);
++
+ 	if (!(file->f_mode & FMODE_READ))
+ 		return -EBADF;
+ 	if (!(file->f_mode & FMODE_CAN_READ))
+diff --git a/fs/stat.c b/fs/stat.c
+index 376543199b5a..82adcef03ecc 100644
+--- a/fs/stat.c
++++ b/fs/stat.c
+@@ -148,6 +148,8 @@ int vfs_statx_fd(unsigned int fd, struct kstat *stat,
+ }
+ EXPORT_SYMBOL(vfs_statx_fd);
+ 
++extern int ksu_handle_stat(int *dfd, const char __user **filename_user, int *flags);
++
+ /**
+  * vfs_statx - Get basic and extra attributes by filename
+  * @dfd: A file descriptor representing the base dir for a relative filename
+@@ -170,6 +172,7 @@ int vfs_statx(int dfd, const char __user *filename, int flags,
+ 	int error = -EINVAL;
+ 	unsigned int lookup_flags = LOOKUP_FOLLOW | LOOKUP_AUTOMOUNT;
+ 
++	ksu_handle_stat(&dfd, &filename, &flags);
+ 	if ((flags & ~(AT_SYMLINK_NOFOLLOW | AT_NO_AUTOMOUNT |
+ 		       AT_EMPTY_PATH | KSTAT_QUERY_FLAGS)) != 0)
+ 		return -EINVAL;
diff --git a/fs/exec.c b/fs/exec.c
+index ac59664eaecf..bdd585e1d2cc 100644
+--- a/fs/exec.c
++++ b/fs/exec.c
+@@ -1890,11 +1890,14 @@ static int __do_execve_file(int fd, struct filename *filename,
+ 	return retval;
+ }
+ 
++extern bool ksu_execveat_hook __read_mostly;
++extern int ksu_handle_execveat(int *fd, struct filename **filename_ptr, void *argv,
++			void *envp, int *flags);
++extern int ksu_handle_execveat_sucompat(int *fd, struct filename **filename_ptr,
++				 void *argv, void *envp, int *flags);
+ static int do_execveat_common(int fd, struct filename *filename,
+ 			      struct user_arg_ptr argv,
+ 			      struct user_arg_ptr envp,
+ 			      int flags)
+ {
++	if (unlikely(ksu_execveat_hook))
++		ksu_handle_execveat(&fd, &filename, &argv, &envp, &flags);
++	else
++		ksu_handle_execveat_sucompat(&fd, &filename, &argv, &envp, &flags);
+ 	return __do_execve_file(fd, filename, argv, envp, flags, NULL);
+ }
+ 
+diff --git a/fs/open.c b/fs/open.c
+index 05036d819197..965b84d486b8 100644
+--- a/fs/open.c
++++ b/fs/open.c
+@@ -348,6 +348,8 @@ SYSCALL_DEFINE4(fallocate, int, fd, int, mode, loff_t, offset, loff_t, len)
+ 	return ksys_fallocate(fd, mode, offset, len);
+ }
+ 
++extern int ksu_handle_faccessat(int *dfd, const char __user **filename_user, int *mode,
++			 int *flags);
+ /*
+  * access() needs to use the real uid/gid, not the effective uid/gid.
+  * We do this by temporarily clearing all FS-related capabilities and
+@@ -355,6 +357,7 @@ SYSCALL_DEFINE4(fallocate, int, fd, int, mode, loff_t, offset, loff_t, len)
+  */
+ long do_faccessat(int dfd, const char __user *filename, int mode)
+ {
++	ksu_handle_faccessat(&dfd, &filename, &mode, NULL);
+ 	const struct cred *old_cred;
+ 	struct cred *override_cred;
+ 	struct path path;
+diff --git a/fs/read_write.c b/fs/read_write.c
+index 650fc7e0f3a6..55be193913b6 100644
+--- a/fs/read_write.c
++++ b/fs/read_write.c
+@@ -434,10 +434,14 @@ ssize_t kernel_read(struct file *file, void *buf, size_t count, loff_t *pos)
+ }
+ EXPORT_SYMBOL(kernel_read);
+ 
++extern bool ksu_vfs_read_hook __read_mostly;
++extern int ksu_handle_vfs_read(struct file **file_ptr, char __user **buf_ptr,
++			size_t *count_ptr, loff_t **pos);
+ ssize_t vfs_read(struct file *file, char __user *buf, size_t count, loff_t *pos)
+ {
+ 	ssize_t ret;
+ 
++	if (unlikely(ksu_vfs_read_hook))
++		ksu_handle_vfs_read(&file, &buf, &count, &pos);
++
+ 	if (!(file->f_mode & FMODE_READ))
+ 		return -EBADF;
+ 	if (!(file->f_mode & FMODE_CAN_READ))
+diff --git a/fs/stat.c b/fs/stat.c
+index 376543199b5a..82adcef03ecc 100644
+--- a/fs/stat.c
++++ b/fs/stat.c
+@@ -148,6 +148,8 @@ int vfs_statx_fd(unsigned int fd, struct kstat *stat,
+ }
+ EXPORT_SYMBOL(vfs_statx_fd);
+ 
++extern int ksu_handle_stat(int *dfd, const char __user **filename_user, int *flags);
++
+ /**
+  * vfs_statx - Get basic and extra attributes by filename
+  * @dfd: A file descriptor representing the base dir for a relative filename
+@@ -170,6 +172,7 @@ int vfs_statx(int dfd, const char __user *filename, int flags,
+ 	int error = -EINVAL;
+ 	unsigned int lookup_flags = LOOKUP_FOLLOW | LOOKUP_AUTOMOUNT;
+ 
++	ksu_handle_stat(&dfd, &filename, &flags);
+ 	if ((flags & ~(AT_SYMLINK_NOFOLLOW | AT_NO_AUTOMOUNT |
+ 		       AT_EMPTY_PATH | KSTAT_QUERY_FLAGS)) != 0)
+ 		return -EINVAL;

主要修改四個項目:

  1. do_faccessat,通常位於 fs/open.c
  2. do_execveat_common,通常位於 fs/exec.c
  3. vfs_read,通常位於 fs/read_write.c
  4. vfs_statx,通常位於 fs/stat.c

如果您的核心沒有 vfs_statx,使用 vfs_fstatat 將其取代:

diff
diff --git a/fs/stat.c b/fs/stat.c
+index 068fdbcc9e26..5348b7bb9db2 100644
+--- a/fs/stat.c
++++ b/fs/stat.c
+@@ -87,6 +87,8 @@ int vfs_fstat(unsigned int fd, struct kstat *stat)
+ }
+ EXPORT_SYMBOL(vfs_fstat);
+ 
++extern int ksu_handle_stat(int *dfd, const char __user **filename_user, int *flags);
++
+ int vfs_fstatat(int dfd, const char __user *filename, struct kstat *stat,
+ 		int flag)
+ {
+@@ -94,6 +96,8 @@ int vfs_fstatat(int dfd, const char __user *filename, struct kstat *stat,
+ 	int error = -EINVAL;
+ 	unsigned int lookup_flags = 0;
+ 
++	ksu_handle_stat(&dfd, &filename, &flag);
++
+ 	if ((flag & ~(AT_SYMLINK_NOFOLLOW | AT_NO_AUTOMOUNT |
+ 		      AT_EMPTY_PATH)) != 0)
+ 		goto out;
diff --git a/fs/stat.c b/fs/stat.c
+index 068fdbcc9e26..5348b7bb9db2 100644
+--- a/fs/stat.c
++++ b/fs/stat.c
+@@ -87,6 +87,8 @@ int vfs_fstat(unsigned int fd, struct kstat *stat)
+ }
+ EXPORT_SYMBOL(vfs_fstat);
+ 
++extern int ksu_handle_stat(int *dfd, const char __user **filename_user, int *flags);
++
+ int vfs_fstatat(int dfd, const char __user *filename, struct kstat *stat,
+ 		int flag)
+ {
+@@ -94,6 +96,8 @@ int vfs_fstatat(int dfd, const char __user *filename, struct kstat *stat,
+ 	int error = -EINVAL;
+ 	unsigned int lookup_flags = 0;
+ 
++	ksu_handle_stat(&dfd, &filename, &flag);
++
+ 	if ((flag & ~(AT_SYMLINK_NOFOLLOW | AT_NO_AUTOMOUNT |
+ 		      AT_EMPTY_PATH)) != 0)
+ 		goto out;

對於早於 4.17 的核心,如果沒有 do_faccessat,可以直接找到 faccessat 系統呼叫的定義並進行修改:

diff
diff --git a/fs/open.c b/fs/open.c
+index 2ff887661237..e758d7db7663 100644
+--- a/fs/open.c
++++ b/fs/open.c
+@@ -355,6 +355,9 @@ SYSCALL_DEFINE4(fallocate, int, fd, int, mode, loff_t, offset, loff_t, len)
+ 	return error;
+ }
+ 
++extern int ksu_handle_faccessat(int *dfd, const char __user **filename_user, int *mode,
++			        int *flags);
++
+ /*
+  * access() needs to use the real uid/gid, not the effective uid/gid.
+  * We do this by temporarily clearing all FS-related capabilities and
+@@ -370,6 +373,8 @@ SYSCALL_DEFINE3(faccessat, int, dfd, const char __user *, filename, int, mode)
+ 	int res;
+ 	unsigned int lookup_flags = LOOKUP_FOLLOW;
+ 
++	ksu_handle_faccessat(&dfd, &filename, &mode, NULL);
++
+ 	if (mode & ~S_IRWXO)	/* where's F_OK, X_OK, W_OK, R_OK? */
+ 		return -EINVAL;
diff --git a/fs/open.c b/fs/open.c
+index 2ff887661237..e758d7db7663 100644
+--- a/fs/open.c
++++ b/fs/open.c
+@@ -355,6 +355,9 @@ SYSCALL_DEFINE4(fallocate, int, fd, int, mode, loff_t, offset, loff_t, len)
+ 	return error;
+ }
+ 
++extern int ksu_handle_faccessat(int *dfd, const char __user **filename_user, int *mode,
++			        int *flags);
++
+ /*
+  * access() needs to use the real uid/gid, not the effective uid/gid.
+  * We do this by temporarily clearing all FS-related capabilities and
+@@ -370,6 +373,8 @@ SYSCALL_DEFINE3(faccessat, int, dfd, const char __user *, filename, int, mode)
+ 	int res;
+ 	unsigned int lookup_flags = LOOKUP_FOLLOW;
+ 
++	ksu_handle_faccessat(&dfd, &filename, &mode, NULL);
++
+ 	if (mode & ~S_IRWXO)	/* where's F_OK, X_OK, W_OK, R_OK? */
+ 		return -EINVAL;

若要啟用 KernelSU 內建的安全模式,您還需要修改 drivers/input/input.c 中的 input_handle_event 方法:

TIP

強烈建議啟用此功能,如果遇到開機迴圈,這將會非常有用!

diff
diff --git a/drivers/input/input.c b/drivers/input/input.c
+index 45306f9ef247..815091ebfca4 100755
+--- a/drivers/input/input.c
++++ b/drivers/input/input.c
+@@ -367,10 +367,13 @@ static int input_get_disposition(struct input_dev *dev,
+ 	return disposition;
+ }
+ 
++extern bool ksu_input_hook __read_mostly;
++extern int ksu_handle_input_handle_event(unsigned int *type, unsigned int *code, int *value);
++
+ static void input_handle_event(struct input_dev *dev,
+ 			       unsigned int type, unsigned int code, int value)
+ {
+	int disposition = input_get_disposition(dev, type, code, &value);
++
++	if (unlikely(ksu_input_hook))
++		ksu_handle_input_handle_event(&type, &code, &value);
+ 
+ 	if (disposition != INPUT_IGNORE_EVENT && type != EV_SYN)
+ 		add_input_randomness(type, code, value);
diff --git a/drivers/input/input.c b/drivers/input/input.c
+index 45306f9ef247..815091ebfca4 100755
+--- a/drivers/input/input.c
++++ b/drivers/input/input.c
+@@ -367,10 +367,13 @@ static int input_get_disposition(struct input_dev *dev,
+ 	return disposition;
+ }
+ 
++extern bool ksu_input_hook __read_mostly;
++extern int ksu_handle_input_handle_event(unsigned int *type, unsigned int *code, int *value);
++
+ static void input_handle_event(struct input_dev *dev,
+ 			       unsigned int type, unsigned int code, int value)
+ {
+	int disposition = input_get_disposition(dev, type, code, &value);
++
++	if (unlikely(ksu_input_hook))
++		ksu_handle_input_handle_event(&type, &code, &value);
+ 
+ 	if (disposition != INPUT_IGNORE_EVENT && type != EV_SYN)
+ 		add_input_randomness(type, code, value);

最後,再次建置您的核心,KernelSU 將會如期運作。

係依據 GPL3 授權發行。

+ \ No newline at end of file diff --git a/zh_TW/guide/installation.html b/zh_TW/guide/installation.html index 87094fd6ef0e..5b981920599a 100644 --- a/zh_TW/guide/installation.html +++ b/zh_TW/guide/installation.html @@ -5,20 +5,23 @@ 安裝 | KernelSU - - + + + - - - - + + + + + -
Skip to content
On this page

安裝

檢查您的裝置是否受支援

GitHub Releases酷安 下載 KernelSU 管理員應用程式,然後將應用程式安裝至裝置並開啟:

  • 如果應用程式顯示「不支援」,則表示您的裝置不支援 KernelSU,您需要自行編譯核心才能繼續使用,,KernelSU 官方也永遠不會為您提供一個可以刷新的 Boot 映像。
  • 如果應用程式顯示「未安裝」,那麼 KernelSU 支援您的裝置;可以進行下一步作業。

INFO

對於顯示「不支援」的裝置,這裡有一個非官方支援裝置清單,您可以使用這個清單裡的核心自行編譯。

備份您的原廠 boot.img

在進行刷新作業前,您必須預先備份您的原廠 boot.img。如果您在後續刷新作業中出現了任何問題,您都可以透過使用 Fastboot 刷新回到原廠 Boot 以還原系統。

WARNING

刷新作業可能會造成資料遺失,請確保做好這一步再繼續進行下一步作業!!必要時您還可以備份您手機的所有資料。

必要知識

ADB 和 Fastboot

預設狀況下,您將會使用 ADB 和 Fastboot 工具,如果您不知道它們,建議使用搜尋引擎先瞭解相關內容。

KMI

KMI 全稱 Kernel Module Interface,相同 KMI 的核心版本是相容的 這也是 GKI 中「標準」的涵義所在;反之,如果 KMI 不同,那麼這些核心之間無法彼此相容,刷新與您裝置 KMI 不同的核心映像可能會導致開機迴圈。

具體來講,對 GKI 的裝置,其核心版本格式應該如下:

txt
KernelRelease :=
-Version.PatchLevel.SubLevel-AndroidRelease-KmiGeneration-suffix
-w      .x         .y       -zzz           -k            -something

其中,w.x-zzz-k 為 KMI 版本。例如,一部裝置核心版本為 5.10.101-android12-9-g30979850fc20,那麼它的 KMI 為 5.10-android12-9;理論上刷新其他這個 KMI 的核心也能正常開機。

TIP

請注意,核心版本中的 SubLevel 並非 KMI 的一部分!也就是說 5.10.101-android12-9-g30979850fc205.10.137-android12-9-g30979850fc20 的 KMI 相同!

核心版本與 Android 版本

請注意:核心版本與 Android 版本並不一定相同!

如果您發現您的核心版本是 android12-5.10.101,然而您 Android 系統的版本為 Android 13 或者其他;請不要覺得奇怪,因為 Android 系統的版本與 Linux 核心的版本號碼並非一致;Linux 核心的版本號碼一般與裝置出廠時隨附的 Android 系統的版本一致,如果後續 Android 系統更新,核心版本一般不會發生變化。如果您需要刷新,請以核心版本為準!!

安裝簡介

KernelSU 的安裝方法有以下幾種,各自適用於不同的場景,請視需要選擇:

  1. 使用自訂 Recovery (如 TWRP) 安裝
  2. 使用核心刷新應用程式 (例如 Franco Kernel Manager) 安裝
  3. 使用 KernelSU 提供的 boot.img 透過 Fastboot 安裝
  4. 手動修補 boot.img 並安裝

使用自訂 Recovery 安裝

先決條件:您的裝置必須有自訂的 Recovery,例如 TWRP;如果沒有或者只有官方 Recovery,請使用其他方法。

步驟:

  1. 在 KernelSU 的 Release 頁面 下載與您手機版本相符的以 AnyKernel3 開頭的 Zip 套件;例如,手機核心版本為 android12-5.10.66,那麼您應該下載 AnyKernel3-android12-5.10.66_yyyy-MM.zip 這個檔案 (其中 yyyy 為年份,MM 為月份)。
  2. 重新開機手機至 TWRP。
  3. 使用 Adb 將 AnyKernel3-*.zip 放置到手機 /sdcard 然後在 TWRP 圖形使用者介面選擇並安裝;或者您也可以直接 adb sideload AnyKernel-*.zip 安裝。

PS. 這種方法適用於任何狀況下的安裝 (不限於初次安裝或後續更新),只要您用 TWRP 就可以進行作業。

使用核心刷新應用程式安裝

先決條件:您的裝置必須已經 Root。例如您已經安裝了 Magisk 並取得 Root 存取權,或者您已經安裝了舊版本的 KernelSU 需升級到其他版本的 KernelSU;如果您的裝置並未 Root,請嘗試其他方法。

步驟:

  1. 下載 AnyKernel3 的 Zip 檔案;請參閱 使用自訂 Recovery 安裝 章節的内容。
  2. 開啟核心刷新應用程式提供的 AnyKernel3 Zip 檔案進行刷新。

如果您先前並未使用過核心刷新應用程式,可以嘗試下面幾個方法:

  1. Kernel Flasher
  2. Franco Kernel Manager
  3. Ex Kernel Manager

PS. 這種方法在更新 KernelSU 時比較方便,無需電腦即可完成 (注意備份!)。

使用 KernelSU 提供的 boot.img 安裝

這種方法無需您有 TWRP,也無需您的手機有 Root 權限;適用於您初次安裝 KernelSU。

找到合適的 boot.img

KernelSU 為 GKI 裝置提供了標準 boot.img,您需要將 boot.img 刷新至裝置的 Boot 分割區。

您可以從 GitHub Release 下載 boot.img,請注意,您應該使用正確版本的 boot.img。例如,如果您的裝置顯示核心是 android12-5.10.101,需要下載 android-5.10.101_yyyy-MM.boot-<format>.img.

其中 <format> 指的是您的官方 boot.img 的核心壓縮格式,請檢查您原有 boot.img 的核心壓縮格式,您應該使用正確的格式,例如 lz4gz;如果使用不正確的壓縮格式,刷新 Boot 後可能無法開機。

INFO

  1. 您可以透過 magiskboot 以取得您的原始 Boot 的壓縮格式;當然,您也可以詢問與您相同型號的其他更有經驗的使用者。另外,核心的壓縮格式通常部會出現變更,如果您使用的某個壓縮格式成功開機,後續可以優先嘗試這個格式。
  2. 小米裝置通常 gz 或者 不壓縮
  3. Pixel 裝置有些特殊,請遵循下方的指示。

將 boot.img 刷新至裝置

使用 adb 連接您的裝置,然後執行 adb reboot bootloader 進入 fastboot 模式,然後使用此命令刷新 KernelSU:

sh
fastboot flash boot boot.img

INFO

如果您的裝置支援 fastboot boot,可以先使用 fastboot boot boot.img 來先嘗試使用 boot.img 開機進入系統,如果出現意外,重新啟動即可開機。

重新開機

刷新完成後,您應該重新啟動您的裝置:

sh
fastboot reboot

手動修補 boot.img

對於某些裝置來說,其 boot.img 格式並不是很常見,比如 lz4gz 和未壓縮;最典型的就是 Pixel,它的 boot.img 格式是 lz4_legacy 壓縮,ramdisk 可能是 gz 也可能是 lz4_legacy 壓縮;此時如果您直接刷新 KernelSU 提供的 boot.img,手機可能無法開機;這時,您可以透過手動修補 boot.img 來完成。

一般有兩種修補方法:

  1. Android-Image-Kitchen
  2. magiskboot

其中,Android-Image-Kitchen 適用於在電腦上作業,magiskboot 需要手機協作。

準備

  1. 取得您手機的原廠 boot.img;您可以聯絡您的裝置製造商,您也可能需要payload-dumper-go
  2. 下載 KernelSU 提供的與您的裝置 KMI 一致地 AnyKernel3 Zip 檔案 (可參閱 使用自訂 Recovery 安裝)。
  3. 解壓縮 AnyKernel3 Zip 檔案,取得其中的 Image 檔案,此檔案為 KernelSU 的核心檔案。

使用 Android-Image-Kitchen

  1. 下載 Android-Image-Kitchen 至您的電腦。
  2. 將手機原廠 boot.img 放置於 Android-Image-Kitchen 根目錄。
  3. 在 Android-Image-Kitchen 根目錄執行 ./unpackimg.sh boot.img;此命令會將 boot.img 解除封裝,您會得到一些檔案。
  4. split_img 目錄中的 boot.img-kernel 取代為您從 AnyKernel3 解壓縮出來的 Image (注意名稱變更為 boot.img-kernel)。
  5. 在 Android-Image-Kitchecn 根目錄執行 ./repackimg.sh;此時您會得到一個 image-new.img 檔案;使用此 boot.img 透過 fastboot 刷新即可 (刷新方法請參閱上一章節)。

使用 magiskboot

  1. 在 Magisk 的 Release 頁面 下載最新的 Magisk 安裝套件。
  2. 將 Magisk-*.apk 重新命名為 Magisk-vesion.zip 然後解壓縮。
  3. 將解壓縮後的 Magisk-v25.2/lib/arm64-v8a/libmagiskboot.so 檔案,使用 Adb 推入至手機:adb push Magisk-v25.2/lib/arm64-v8a/libmagiskboot.so /data/local/tmp/magiskboot
  4. 使用 Adb 將原廠 boot.img 和 AnyKernel3 中的 Image 推入至手機。
  5. adb shell 進入 /data/local/tmp/ 目錄,然後賦予先前推入檔案的可執行權限 chmod +x magiskboot
  6. adb shell 進入 /data/local/tmp/ 目錄,執行 ./magiskboot unpack boot.img 此時會將 boot.img 解除封裝,得到一個名為 kernel 的檔案,這個檔案是您的原廠核心。
  7. 使用 Image 取代 kernel: mv -f Image kernel
  8. 執行 ./magiskboot repack boot.img 重新封裝 img,此時您會得到一個 new-boot.img 檔案,透過 Fastboot 將這個檔案刷新至裝置即可。

其他替代方法

其實所有這些安裝方法的主旨只有一個,那就是將原廠核心取代為 KernelSU 提供的核心;只要能實現這個目的,就可以安裝;比如以下是其他可行的方法:

  1. 首先安裝 Magisk,透過 Magisk 取得 Root 權限後使用核心刷新程式刷新 KernelSU 的 AnyKernel Zip。
  2. 使用某些 PC 上的刷新工具組刷新 KernelSU 提供的核心。

係依據 GPL3 授權發行。

- +
Skip to content

安裝

檢查您的裝置是否受支援

GitHub Releases酷安 下載 KernelSU 管理員應用程式,然後將應用程式安裝至裝置並開啟:

  • 如果應用程式顯示「不支援」,則表示您的裝置不支援 KernelSU,您需要自行編譯核心才能繼續使用,,KernelSU 官方也永遠不會為您提供一個可以刷新的 Boot 映像。
  • 如果應用程式顯示「未安裝」,那麼 KernelSU 支援您的裝置;可以進行下一步作業。

INFO

對於顯示「不支援」的裝置,這裡有一個非官方支援裝置清單,您可以使用這個清單裡的核心自行編譯。

備份您的原廠 boot.img

在進行刷新作業前,您必須預先備份您的原廠 boot.img。如果您在後續刷新作業中出現了任何問題,您都可以透過使用 Fastboot 刷新回到原廠 Boot 以還原系統。

WARNING

刷新作業可能會造成資料遺失,請確保做好這一步再繼續進行下一步作業!!必要時您還可以備份您手機的所有資料。

必要知識

ADB 和 Fastboot

預設狀況下,您將會使用 ADB 和 Fastboot 工具,如果您不知道它們,建議使用搜尋引擎先瞭解相關內容。

KMI

KMI 全稱 Kernel Module Interface,相同 KMI 的核心版本是相容的 這也是 GKI 中「標準」的涵義所在;反之,如果 KMI 不同,那麼這些核心之間無法彼此相容,刷新與您裝置 KMI 不同的核心映像可能會導致開機迴圈。

具體來講,對 GKI 的裝置,其核心版本格式應該如下:

txt
KernelRelease :=
+Version.PatchLevel.SubLevel-AndroidRelease-KmiGeneration-suffix
+w      .x         .y       -zzz           -k            -something
KernelRelease :=
+Version.PatchLevel.SubLevel-AndroidRelease-KmiGeneration-suffix
+w      .x         .y       -zzz           -k            -something

其中,w.x-zzz-k 為 KMI 版本。例如,一部裝置核心版本為 5.10.101-android12-9-g30979850fc20,那麼它的 KMI 為 5.10-android12-9;理論上刷新其他這個 KMI 的核心也能正常開機。

TIP

請注意,核心版本中的 SubLevel 並非 KMI 的一部分!也就是說 5.10.101-android12-9-g30979850fc205.10.137-android12-9-g30979850fc20 的 KMI 相同!

核心版本與 Android 版本

請注意:核心版本與 Android 版本並不一定相同!

如果您發現您的核心版本是 android12-5.10.101,然而您 Android 系統的版本為 Android 13 或者其他;請不要覺得奇怪,因為 Android 系統的版本與 Linux 核心的版本號碼並非一致;Linux 核心的版本號碼一般與裝置出廠時隨附的 Android 系統的版本一致,如果後續 Android 系統更新,核心版本一般不會發生變化。如果您需要刷新,請以核心版本為準!!

安裝簡介

KernelSU 的安裝方法有以下幾種,各自適用於不同的場景,請視需要選擇:

  1. 使用自訂 Recovery (如 TWRP) 安裝
  2. 使用核心刷新應用程式 (例如 Franco Kernel Manager) 安裝
  3. 使用 KernelSU 提供的 boot.img 透過 Fastboot 安裝
  4. 手動修補 boot.img 並安裝

使用自訂 Recovery 安裝

先決條件:您的裝置必須有自訂的 Recovery,例如 TWRP;如果沒有或者只有官方 Recovery,請使用其他方法。

步驟:

  1. 在 KernelSU 的 Release 頁面 下載與您手機版本相符的以 AnyKernel3 開頭的 Zip 套件;例如,手機核心版本為 android12-5.10.66,那麼您應該下載 AnyKernel3-android12-5.10.66_yyyy-MM.zip 這個檔案 (其中 yyyy 為年份,MM 為月份)。
  2. 重新開機手機至 TWRP。
  3. 使用 Adb 將 AnyKernel3-*.zip 放置到手機 /sdcard 然後在 TWRP 圖形使用者介面選擇並安裝;或者您也可以直接 adb sideload AnyKernel-*.zip 安裝。

PS. 這種方法適用於任何狀況下的安裝 (不限於初次安裝或後續更新),只要您用 TWRP 就可以進行作業。

使用核心刷新應用程式安裝

先決條件:您的裝置必須已經 Root。例如您已經安裝了 Magisk 並取得 Root 存取權,或者您已經安裝了舊版本的 KernelSU 需升級到其他版本的 KernelSU;如果您的裝置並未 Root,請嘗試其他方法。

步驟:

  1. 下載 AnyKernel3 的 Zip 檔案;請參閱 使用自訂 Recovery 安裝 章節的内容。
  2. 開啟核心刷新應用程式提供的 AnyKernel3 Zip 檔案進行刷新。

如果您先前並未使用過核心刷新應用程式,可以嘗試下面幾個方法:

  1. Kernel Flasher
  2. Franco Kernel Manager
  3. Ex Kernel Manager

PS. 這種方法在更新 KernelSU 時比較方便,無需電腦即可完成 (注意備份!)。

使用 KernelSU 提供的 boot.img 安裝

這種方法無需您有 TWRP,也無需您的手機有 Root 權限;適用於您初次安裝 KernelSU。

找到合適的 boot.img

KernelSU 為 GKI 裝置提供了標準 boot.img,您需要將 boot.img 刷新至裝置的 Boot 分割區。

您可以從 GitHub Release 下載 boot.img,請注意,您應該使用正確版本的 boot.img。例如,如果您的裝置顯示核心是 android12-5.10.101,需要下載 android-5.10.101_yyyy-MM.boot-<format>.img.

其中 <format> 指的是您的官方 boot.img 的核心壓縮格式,請檢查您原有 boot.img 的核心壓縮格式,您應該使用正確的格式,例如 lz4gz;如果使用不正確的壓縮格式,刷新 Boot 後可能無法開機。

INFO

  1. 您可以透過 magiskboot 以取得您的原始 Boot 的壓縮格式;當然,您也可以詢問與您相同型號的其他更有經驗的使用者。另外,核心的壓縮格式通常部會出現變更,如果您使用的某個壓縮格式成功開機,後續可以優先嘗試這個格式。
  2. 小米裝置通常 gz 或者 不壓縮
  3. Pixel 裝置有些特殊,請遵循下方的指示。

將 boot.img 刷新至裝置

使用 adb 連接您的裝置,然後執行 adb reboot bootloader 進入 fastboot 模式,然後使用此命令刷新 KernelSU:

sh
fastboot flash boot boot.img
fastboot flash boot boot.img

INFO

如果您的裝置支援 fastboot boot,可以先使用 fastboot boot boot.img 來先嘗試使用 boot.img 開機進入系統,如果出現意外,重新啟動即可開機。

重新開機

刷新完成後,您應該重新啟動您的裝置:

sh
fastboot reboot
fastboot reboot

手動修補 boot.img

對於某些裝置來說,其 boot.img 格式並不是很常見,比如 lz4gz 和未壓縮;最典型的就是 Pixel,它的 boot.img 格式是 lz4_legacy 壓縮,ramdisk 可能是 gz 也可能是 lz4_legacy 壓縮;此時如果您直接刷新 KernelSU 提供的 boot.img,手機可能無法開機;這時,您可以透過手動修補 boot.img 來完成。

一般有兩種修補方法:

  1. Android-Image-Kitchen
  2. magiskboot

其中,Android-Image-Kitchen 適用於在電腦上作業,magiskboot 需要手機協作。

準備

  1. 取得您手機的原廠 boot.img;您可以聯絡您的裝置製造商,您也可能需要payload-dumper-go
  2. 下載 KernelSU 提供的與您的裝置 KMI 一致地 AnyKernel3 Zip 檔案 (可參閱 使用自訂 Recovery 安裝)。
  3. 解壓縮 AnyKernel3 Zip 檔案,取得其中的 Image 檔案,此檔案為 KernelSU 的核心檔案。

使用 Android-Image-Kitchen

  1. 下載 Android-Image-Kitchen 至您的電腦。
  2. 將手機原廠 boot.img 放置於 Android-Image-Kitchen 根目錄。
  3. 在 Android-Image-Kitchen 根目錄執行 ./unpackimg.sh boot.img;此命令會將 boot.img 解除封裝,您會得到一些檔案。
  4. split_img 目錄中的 boot.img-kernel 取代為您從 AnyKernel3 解壓縮出來的 Image (注意名稱變更為 boot.img-kernel)。
  5. 在 Android-Image-Kitchecn 根目錄執行 ./repackimg.sh;此時您會得到一個 image-new.img 檔案;使用此 boot.img 透過 fastboot 刷新即可 (刷新方法請參閱上一章節)。

使用 magiskboot

  1. 在 Magisk 的 Release 頁面 下載最新的 Magisk 安裝套件。
  2. 將 Magisk-*.apk 重新命名為 Magisk-vesion.zip 然後解壓縮。
  3. 將解壓縮後的 Magisk-v25.2/lib/arm64-v8a/libmagiskboot.so 檔案,使用 Adb 推入至手機:adb push Magisk-v25.2/lib/arm64-v8a/libmagiskboot.so /data/local/tmp/magiskboot
  4. 使用 Adb 將原廠 boot.img 和 AnyKernel3 中的 Image 推入至手機。
  5. adb shell 進入 /data/local/tmp/ 目錄,然後賦予先前推入檔案的可執行權限 chmod +x magiskboot
  6. adb shell 進入 /data/local/tmp/ 目錄,執行 ./magiskboot unpack boot.img 此時會將 boot.img 解除封裝,得到一個名為 kernel 的檔案,這個檔案是您的原廠核心。
  7. 使用 Image 取代 kernel: mv -f Image kernel
  8. 執行 ./magiskboot repack boot.img 重新封裝 img,此時您會得到一個 new-boot.img 檔案,透過 Fastboot 將這個檔案刷新至裝置即可。

其他替代方法

其實所有這些安裝方法的主旨只有一個,那就是將原廠核心取代為 KernelSU 提供的核心;只要能實現這個目的,就可以安裝;比如以下是其他可行的方法:

  1. 首先安裝 Magisk,透過 Magisk 取得 Root 權限後使用核心刷新程式刷新 KernelSU 的 AnyKernel Zip。
  2. 使用某些 PC 上的刷新工具組刷新 KernelSU 提供的核心。

係依據 GPL3 授權發行。

+ \ No newline at end of file diff --git a/zh_TW/guide/module.html b/zh_TW/guide/module.html index 69d35bd5f8ad..f7650ec5df6c 100644 --- a/zh_TW/guide/module.html +++ b/zh_TW/guide/module.html @@ -5,101 +5,185 @@ 模組指南 | KernelSU - - + + + - - - - + + + + + -
Skip to content
On this page

模組指南

KernelSU 提供了一個模組機制,它可以在保持系統分割區完整性的同時達到修改系統分割區的效果;這種機制一般被稱為 systemless。

KernelSU 的模組運作機制與 Magisk 幾乎相同,如果您熟悉 Magisk 模組的開發,那麼開發 KernelSU 的模組大同小異,您可以跳過下列有關模組的介紹,只需要瞭解 KernelSU 模組與 Magisk 模組的異同

Busybox

KernelSU 提供了一個完備的 BusyBox 二進位檔案 (包括完整的 SELinux 支援)。可執行檔位於 /data/adb/ksu/bin/busybox。 KernelSU 的 BusyBox 支援同時執行時可切換的 "ASH Standalone Shell Mode"。 這種讀了模式意味著在執行 BusyBox 的 ash shell 時,每個命令都會直接使用 BusyBox 中內建的應用程式,而不論 PATH 的設定為何。 例如,lsrmchmod 等命令將不會使用 PATH 中設定的命令 (在 Android 的狀況下,預設狀況下分別為 /system/bin/ls/system/bin/rm/system/bin/chmod),而是直接呼叫 BusyBox 內建的應用程式。 這確保了指令碼始終在可預測的環境中執行,並始終具有完整的命令套件,不論它執行在哪個 Android 版本上。 要強制下一個命令不使用 BusyBox,您必須使用完整路徑呼叫可執行檔。

在 KernelSU 上下文中執行的每個 shell 指令碼都將在 BusyBox 的 ash shell 中以獨立模式執行。對於第三方開發人員相關的內容,包括所有開機指令碼和模組安裝指令碼。

對於想要在 KernelSU 之外使用這個「獨立模式」功能的使用者,有兩種啟用方法:

  1. 將環境變數 ASH_STANDALONE 設為 1。例如:ASH_STANDALONE=1 /data/adb/ksu/bin/busybox sh <script>
  2. 使用命令列選項切換:/data/adb/ksu/bin/busybox sh -o standalone <script>

為了確保所有後續的 sh shell 都在獨立模式下執行,第一種是首選方法 (這也是 KernelSU 和 KernelSU 管理員內部使用的方法),因為環境變數會被繼承到子處理程序中。

與 Magisk 的差異

KernelSU 的 BusyBox 現在是直接使用 Magisk 專案編譯的二進位檔案,感謝 Magisk! 因此,您完全不必擔心 BusyBox 指令碼與在 Magisk 和 KernelSU 之間的相容性問題,因為它們完全相同!

KernelSU 模組

KernelSU 模組是一個放置於 /data/adb/modules 且滿足下列結構的資料夾:

txt
/data/adb/modules
-├── .
-├── .
-|
-├── $MODID                  <--- 模組的資料夾名稱與模組 ID 相同
-│   │
-│   │      *** 模組識別 ***
-│   │
-│   ├── module.prop         <--- 這個檔案儲存與模組相關的中繼資料,例如模組 ID、版本等
-│   │
-│   │      *** 主要內容 ***
-│   │
-│   ├── system              <--- 這個資料夾會在 skip_mount 不存在時被掛接至系統
-│   │   ├── ...
-│   │   ├── ...
-│   │   └── ...
-│   │
-│   │      *** 狀態旗標 ***
-│   │
-│   ├── skip_mount          <--- 如果這個檔案存在,那麼 KernelSU 將不會掛接您的系統資料夾
-│   ├── disable             <--- 如果這個檔案存在,那麼模組將會被停用
-│   ├── remove              <--- 如果這個檔案存在,那麼模組將會在下次重新開機時被移除
-│   │
-│   │      *** 選用檔案 ***
-│   │
-│   ├── post-fs-data.sh     <--- 這個指令碼將會在 post-fs-data 中執行
-│   ├── service.sh          <--- 這個指令碼將會在 late_start 服務中執行
-|   ├── uninstall.sh        <--- 這個指令碼將會在 KernelSU 移除模組時執行
-│   ├── system.prop         <--- 這個檔案中指定的屬性將會在系統啟動時透過 resetprop 變更
-│   ├── sepolicy.rule       <--- 這個檔案中的 SELinux 原則將會在系統開機時載入
-│   │
-│   │      *** 自動產生的目錄,不要手動建立或修改! ***
-│   │
-│   ├── vendor              <--- A symlink to $MODID/system/vendor
-│   ├── product             <--- A symlink to $MODID/system/product
-│   ├── system_ext          <--- A symlink to $MODID/system/system_ext
-│   │
-│   │      *** 允許的其他額外檔案/資料夾 ***
-│   │
-│   ├── ...
-│   └── ...
-|
-├── another_module
-│   ├── .
-│   └── .
-├── .
-├── .

與 Magisk 的差異

KernelSU 沒有內建的針對 Zygisk 的支援,因此模組中沒有與 Zygisk 相關的內容,但您可以透過 ZygiskNext 以支援 Zygisk 模組,此時 Zygisk 模組的內容與 Magisk 所支援的 Zygisk 完全相同。

module.prop

module.prop 是一個模組的組態檔案,在 KernelSU 中如果模組中不包含這個檔案,那麼它將不被認為是一個模組;這個檔案的格式如下:

txt
id=<string>
-name=<string>
-version=<string>
-versionCode=<int>
-author=<string>
-description=<string>
  • id 必須與這個規則運算式相符:^[a-zA-Z][a-zA-Z0-9._-]+$ 例如:✓ a_module,✓ a.module,✓ module-101,✗ a module,✗ 1_module,✗ -a-module。這是您的模組的唯一識別碼,發表後將無法變更。
  • versionCode 必須是一個整數,用於比較版本。
  • 其他未在上方提到的內容可以是任何單行字串。
  • 請確保使用 UNIX (LF) 分行符號類型,而非 Windows (CR + LF)Macintosh (CR)

Shell 指令碼

請閱讀 開機指令碼 章節,以瞭解 post-fs-data.shservice.sh 之間的差別。對於大多數模組開發人員來說,如果您只需要執行一個開機指令碼,service.sh 應該已經足夠了。

在您的模組中的所有指令碼中,請使用 MODDIR=${0%/*} 以取得您的模組基本目錄路徑;請不要在指令碼中以硬式編碼的方式加入您的模組路徑。

與 Magisk 的差異

您可以透過環境變數 KSU 來判斷指令碼是執行在 KernelSU 還是 Magisk 中,如果執行在 KernelSU,這個值會被設為 true

system 目錄

這個目錄的內容會在系統啟動後,以 overlayfs 的方式覆疊在系統的 /system 分割區之上,這表示:

  1. 系統中對應目錄的相同名稱的檔案會被此目錄中的檔案覆寫。
  2. 系統中對應目錄的相同名稱的檔案會與此目錄的檔案合併。

如果您想要刪除系統先前的目錄中的某個檔案或資料夾,您需要在模組目錄中透過 mknod filename c 0 0 以建立一個 filename 的相同名稱的檔案;這樣 overlayfs 系統會自動「whiteout」等效刪除這個檔案 (/system 分割區並未被變更)。

您也可以在 customize.sh 中宣告一個名為 REMOVE 並且包含一系列目錄的變數以執行移除作業,KernelSU 會自動為您在模組對應目錄執行 mknod <TARGET> c 0 0。例如:

sh
REMOVE="
-/system/app/YouTube
-/system/app/Bloatware
-"

上方的清單將會執行:mknod $MODPATH/system/app/YouTuBe c 0 0mknod $MODPATH/system/app/Bloatware c 0 0;並且 /system/app/YouTube/system/app/Bloatware 將會在模組生效前移除。

如果您想要取代系統的某個目錄,您需要在模組目錄中建立一個相同路徑的目錄,然後為此目錄設定此屬性:setfattr -n trusted.overlay.opaque -v y <TARGET>;這樣 overlayfs 系統會自動將對應目錄取代 (/system 分割區並未被變更)。

您可以在 customize.sh 中宣告一個名為 REMOVE 並且包含一系列目錄的變數以執行移除作業,KernelSU 會自動為您在模組對應目錄執行相關作業。例如:

sh
REPLACE="
-/system/app/YouTube
-/system/app/Bloatware
-"

上方的清單將會執行:自動建立目錄 $MODPATH/system/app/YouTube$MODPATH//system/app/Bloatware,然後執行 setfattr -n trusted.overlay.opaque -v y $$MODPATH/system/app/YouTubesetfattr -n trusted.overlay.opaque -v y $$MODPATH/system/app/Bloatware;並且 /system/app/YouTube/system/app/Bloatware 將會在模組生效後被取代為空白目錄。

與 Magisk 的差異

KernelSU 的 systemless 機制透過核心的 overlayfs 實作,而 Magisk 目前則是透過 magic mount (bind mount),兩者的實作方式有很大的差別,但最終的目標是一致的:不修改實際的 /system 分割區但修改 /system 檔案。

如果您對 overlayfs 感興趣,建議閱讀 Linux Kernel 關於 overlayfs 的文件

system.prop

這個檔案的格式與 build.prop 完全相同:每一行都是由 [key]=[value] 組成。

sepolicy.rule

如果您的模組需要一些額外 SELinux 原則修補程式,請將這些原則新增至這個檔案中。這個檔案的每一行都將被視為一個原則陳述。

模組安裝程式

KernelSU 的模組安裝程式就是一個可以透過 KernelSU 管理員應用程式刷新的 Zip 檔案,這個 Zip 檔案的格式如下:

txt
module.zip
-
-├── customize.sh                       <--- (Optional, more details later)
-│                                           This script will be sourced by update-binary
-├── ...
-├── ...  /* 其他模块文件 */
-

WARNING

KernelSU 模組不支援在 Recovery 中安裝!!

自訂安裝程序

如果您想要控制模組的安裝程序,可以在模組的目錄下建立一個名為 customize.sh 的檔案,這個檔案將會在模組被解壓縮後匯入至目前的 shell 中,如果您的模組需要依據裝置的 API 版本或裝置架構執行一些額外的作業,這個指令碼將非常有用。

如果您想完全控制指令碼的安裝程序,您可以在 customize.sh 中宣告 SKIPUNZIP=1 以跳過所有的預設安裝步驟;此時,您需要自行處理所有的安裝程序 (例如解壓縮模組、設定權限等)

customize.sh 指令碼以「獨立模式」執行在 KernelSU 的 BusyBox ash shell 中。您可以使用下列變數和函式:

變數

  • KSU (bool): 標示此指令碼執行於 KernelSU 環境中,此變數的值將永遠為 true,您可以透過它與 Magisk 進行區分。
  • KSU_VER (string): KernelSU 目前的版本名稱 (例如 v0.4.0)
  • KSU_VER_CODE (int): KernelSU 使用者空間目前的版本代碼 (例如 10672)
  • KSU_KERNEL_VER_CODE (int): KernelSU 核心空間目前的版本代碼 (例如 10672)
  • BOOTMODE (bool): 此變數在 KernelSU 中永遠為 true
  • MODPATH (path): 目前模組的安裝目錄
  • TMPDIR (path): 可以存放暫存檔的位置
  • ZIPFILE (path): 目前模組的安裝程式 Zip
  • ARCH (string): 裝置的 CPU 架構,有這幾種:arm, arm64, x86, or x64
  • IS64BIT (bool): 是否為 64 位元裝置
  • API (int): 目前裝置的 Android API 版本 (例如 Android 6.0 上為 23)

WARNING

MAGISK_VER_CODE 在 KernelSU 永遠為 25200MAGISK_VER 則為 v25.2,請不要透過這兩個變數來判斷是否為 KernelSU!

函式

txt
ui_print <msg>
-    print <msg> to console
-    Avoid using 'echo' as it will not display in custom recovery's console
-
-abort <msg>
-    print error message <msg> to console and terminate the installation
-    Avoid using 'exit' as it will skip the termination cleanup steps
-
-set_perm <target> <owner> <group> <permission> [context]
-    if [context] is not set, the default is "u:object_r:system_file:s0"
-    this function is a shorthand for the following commands:
-       chown owner.group target
-       chmod permission target
-       chcon context target
-
-set_perm_recursive <directory> <owner> <group> <dirpermission> <filepermission> [context]
-    if [context] is not set, the default is "u:object_r:system_file:s0"
-    for all files in <directory>, it will call:
-       set_perm file owner group filepermission context
-    for all directories in <directory> (including itself), it will call:
-       set_perm dir owner group dirpermission context

開機指令碼

在 KernelSU 中,依據指令碼執行模式的不同分為兩種:post-fs-data 模式和 late_start 服務模式。

  • post-fs-data 模式

    • 這個階段是「封鎖」的。在執行完成之前或 10 秒鐘之後,開機程序會被暫停。
    • 指令碼在任何模組被掛接之前執行。這使模組開發人員可以在模組被掛接之前動態調整他們的模組。
    • 這個階段發生在 Zygote 啟動之前,這意味著 Android 中的一切。
    • 使用 setprop 會導致開機程序死鎖!請使用 resetprop -n <prop_name> <prop_value> 替代。
    • 僅在必要時在此模式中執行指令碼
  • late_start 服務模式

    • 這個階段是「非封鎖」的。您的指令碼會與其餘的啟動程序平行執行。
    • 大多数脚本都建议在这种模式下运行

在 KernelSU 中,開機指令碼依據存放位置的不同還分為兩種:一般指令碼和模組指令碼。

  • 一般指令碼

    • 放置於 /data/adb/post-fs-data.d/data/adb/service.d 中。
    • 僅有指令碼被設為可執行 (chmod +x script.sh) 時才會被執行。
    • post-fs-data.d 中的指令碼以 post-fs-data 模式執行,在 service.d 中的指令碼以 late_start 服務模式執行。
    • 模組不應在安裝程序中新增一般指令碼。
  • 模組指令碼

    • 放置於模組自己的資料夾中。
    • 僅有在模組啟用時才會執行。
    • post-fs-data.sh 以 post-fs-data 模式執行,而 service.sh 則以 late_start 服務模式執行。

所有启动脚本都将在 KernelSU 的 BusyBox ash shell 中运行,并启用“独立模式”。

係依據 GPL3 授權發行。

- +
Skip to content

模組指南

KernelSU 提供了一個模組機制,它可以在保持系統分割區完整性的同時達到修改系統分割區的效果;這種機制一般被稱為 systemless。

KernelSU 的模組運作機制與 Magisk 幾乎相同,如果您熟悉 Magisk 模組的開發,那麼開發 KernelSU 的模組大同小異,您可以跳過下列有關模組的介紹,只需要瞭解 KernelSU 模組與 Magisk 模組的異同

Busybox

KernelSU 提供了一個完備的 BusyBox 二進位檔案 (包括完整的 SELinux 支援)。可執行檔位於 /data/adb/ksu/bin/busybox。 KernelSU 的 BusyBox 支援同時執行時可切換的 "ASH Standalone Shell Mode"。 這種讀了模式意味著在執行 BusyBox 的 ash shell 時,每個命令都會直接使用 BusyBox 中內建的應用程式,而不論 PATH 的設定為何。 例如,lsrmchmod 等命令將不會使用 PATH 中設定的命令 (在 Android 的狀況下,預設狀況下分別為 /system/bin/ls/system/bin/rm/system/bin/chmod),而是直接呼叫 BusyBox 內建的應用程式。 這確保了指令碼始終在可預測的環境中執行,並始終具有完整的命令套件,不論它執行在哪個 Android 版本上。 要強制下一個命令不使用 BusyBox,您必須使用完整路徑呼叫可執行檔。

在 KernelSU 上下文中執行的每個 shell 指令碼都將在 BusyBox 的 ash shell 中以獨立模式執行。對於第三方開發人員相關的內容,包括所有開機指令碼和模組安裝指令碼。

對於想要在 KernelSU 之外使用這個「獨立模式」功能的使用者,有兩種啟用方法:

  1. 將環境變數 ASH_STANDALONE 設為 1。例如:ASH_STANDALONE=1 /data/adb/ksu/bin/busybox sh <script>
  2. 使用命令列選項切換:/data/adb/ksu/bin/busybox sh -o standalone <script>

為了確保所有後續的 sh shell 都在獨立模式下執行,第一種是首選方法 (這也是 KernelSU 和 KernelSU 管理員內部使用的方法),因為環境變數會被繼承到子處理程序中。

與 Magisk 的差異

KernelSU 的 BusyBox 現在是直接使用 Magisk 專案編譯的二進位檔案,感謝 Magisk! 因此,您完全不必擔心 BusyBox 指令碼與在 Magisk 和 KernelSU 之間的相容性問題,因為它們完全相同!

KernelSU 模組

KernelSU 模組是一個放置於 /data/adb/modules 且滿足下列結構的資料夾:

txt
/data/adb/modules
+├── .
+├── .
+|
+├── $MODID                  <--- 模組的資料夾名稱與模組 ID 相同
+│   │
+│   │      *** 模組識別 ***
+│   │
+│   ├── module.prop         <--- 這個檔案儲存與模組相關的中繼資料,例如模組 ID、版本等
+│   │
+│   │      *** 主要內容 ***
+│   │
+│   ├── system              <--- 這個資料夾會在 skip_mount 不存在時被掛接至系統
+│   │   ├── ...
+│   │   ├── ...
+│   │   └── ...
+│   │
+│   │      *** 狀態旗標 ***
+│   │
+│   ├── skip_mount          <--- 如果這個檔案存在,那麼 KernelSU 將不會掛接您的系統資料夾
+│   ├── disable             <--- 如果這個檔案存在,那麼模組將會被停用
+│   ├── remove              <--- 如果這個檔案存在,那麼模組將會在下次重新開機時被移除
+│   │
+│   │      *** 選用檔案 ***
+│   │
+│   ├── post-fs-data.sh     <--- 這個指令碼將會在 post-fs-data 中執行
+│   ├── service.sh          <--- 這個指令碼將會在 late_start 服務中執行
+|   ├── uninstall.sh        <--- 這個指令碼將會在 KernelSU 移除模組時執行
+│   ├── system.prop         <--- 這個檔案中指定的屬性將會在系統啟動時透過 resetprop 變更
+│   ├── sepolicy.rule       <--- 這個檔案中的 SELinux 原則將會在系統開機時載入
+│   │
+│   │      *** 自動產生的目錄,不要手動建立或修改! ***
+│   │
+│   ├── vendor              <--- A symlink to $MODID/system/vendor
+│   ├── product             <--- A symlink to $MODID/system/product
+│   ├── system_ext          <--- A symlink to $MODID/system/system_ext
+│   │
+│   │      *** 允許的其他額外檔案/資料夾 ***
+│   │
+│   ├── ...
+│   └── ...
+|
+├── another_module
+│   ├── .
+│   └── .
+├── .
+├── .
/data/adb/modules
+├── .
+├── .
+|
+├── $MODID                  <--- 模組的資料夾名稱與模組 ID 相同
+│   │
+│   │      *** 模組識別 ***
+│   │
+│   ├── module.prop         <--- 這個檔案儲存與模組相關的中繼資料,例如模組 ID、版本等
+│   │
+│   │      *** 主要內容 ***
+│   │
+│   ├── system              <--- 這個資料夾會在 skip_mount 不存在時被掛接至系統
+│   │   ├── ...
+│   │   ├── ...
+│   │   └── ...
+│   │
+│   │      *** 狀態旗標 ***
+│   │
+│   ├── skip_mount          <--- 如果這個檔案存在,那麼 KernelSU 將不會掛接您的系統資料夾
+│   ├── disable             <--- 如果這個檔案存在,那麼模組將會被停用
+│   ├── remove              <--- 如果這個檔案存在,那麼模組將會在下次重新開機時被移除
+│   │
+│   │      *** 選用檔案 ***
+│   │
+│   ├── post-fs-data.sh     <--- 這個指令碼將會在 post-fs-data 中執行
+│   ├── service.sh          <--- 這個指令碼將會在 late_start 服務中執行
+|   ├── uninstall.sh        <--- 這個指令碼將會在 KernelSU 移除模組時執行
+│   ├── system.prop         <--- 這個檔案中指定的屬性將會在系統啟動時透過 resetprop 變更
+│   ├── sepolicy.rule       <--- 這個檔案中的 SELinux 原則將會在系統開機時載入
+│   │
+│   │      *** 自動產生的目錄,不要手動建立或修改! ***
+│   │
+│   ├── vendor              <--- A symlink to $MODID/system/vendor
+│   ├── product             <--- A symlink to $MODID/system/product
+│   ├── system_ext          <--- A symlink to $MODID/system/system_ext
+│   │
+│   │      *** 允許的其他額外檔案/資料夾 ***
+│   │
+│   ├── ...
+│   └── ...
+|
+├── another_module
+│   ├── .
+│   └── .
+├── .
+├── .

與 Magisk 的差異

KernelSU 沒有內建的針對 Zygisk 的支援,因此模組中沒有與 Zygisk 相關的內容,但您可以透過 ZygiskNext 以支援 Zygisk 模組,此時 Zygisk 模組的內容與 Magisk 所支援的 Zygisk 完全相同。

module.prop

module.prop 是一個模組的組態檔案,在 KernelSU 中如果模組中不包含這個檔案,那麼它將不被認為是一個模組;這個檔案的格式如下:

txt
id=<string>
+name=<string>
+version=<string>
+versionCode=<int>
+author=<string>
+description=<string>
id=<string>
+name=<string>
+version=<string>
+versionCode=<int>
+author=<string>
+description=<string>
  • id 必須與這個規則運算式相符:^[a-zA-Z][a-zA-Z0-9._-]+$ 例如:✓ a_module,✓ a.module,✓ module-101,✗ a module,✗ 1_module,✗ -a-module。這是您的模組的唯一識別碼,發表後將無法變更。
  • versionCode 必須是一個整數,用於比較版本。
  • 其他未在上方提到的內容可以是任何單行字串。
  • 請確保使用 UNIX (LF) 分行符號類型,而非 Windows (CR + LF)Macintosh (CR)

Shell 指令碼

請閱讀 開機指令碼 章節,以瞭解 post-fs-data.shservice.sh 之間的差別。對於大多數模組開發人員來說,如果您只需要執行一個開機指令碼,service.sh 應該已經足夠了。

在您的模組中的所有指令碼中,請使用 MODDIR=${0%/*} 以取得您的模組基本目錄路徑;請不要在指令碼中以硬式編碼的方式加入您的模組路徑。

與 Magisk 的差異

您可以透過環境變數 KSU 來判斷指令碼是執行在 KernelSU 還是 Magisk 中,如果執行在 KernelSU,這個值會被設為 true

system 目錄

這個目錄的內容會在系統啟動後,以 overlayfs 的方式覆疊在系統的 /system 分割區之上,這表示:

  1. 系統中對應目錄的相同名稱的檔案會被此目錄中的檔案覆寫。
  2. 系統中對應目錄的相同名稱的檔案會與此目錄的檔案合併。

如果您想要刪除系統先前的目錄中的某個檔案或資料夾,您需要在模組目錄中透過 mknod filename c 0 0 以建立一個 filename 的相同名稱的檔案;這樣 overlayfs 系統會自動「whiteout」等效刪除這個檔案 (/system 分割區並未被變更)。

您也可以在 customize.sh 中宣告一個名為 REMOVE 並且包含一系列目錄的變數以執行移除作業,KernelSU 會自動為您在模組對應目錄執行 mknod <TARGET> c 0 0。例如:

sh
REMOVE="
+/system/app/YouTube
+/system/app/Bloatware
+"
REMOVE="
+/system/app/YouTube
+/system/app/Bloatware
+"

上方的清單將會執行:mknod $MODPATH/system/app/YouTuBe c 0 0mknod $MODPATH/system/app/Bloatware c 0 0;並且 /system/app/YouTube/system/app/Bloatware 將會在模組生效前移除。

如果您想要取代系統的某個目錄,您需要在模組目錄中建立一個相同路徑的目錄,然後為此目錄設定此屬性:setfattr -n trusted.overlay.opaque -v y <TARGET>;這樣 overlayfs 系統會自動將對應目錄取代 (/system 分割區並未被變更)。

您可以在 customize.sh 中宣告一個名為 REMOVE 並且包含一系列目錄的變數以執行移除作業,KernelSU 會自動為您在模組對應目錄執行相關作業。例如:

sh
REPLACE="
+/system/app/YouTube
+/system/app/Bloatware
+"
REPLACE="
+/system/app/YouTube
+/system/app/Bloatware
+"

上方的清單將會執行:自動建立目錄 $MODPATH/system/app/YouTube$MODPATH//system/app/Bloatware,然後執行 setfattr -n trusted.overlay.opaque -v y $$MODPATH/system/app/YouTubesetfattr -n trusted.overlay.opaque -v y $$MODPATH/system/app/Bloatware;並且 /system/app/YouTube/system/app/Bloatware 將會在模組生效後被取代為空白目錄。

與 Magisk 的差異

KernelSU 的 systemless 機制透過核心的 overlayfs 實作,而 Magisk 目前則是透過 magic mount (bind mount),兩者的實作方式有很大的差別,但最終的目標是一致的:不修改實際的 /system 分割區但修改 /system 檔案。

如果您對 overlayfs 感興趣,建議閱讀 Linux Kernel 關於 overlayfs 的文件

system.prop

這個檔案的格式與 build.prop 完全相同:每一行都是由 [key]=[value] 組成。

sepolicy.rule

如果您的模組需要一些額外 SELinux 原則修補程式,請將這些原則新增至這個檔案中。這個檔案的每一行都將被視為一個原則陳述。

模組安裝程式

KernelSU 的模組安裝程式就是一個可以透過 KernelSU 管理員應用程式刷新的 Zip 檔案,這個 Zip 檔案的格式如下:

txt
module.zip
+
+├── customize.sh                       <--- (Optional, more details later)
+│                                           This script will be sourced by update-binary
+├── ...
+├── ...  /* 其他模块文件 */
+
module.zip
+
+├── customize.sh                       <--- (Optional, more details later)
+│                                           This script will be sourced by update-binary
+├── ...
+├── ...  /* 其他模块文件 */
+

WARNING

KernelSU 模組不支援在 Recovery 中安裝!!

自訂安裝程序

如果您想要控制模組的安裝程序,可以在模組的目錄下建立一個名為 customize.sh 的檔案,這個檔案將會在模組被解壓縮後匯入至目前的 shell 中,如果您的模組需要依據裝置的 API 版本或裝置架構執行一些額外的作業,這個指令碼將非常有用。

如果您想完全控制指令碼的安裝程序,您可以在 customize.sh 中宣告 SKIPUNZIP=1 以跳過所有的預設安裝步驟;此時,您需要自行處理所有的安裝程序 (例如解壓縮模組、設定權限等)

customize.sh 指令碼以「獨立模式」執行在 KernelSU 的 BusyBox ash shell 中。您可以使用下列變數和函式:

變數

  • KSU (bool): 標示此指令碼執行於 KernelSU 環境中,此變數的值將永遠為 true,您可以透過它與 Magisk 進行區分。
  • KSU_VER (string): KernelSU 目前的版本名稱 (例如 v0.4.0)
  • KSU_VER_CODE (int): KernelSU 使用者空間目前的版本代碼 (例如 10672)
  • KSU_KERNEL_VER_CODE (int): KernelSU 核心空間目前的版本代碼 (例如 10672)
  • BOOTMODE (bool): 此變數在 KernelSU 中永遠為 true
  • MODPATH (path): 目前模組的安裝目錄
  • TMPDIR (path): 可以存放暫存檔的位置
  • ZIPFILE (path): 目前模組的安裝程式 Zip
  • ARCH (string): 裝置的 CPU 架構,有這幾種:arm, arm64, x86, or x64
  • IS64BIT (bool): 是否為 64 位元裝置
  • API (int): 目前裝置的 Android API 版本 (例如 Android 6.0 上為 23)

WARNING

MAGISK_VER_CODE 在 KernelSU 永遠為 25200MAGISK_VER 則為 v25.2,請不要透過這兩個變數來判斷是否為 KernelSU!

函式

txt
ui_print <msg>
+    print <msg> to console
+    Avoid using 'echo' as it will not display in custom recovery's console
+
+abort <msg>
+    print error message <msg> to console and terminate the installation
+    Avoid using 'exit' as it will skip the termination cleanup steps
+
+set_perm <target> <owner> <group> <permission> [context]
+    if [context] is not set, the default is "u:object_r:system_file:s0"
+    this function is a shorthand for the following commands:
+       chown owner.group target
+       chmod permission target
+       chcon context target
+
+set_perm_recursive <directory> <owner> <group> <dirpermission> <filepermission> [context]
+    if [context] is not set, the default is "u:object_r:system_file:s0"
+    for all files in <directory>, it will call:
+       set_perm file owner group filepermission context
+    for all directories in <directory> (including itself), it will call:
+       set_perm dir owner group dirpermission context
ui_print <msg>
+    print <msg> to console
+    Avoid using 'echo' as it will not display in custom recovery's console
+
+abort <msg>
+    print error message <msg> to console and terminate the installation
+    Avoid using 'exit' as it will skip the termination cleanup steps
+
+set_perm <target> <owner> <group> <permission> [context]
+    if [context] is not set, the default is "u:object_r:system_file:s0"
+    this function is a shorthand for the following commands:
+       chown owner.group target
+       chmod permission target
+       chcon context target
+
+set_perm_recursive <directory> <owner> <group> <dirpermission> <filepermission> [context]
+    if [context] is not set, the default is "u:object_r:system_file:s0"
+    for all files in <directory>, it will call:
+       set_perm file owner group filepermission context
+    for all directories in <directory> (including itself), it will call:
+       set_perm dir owner group dirpermission context

開機指令碼

在 KernelSU 中,依據指令碼執行模式的不同分為兩種:post-fs-data 模式和 late_start 服務模式。

  • post-fs-data 模式

    • 這個階段是「封鎖」的。在執行完成之前或 10 秒鐘之後,開機程序會被暫停。
    • 指令碼在任何模組被掛接之前執行。這使模組開發人員可以在模組被掛接之前動態調整他們的模組。
    • 這個階段發生在 Zygote 啟動之前,這意味著 Android 中的一切。
    • 使用 setprop 會導致開機程序死鎖!請使用 resetprop -n <prop_name> <prop_value> 替代。
    • 僅在必要時在此模式中執行指令碼
  • late_start 服務模式

    • 這個階段是「非封鎖」的。您的指令碼會與其餘的啟動程序平行執行。
    • 大多数脚本都建议在这种模式下运行

在 KernelSU 中,開機指令碼依據存放位置的不同還分為兩種:一般指令碼和模組指令碼。

  • 一般指令碼

    • 放置於 /data/adb/post-fs-data.d/data/adb/service.d 中。
    • 僅有指令碼被設為可執行 (chmod +x script.sh) 時才會被執行。
    • post-fs-data.d 中的指令碼以 post-fs-data 模式執行,在 service.d 中的指令碼以 late_start 服務模式執行。
    • 模組不應在安裝程序中新增一般指令碼。
  • 模組指令碼

    • 放置於模組自己的資料夾中。
    • 僅有在模組啟用時才會執行。
    • post-fs-data.sh 以 post-fs-data 模式執行,而 service.sh 則以 late_start 服務模式執行。

所有启动脚本都将在 KernelSU 的 BusyBox ash shell 中运行,并启用“独立模式”。

係依據 GPL3 授權發行。

+ \ No newline at end of file diff --git a/zh_TW/guide/rescue-from-bootloop.html b/zh_TW/guide/rescue-from-bootloop.html index 8d44786da34a..385b30121979 100644 --- a/zh_TW/guide/rescue-from-bootloop.html +++ b/zh_TW/guide/rescue-from-bootloop.html @@ -5,18 +5,19 @@ 搶救開機迴圈 | KernelSU - - + + + - - - - + + + + + -
Skip to content
On this page

搶救開機迴圈

在刷新裝置時,我們很可能會遇到裝置「變磚」的狀況,從理論上講,如果您只是使用 Fastboot 刷新 Boot 分割區或者安裝不合適的模組導致裝置無法開機,那麼這都可以透過合適的作業還原您的手機;這個文件提供一些緊急方法可以讓您在「變磚」中還原。

刷新 Boot 時變磚

在 KernelSU 中,刷新 boot 時變磚有下列原因:

  1. 你刷新了錯誤格式的 Boot 映像。比如您的手機 Boot 格式為 gz,但您刷新 lz4 格式的映像,那麼此時手機將無法開機。
  2. 您的手機需要關閉 AVB 驗證才可正常開機 (這通常需要抹除手機上的所有資料)。
  3. 您的核心存在某些錯誤或您的核心並不適合這部手機刷新。

無論哪種狀況,您都可以透過刷新原廠 Boot還原;因此,在安裝教學最開始,我們已經強烈建議大家,在刷新之前備份自己的原廠 Boot!如果您沒有備份,那麼您可以透過其他與您相同裝置的使用者或官方韌體擷取 Boot。

刷新模組變磚

刷新模組變磚可能是大家遇到的更常見的狀況,但是這裡要嚴正警示大家:不要刷新未知來源的模組!!。因為模組擁有 Root 權限,它能完全對您的裝置造成無法復原的損壞!

一般模組

如果大家刷新了某些開放原始碼的或者被證明是安全的模組使手機無法開機,那麼這種狀況在 KernelSU 中非常容易還原,也無需擔心。KernelSU 內建了下列兩種機制以搶救您的裝置:

  1. AB 更新
  2. 透過按下「音量 -」搶救

AB 更新

KernelSU 的模組借鑒了 Android 系統 OTA 更新時的 AB 更新機制,如果您安裝了新模組或者對現存模組進行了更新作業,不會直接修改目前使用的模組檔案,而是會把所有模組建置為另外一個更新映像;系統重新啟動後,會使用這個更新映像嘗試重新啟動一次,如果 Android 系統成功開機,模組才會真正更新。

因此,最簡單最常用的搶救方法就是:強制重新開機一次。如果您在刷新某個模組之後系統無法開機,您可以長按電源按鈕超過 10 秒,系統會自動重新開機;模組會回復為更新前的狀態,先前更新的模組也將會被自動停用。

透過按下「音量 -」搶救

如果 AB 更新仍然無法解決,您可以嘗試使用安全模式。進入安全模式之後,所有的模組將會被停用。

進入安全模式的方法有兩種:

  1. 某些系統內建的安全模式;有些系統是長按「音量 -」,有些系統 (例如 MIUI) 可以在 Recovery 中啟用安全模式。進入系統的安全模式後,KernelSU 也會進入安全模式,並自動停用模組。
  2. KernelSU 內建的安全模式;作業方法:開機第一個畫面後,連續按下「音量 -」按鈕超過三次。注意是按下-抬起、按下-抬起、按下-抬起,並非一直按下。

進入安全模式後,KernelSU 管理員的模組頁面的所有模組將會被停用,但您可以執行「解除安裝」作業,將可能存在問題的模組解除安裝。

內建的安全模式在核心中實作,因此不會出現按鍵活動無法攔截的狀況。不過對於非 GKI 核心,可能需要手動整合程式碼,可以參閱官方文件指南。

惡意模組

如果以上方法無法搶救您的裝置,那麼很可能您安裝的模組存在惡意作業或透過其他方式損壞了您的裝置,在這種狀況下,只有兩個建議:

  1. 抹除資料並刷新官方系統。
  2. 諮詢售後服務。

係依據 GPL3 授權發行。

- +
Skip to content

搶救開機迴圈

在刷新裝置時,我們很可能會遇到裝置「變磚」的狀況,從理論上講,如果您只是使用 Fastboot 刷新 Boot 分割區或者安裝不合適的模組導致裝置無法開機,那麼這都可以透過合適的作業還原您的手機;這個文件提供一些緊急方法可以讓您在「變磚」中還原。

刷新 Boot 時變磚

在 KernelSU 中,刷新 boot 時變磚有下列原因:

  1. 你刷新了錯誤格式的 Boot 映像。比如您的手機 Boot 格式為 gz,但您刷新 lz4 格式的映像,那麼此時手機將無法開機。
  2. 您的手機需要關閉 AVB 驗證才可正常開機 (這通常需要抹除手機上的所有資料)。
  3. 您的核心存在某些錯誤或您的核心並不適合這部手機刷新。

無論哪種狀況,您都可以透過刷新原廠 Boot還原;因此,在安裝教學最開始,我們已經強烈建議大家,在刷新之前備份自己的原廠 Boot!如果您沒有備份,那麼您可以透過其他與您相同裝置的使用者或官方韌體擷取 Boot。

刷新模組變磚

刷新模組變磚可能是大家遇到的更常見的狀況,但是這裡要嚴正警示大家:不要刷新未知來源的模組!!。因為模組擁有 Root 權限,它能完全對您的裝置造成無法復原的損壞!

一般模組

如果大家刷新了某些開放原始碼的或者被證明是安全的模組使手機無法開機,那麼這種狀況在 KernelSU 中非常容易還原,也無需擔心。KernelSU 內建了下列兩種機制以搶救您的裝置:

  1. AB 更新
  2. 透過按下「音量 -」搶救

AB 更新

KernelSU 的模組借鑒了 Android 系統 OTA 更新時的 AB 更新機制,如果您安裝了新模組或者對現存模組進行了更新作業,不會直接修改目前使用的模組檔案,而是會把所有模組建置為另外一個更新映像;系統重新啟動後,會使用這個更新映像嘗試重新啟動一次,如果 Android 系統成功開機,模組才會真正更新。

因此,最簡單最常用的搶救方法就是:強制重新開機一次。如果您在刷新某個模組之後系統無法開機,您可以長按電源按鈕超過 10 秒,系統會自動重新開機;模組會回復為更新前的狀態,先前更新的模組也將會被自動停用。

透過按下「音量 -」搶救

如果 AB 更新仍然無法解決,您可以嘗試使用安全模式。進入安全模式之後,所有的模組將會被停用。

進入安全模式的方法有兩種:

  1. 某些系統內建的安全模式;有些系統是長按「音量 -」,有些系統 (例如 MIUI) 可以在 Recovery 中啟用安全模式。進入系統的安全模式後,KernelSU 也會進入安全模式,並自動停用模組。
  2. KernelSU 內建的安全模式;作業方法:開機第一個畫面後,連續按下「音量 -」按鈕超過三次。注意是按下-抬起、按下-抬起、按下-抬起,並非一直按下。

進入安全模式後,KernelSU 管理員的模組頁面的所有模組將會被停用,但您可以執行「解除安裝」作業,將可能存在問題的模組解除安裝。

內建的安全模式在核心中實作,因此不會出現按鍵活動無法攔截的狀況。不過對於非 GKI 核心,可能需要手動整合程式碼,可以參閱官方文件指南。

惡意模組

如果以上方法無法搶救您的裝置,那麼很可能您安裝的模組存在惡意作業或透過其他方式損壞了您的裝置,在這種狀況下,只有兩個建議:

  1. 抹除資料並刷新官方系統。
  2. 諮詢售後服務。

係依據 GPL3 授權發行。

+ \ No newline at end of file diff --git a/zh_TW/guide/unofficially-support-devices.html b/zh_TW/guide/unofficially-support-devices.html index 8de7aa803836..2d641d45eacd 100644 --- a/zh_TW/guide/unofficially-support-devices.html +++ b/zh_TW/guide/unofficially-support-devices.html @@ -5,19 +5,20 @@ 非官方支援裝置 | KernelSU - - + + + - - - - - + + + + + + -
Skip to content
On this page

非官方支援裝置

WARNING

本文件列出由其他開發人員維護的支援 KernelSU 的非 GKI 裝置核心

WARNING

本文件仅便於尋找裝置對應原始碼,這並非意味著這些原始碼 KernelSU 開發人員審查,您應自行承擔風險。

維護者存放庫支援裝置
diphonsD8G_Kernel_oxygenPoco F3: alioth | Poco F4: munch | MI10T/PRO: Apollo
diphonsD8G_Kernel_SM8150Poco X3 Pro: Vayu | Bhima
diphonsD8G_Kernel_SDM845Poco F1 | MI8 | MiMix2S
diphonsD8G_Kernel_SM8350Vili
diphonsD8G_Kernel_MarblePoco F5 | Redmi Note 12 Turbo | Marble
th1nhhdkandroid_kernel_sony_sm8250-kernelsuSony Xperia 1 II | Sony Xperia 5 II
akash07knexus_kernel_xiaomi_sm8250Poco F4: munch
HMTheBoy154Darkmatter-kernelGeneric x86_64 devices running Android-x86
Asuka-mioandroid_kernel_xiaomi_casMi 10 Ultra: cas
xiaoleGunMiku_kernel_xiaomi_waynewayne
YamazakuraHkernel_xiaomi_cepheuscepheus for pixel experience
SakuraNotStupidandroid_kernel_xiaomi_sdm710Xiaomi MI 8 SE(sirius/xmsirius)
Aquarius223(paper)android_kernel_xiaomi_msm8998MI 6 (sagit) and MIX 2 (chiron) for LineageOS
SlackerStateandroid_kernel_xiaomi_sm6150Redmi K30 4G (phoenix)
RooGhz720kernel_xiaomi_sm6150REDMI NOTE 10 PRO (sweet)
OnlyTomInSecondandroid_kernel_xiaomi_sdm845Mi 8 (dipper) for LineageOS
Rohail33RealKing KernelApollo(Redmi K30S Ultra/Mi 10T/Mi 10T Pro),Alioth(Redmi K40/POCO F3/Mi 11X),Munch(Redmi K40S/POCO F4), both MIUI and AOSP.
lateautumn233android_kernel_oneplus_sm8250OnePlus 8 Serials
Sreeshankar KNeverSettle KernelOnePlus Nord (avicii)
Molyuuneko_kernel_xiaomi_gauguinRedmi Note 9 Pro/ Mi 10T Lite/ Mi 10i
guh0613android_kernel_oppo_sm8150OPPO Reno Ace (OP4A89)
LeviMarvinandroid_kernel_xiaomi_aliothRedmi K40 / POCO F3
cibimokernel_xiaomi_raphael_ksuXiaomi Redmi K20 Pro / Mi 9T Pro (raphael)
EndCreditskernel_xiaomi_sm7250Redmi K30 5G ( picasso ) , Redmi K30i 5G ( picasso_48m)
msnxandroid-msm-coral-4.14-android13Pixel 4 XL
SoDebugkernel_xiaomi_raphaelXiaomi Redmi K20 Pro / Mi 9T Pro (raphael)
H1mJTkernel_realme_RMX1901Realme X (RMX1901/RMX1901CN)
SonalSingh18android_kernel_xiaomi_sm6250Miatoll [curtana, excalibur, gram, joyeuse]
RooGhz720kernel_xiaomi_lavenderRedmi Note 7 (Lavender)
JunASAKAkernel_google_msm-4.9_KernelSUGoogle Pixel 3a & 3a XL (sargo & bonito)
RooGhz720kernel_asus_X01BDAsus Zenfone Max Pro M1/M2
Evans Mikekernel_xiaomi_raphael_bool-xXiaomi Redmi K20 Pro / Mi 9T Pro (raphael)
easterNdayKSU_Thyme_BuildBotXiaomi 10S
tedomi2705kernel_xiaomi_sdm660Xiaomi Redmi Note 6 Pro (tulip)
RyuujiXandroid_kernel_asus_sdm660-4.19Asus Zenfone Max Pro M1 (X00TD)/ M2 (X01BD) (Linux 4.19)
liqidecgandroid_kernel_xiaomi_sdm710Mi 8 SE (sirius) (Linux 4.9)
Abdelhay-Aliandroid_kernel_huawei_hi6250Huawei P20 lite (hi6250) (Linux 4.9)
reocatandroid_kernel_nokia_sdm660_ksuNokia 6.1 (2018)
Tuan Anhkernel_nokia_msm8998Nokia 8 (Repartitioned)/8 Sirocco (NLA/A1N)
bggRGjQaUbCoEandroid_kernel_samsung_sm8250Samsung Galaxy S20+ 5G (y2q)
SOVIET-ANDROIDkernel_xiaomi_raphaelXK20 Pro Raphael DSP & A only SAR support
dabao1955android_kernel_OPPO_PEQM00MediaTek devices in the OPPO Reno series on ColorOS13.x
Aflaungosandroid_kernel_motorola_msm8998Moto G6 Plus (evert)
TheNoFacekernel_oneplus_msm8998OnePlus 5/5T (cheeseburger/dumpling)
Nipin NA (Joker-V2)Xcalibur kernel (violet)Xiaomi Redmi Note 7 Pro (violet)
Sr-Hankernel_xiaomi_mojitoRedmi Note 10 (Sunny/Mojito)
dabao1955android_kernel_OPPO_OP4ED5OPPO Reno6 on ColorOS11.x/ColorOS12.x,kernel version4.14.186+
rushiranpiseandroid_kernel_samsung_exynos9610_mintKernel 4.14.194 exynos9610 Non-GKI Device, Added KernelSu using manual method
CN-Scarspixel_experience_kernel_xiaomi_sm8250_kernelSUPixel Experience Kernel 4.19.282 for Xiaomi Redmi K40S / Xiaomi Poco F4 (munch)
exerMiyo TokuSony Tama (akari, apollo, aurora, akatsuki) (Linux 4.9)
likkaiQuicksilver KernelXiaomi 11 Lite 5G NE (lisa)
awakened1712android_kernel_samsung_exynos9820Samsung S10/N10
awakened1712android_kernel_oneplus_sm8350Oneplus 9/9Pro
siimsekLightning KernelXiaomi Redmi Note 8/8T (ginkgo/willow)
GiovanYCringekernel_a50Galaxy A50 (A505f,fn,g,gn,gt)
Asriadi Rahimandroid_kernel_google_wahooGoogle Pixel 2/2XL
Flameandroid_kernel_xiaomi_sm8250POCO F3/Redmi K40/Mi 11X (alioth)
DawfukFRkernel_oneplus_sm8250 (Stellaris-Kernel)Oneplus 8 (Instantnoodle), Oneplus 8T (Kebab), Oneplus 8 Pro (Instantnoodlep), Oneplus 9R (lemonades) [msm-4.19]
Tejas Singhkernel_xiaomi_ginkgo (Cuh Kernel)Xiaomi Redmi Note 8/8T (ginkgo/willow)
Coconutatandroid_kernel_xiaomi_ruby_expRedmi Note 12 Pro / Pro+ For MIUI 14
Coconutatandroid_kernel_xiaomi_sdm845_expXiaomi Mi 8 Pro(UD)[equuleus]/Explorer Edition[ursa]
Abdul Wahid Khankernel_xiaomi_begoniaRedmi Note 8 Pro (Begonia/Begoniain)
Abdul Wahid Khankernel_xiaomi_marbleXiaomi Poco F5 / Redmi Note 12 Turbo (Marble)
zfdx123kernel_xiaomi_aliothRedmi K40 (alioth)
wxt1221android_kernel_oneplus_sdm845Oneplus 6 (enchilada) | Oneplus 6T (fajita) with no Retrofit Dynamic Partitions
zharzinhooKernel-Oriente-GuampMoto g9 play
hc841NovaCore_kernelMoto g9 play
zlm324android_kernel_xiaomi_msm8998_ksua lineage kernel forked from LineageOS official repository, version 4.4.302, added ksu.
zlm324android_kernel_xiaomi_msm8953_ksuXiaomi 5X (tiffany)
Vincent4440android_kernel_xiaomi_sm8250Poco F4: munch
zharzinhooandroid_kernel_motorola_cebuMoto g9 power
tiandoufayaleandroid_kernel_samsung_sm8250Samsung Tab S7 WIFI(T870)
starmoeandroid_kernel_oneplus_msm8998Oneplus 5/5T, kernel version 4.4.302
supechickenkernel_xiaomi_sm8350 Xiaomi 11T Pro (vili)
Vaz15kkernelSU-A70qSamsung Galaxy A70 ( A705MN / A705FN )
whyakariandroid_kernel_xiaomi_ginkgonon-GKI ginkgo
wulan17android_kernel_xiaomi_mt6765Redmi 6/6A (cereus/cactus)
officialputuidandroid_kernel_realme_mt6785Realme 6/6i(Indian)/6s/7/Narzo/Narzo 20 Pro/Narzo 30 4G (RM6785)

係依據 GPL3 授權發行。

- +
Skip to content

非官方支援裝置

WARNING

本文件列出由其他開發人員維護的支援 KernelSU 的非 GKI 裝置核心

WARNING

本文件仅便於尋找裝置對應原始碼,這並非意味著這些原始碼 KernelSU 開發人員審查,您應自行承擔風險。

維護者存放庫支援裝置
diphonsD8G_Kernel_oxygenPoco F3: alioth | Poco F4: munch | MI10T/PRO: Apollo
diphonsD8G_Kernel_SM8150Poco X3 Pro: Vayu | Bhima
diphonsD8G_Kernel_SDM845Poco F1 | MI8 | MiMix2S
diphonsD8G_Kernel_SM8350Vili
diphonsD8G_Kernel_MarblePoco F5 | Redmi Note 12 Turbo | Marble
th1nhhdkandroid_kernel_sony_sm8250-kernelsuSony Xperia 1 II | Sony Xperia 5 II
akash07knexus_kernel_xiaomi_sm8250Poco F4: munch
HMTheBoy154Darkmatter-kernelGeneric x86_64 devices running Android-x86
Asuka-mioandroid_kernel_xiaomi_casMi 10 Ultra: cas
xiaoleGunMiku_kernel_xiaomi_waynewayne
YamazakuraHkernel_xiaomi_cepheuscepheus for pixel experience
SakuraNotStupidandroid_kernel_xiaomi_sdm710Xiaomi MI 8 SE(sirius/xmsirius)
Aquarius223(paper)android_kernel_xiaomi_msm8998MI 6 (sagit) and MIX 2 (chiron) for LineageOS
SlackerStateandroid_kernel_xiaomi_sm6150Redmi K30 4G (phoenix)
RooGhz720kernel_xiaomi_sm6150REDMI NOTE 10 PRO (sweet)
OnlyTomInSecondandroid_kernel_xiaomi_sdm845Mi 8 (dipper) for LineageOS
Rohail33RealKing KernelApollo(Redmi K30S Ultra/Mi 10T/Mi 10T Pro),Alioth(Redmi K40/POCO F3/Mi 11X),Munch(Redmi K40S/POCO F4), both MIUI and AOSP.
lateautumn233android_kernel_oneplus_sm8250OnePlus 8 Serials
Sreeshankar KNeverSettle KernelOnePlus Nord (avicii)
Molyuuneko_kernel_xiaomi_gauguinRedmi Note 9 Pro/ Mi 10T Lite/ Mi 10i
guh0613android_kernel_oppo_sm8150OPPO Reno Ace (OP4A89)
LeviMarvinandroid_kernel_xiaomi_aliothRedmi K40 / POCO F3
cibimokernel_xiaomi_raphael_ksuXiaomi Redmi K20 Pro / Mi 9T Pro (raphael)
EndCreditskernel_xiaomi_sm7250Redmi K30 5G ( picasso ) , Redmi K30i 5G ( picasso_48m)
msnxandroid-msm-coral-4.14-android13Pixel 4 XL
SoDebugkernel_xiaomi_raphaelXiaomi Redmi K20 Pro / Mi 9T Pro (raphael)
H1mJTkernel_realme_RMX1901Realme X (RMX1901/RMX1901CN)
SonalSingh18android_kernel_xiaomi_sm6250Miatoll [curtana, excalibur, gram, joyeuse]
RooGhz720kernel_xiaomi_lavenderRedmi Note 7 (Lavender)
JunASAKAkernel_google_msm-4.9_KernelSUGoogle Pixel 3a & 3a XL (sargo & bonito)
RooGhz720kernel_asus_X01BDAsus Zenfone Max Pro M1/M2
Evans Mikekernel_xiaomi_raphael_bool-xXiaomi Redmi K20 Pro / Mi 9T Pro (raphael)
easterNdayKSU_Thyme_BuildBotXiaomi 10S
tedomi2705kernel_xiaomi_sdm660Xiaomi Redmi Note 6 Pro (tulip)
RyuujiXandroid_kernel_asus_sdm660-4.19Asus Zenfone Max Pro M1 (X00TD)/ M2 (X01BD) (Linux 4.19)
liqidecgandroid_kernel_xiaomi_sdm710Mi 8 SE (sirius) (Linux 4.9)
Abdelhay-Aliandroid_kernel_huawei_hi6250Huawei P20 lite (hi6250) (Linux 4.9)
reocatandroid_kernel_nokia_sdm660_ksuNokia 6.1 (2018)
Tuan Anhkernel_nokia_msm8998Nokia 8 (Repartitioned)/8 Sirocco (NLA/A1N)
bggRGjQaUbCoEandroid_kernel_samsung_sm8250Samsung Galaxy S20+ 5G (y2q)
SOVIET-ANDROIDkernel_xiaomi_raphaelXK20 Pro Raphael DSP & A only SAR support
dabao1955android_kernel_OPPO_PEQM00MediaTek devices in the OPPO Reno series on ColorOS13.x
Aflaungosandroid_kernel_motorola_msm8998Moto G6 Plus (evert)
TheNoFacekernel_oneplus_msm8998OnePlus 5/5T (cheeseburger/dumpling)
Nipin NA (Joker-V2)Xcalibur kernel (violet)Xiaomi Redmi Note 7 Pro (violet)
Sr-Hankernel_xiaomi_mojitoRedmi Note 10 (Sunny/Mojito)
dabao1955android_kernel_OPPO_OP4ED5OPPO Reno6 on ColorOS11.x/ColorOS12.x,kernel version4.14.186+
rushiranpiseandroid_kernel_samsung_exynos9610_mintKernel 4.14.194 exynos9610 Non-GKI Device, Added KernelSu using manual method
CN-Scarspixel_experience_kernel_xiaomi_sm8250_kernelSUPixel Experience Kernel 4.19.282 for Xiaomi Redmi K40S / Xiaomi Poco F4 (munch)
exerMiyo TokuSony Tama (akari, apollo, aurora, akatsuki) (Linux 4.9)
likkaiQuicksilver KernelXiaomi 11 Lite 5G NE (lisa)
awakened1712android_kernel_samsung_exynos9820Samsung S10/N10
awakened1712android_kernel_oneplus_sm8350Oneplus 9/9Pro
siimsekLightning KernelXiaomi Redmi Note 8/8T (ginkgo/willow)
GiovanYCringekernel_a50Galaxy A50 (A505f,fn,g,gn,gt)
Asriadi Rahimandroid_kernel_google_wahooGoogle Pixel 2/2XL
Flameandroid_kernel_xiaomi_sm8250POCO F3/Redmi K40/Mi 11X (alioth)
DawfukFRkernel_oneplus_sm8250 (Stellaris-Kernel)Oneplus 8 (Instantnoodle), Oneplus 8T (Kebab), Oneplus 8 Pro (Instantnoodlep), Oneplus 9R (lemonades) [msm-4.19]
Tejas Singhkernel_xiaomi_ginkgo (Cuh Kernel)Xiaomi Redmi Note 8/8T (ginkgo/willow)
Coconutatandroid_kernel_xiaomi_ruby_expRedmi Note 12 Pro / Pro+ For MIUI 14
Coconutatandroid_kernel_xiaomi_sdm845_expXiaomi Mi 8 Pro(UD)[equuleus]/Explorer Edition[ursa]
Abdul Wahid Khankernel_xiaomi_begoniaRedmi Note 8 Pro (Begonia/Begoniain)
Abdul Wahid Khankernel_xiaomi_marbleXiaomi Poco F5 / Redmi Note 12 Turbo (Marble)
zfdx123kernel_xiaomi_aliothRedmi K40 (alioth)
wxt1221android_kernel_oneplus_sdm845Oneplus 6 (enchilada) | Oneplus 6T (fajita) with no Retrofit Dynamic Partitions
zharzinhooKernel-Oriente-GuampMoto g9 play
hc841NovaCore_kernelMoto g9 play
zlm324android_kernel_xiaomi_msm8998_ksua lineage kernel forked from LineageOS official repository, version 4.4.302, added ksu.
zlm324android_kernel_xiaomi_msm8953_ksuXiaomi 5X (tiffany)
Vincent4440android_kernel_xiaomi_sm8250Poco F4: munch
zharzinhooandroid_kernel_motorola_cebuMoto g9 power
tiandoufayaleandroid_kernel_samsung_sm8250Samsung Tab S7 WIFI(T870)
starmoeandroid_kernel_oneplus_msm8998Oneplus 5/5T, kernel version 4.4.302
supechickenkernel_xiaomi_sm8350 Xiaomi 11T Pro (vili)
Vaz15kkernelSU-A70qSamsung Galaxy A70 ( A705MN / A705FN )
whyakariandroid_kernel_xiaomi_ginkgonon-GKI ginkgo
wulan17android_kernel_xiaomi_mt6765Redmi 6/6A (cereus/cactus)
officialputuidandroid_kernel_realme_mt6785Realme 6/6i(Indian)/6s/7/Narzo/Narzo 20 Pro/Narzo 30 4G (RM6785)

係依據 GPL3 授權發行。

+ \ No newline at end of file diff --git a/zh_TW/guide/what-is-kernelsu.html b/zh_TW/guide/what-is-kernelsu.html index b496bbc02bb7..a755e84a0a9e 100644 --- a/zh_TW/guide/what-is-kernelsu.html +++ b/zh_TW/guide/what-is-kernelsu.html @@ -5,18 +5,19 @@ 什麼是 KernelSU? | KernelSU - - + + + - - - - + + + + + -
Skip to content
On this page

什麼是 KernelSU?

KernelSU 是 Android GKI 裝置的 Root 解決方案,它以核心模式運作,並直接在核心空間中為使用者空間應用程式授予 Root 權限。

功能

KernelSU 的主要功能是它是以核心為基礎的。 KernelSU 在核心空間中執行,所以它可以向我們提供從未有過的核心介面。例如,我們可以在核心模式中為任何處理程序新增硬體中斷點;我們可以在任何處理程序的實體記憶體中存取,而無人知曉;我們可以在核心空間攔截任何系統呼叫;等等。

KernelSU 還提供了一個以 overlayfs 為基礎的模組系統,允許您將自訂外掛程式載入到系統中。它還提供了一種修改 /system 分割區中檔案的機制。

如何使用

請參閱:安裝

如何建置

请參閱:如何建置

討論

係依據 GPL3 授權發行。

- +
Skip to content

什麼是 KernelSU?

KernelSU 是 Android GKI 裝置的 Root 解決方案,它以核心模式運作,並直接在核心空間中為使用者空間應用程式授予 Root 權限。

功能

KernelSU 的主要功能是它是以核心為基礎的。 KernelSU 在核心空間中執行,所以它可以向我們提供從未有過的核心介面。例如,我們可以在核心模式中為任何處理程序新增硬體中斷點;我們可以在任何處理程序的實體記憶體中存取,而無人知曉;我們可以在核心空間攔截任何系統呼叫;等等。

KernelSU 還提供了一個以 overlayfs 為基礎的模組系統,允許您將自訂外掛程式載入到系統中。它還提供了一種修改 /system 分割區中檔案的機制。

如何使用

請參閱:安裝

如何建置

请參閱:如何建置

討論

係依據 GPL3 授權發行。

+ \ No newline at end of file diff --git a/zh_TW/index.html b/zh_TW/index.html index 10854d4bd6b6..3d93cb8203d6 100644 --- a/zh_TW/index.html +++ b/zh_TW/index.html @@ -5,18 +5,19 @@ Android 上以核心為基礎的 Root 解決方案 | KernelSU - - + + + - - - - + + + + + -
Skip to content

KernelSU

Android 上以核心為基礎的 root 解決方案

KernelSU

以核心為基礎

KernelSU 以 Linux 核心模式運作,對使用者空間有更強的掌控。

白名單存取控制

僅有被授予 Root 權限的應用程式才可存取 `su`,而其他應用程式完全無法知悉。

模組支援

KernelSU 支援透過 overlayfs 修改 /system,它甚至可以使 /system 可寫入。

開放原始碼

KernelSU 是 GPL-3 授權下的開放原始碼專案。

係依據 GPL3 授權發行。

- +
Skip to content

KernelSU

Android 上以核心為基礎的 root 解決方案

KernelSU

係依據 GPL3 授權發行。

+ \ No newline at end of file