Compare commits
68 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0af1ab2845 | ||
|
|
c664aa66af | ||
|
|
7a449345d1 | ||
|
|
fa48b2fdaa | ||
|
|
f1f7c61aee | ||
|
|
ccc5cb74bf | ||
|
|
75fe8c9b6a | ||
|
|
a12c78d213 | ||
|
|
6c145179d4 | ||
|
|
ac05038e64 | ||
|
|
d5d2bb35d2 | ||
|
|
cb01bf428d | ||
|
|
7146210192 | ||
|
|
528df754a5 | ||
|
|
8a14f64964 | ||
|
|
bf4e12ce80 | ||
|
|
5497c0004d | ||
|
|
32baf8d9b0 | ||
|
|
763ce7e86c | ||
|
|
3a8ecf1253 | ||
|
|
7026c340b0 | ||
|
|
6dc803702b | ||
|
|
c64d196a10 | ||
|
|
c690d9c02c | ||
|
|
452fe3d508 | ||
|
|
e27d461eb0 | ||
|
|
a77faa1fe2 | ||
|
|
9a9623f578 | ||
|
|
cc1c66bb6f | ||
|
|
695e749e3e | ||
|
|
84c85ebe3c | ||
|
|
ed7dc01fb2 | ||
|
|
7d475e642f | ||
|
|
cead5b03f4 | ||
|
|
0c578e4518 | ||
|
|
fad6420405 | ||
|
|
415555aef8 | ||
|
|
f3e9033054 | ||
|
|
192323ad5c | ||
|
|
6973ce6a1f | ||
|
|
e8852223c4 | ||
|
|
42601b232c | ||
|
|
cd8b6ab382 | ||
|
|
3ed1d9aebc | ||
|
|
d83fc2aec6 | ||
|
|
098583f058 | ||
|
|
2521226745 | ||
|
|
942e5070f5 | ||
|
|
6726828e1d | ||
|
|
4418ebadc4 | ||
|
|
37368fb41c | ||
|
|
1e5db5d6bf | ||
|
|
067ced4a8c | ||
|
|
41d21e994c | ||
|
|
596dab1db8 | ||
|
|
71c0424512 | ||
|
|
49190feab6 | ||
|
|
9b37ec4a84 | ||
|
|
12f54b115c | ||
|
|
5586f9ce30 | ||
|
|
0789632efc | ||
|
|
c3533861f2 | ||
|
|
662a2b1237 | ||
|
|
436071a3f1 | ||
|
|
ea602be270 | ||
|
|
8c8f344161 | ||
|
|
1af8a87876 | ||
|
|
6640b5b7b6 |
24
.github/workflows/build-lkm.yml
vendored
@@ -24,23 +24,23 @@ jobs:
|
||||
matrix:
|
||||
include:
|
||||
- version: "android12-5.10"
|
||||
sub_level: 237
|
||||
os_patch_level: 2025-06
|
||||
sub_level: 240
|
||||
os_patch_level: 2025-09
|
||||
- version: "android13-5.10"
|
||||
sub_level: 236
|
||||
os_patch_level: 2025-05
|
||||
sub_level: 238
|
||||
os_patch_level: 2025-07
|
||||
- version: "android13-5.15"
|
||||
sub_level: 180
|
||||
os_patch_level: 2025-05
|
||||
sub_level: 189
|
||||
os_patch_level: 2025-09
|
||||
- version: "android14-5.15"
|
||||
sub_level: 180
|
||||
os_patch_level: 2025-05
|
||||
sub_level: 185
|
||||
os_patch_level: 2025-07
|
||||
- version: "android14-6.1"
|
||||
sub_level: 138
|
||||
os_patch_level: 2025-06
|
||||
sub_level: 145
|
||||
os_patch_level: 2025-09
|
||||
- version: "android15-6.6"
|
||||
sub_level: 89
|
||||
os_patch_level: 2025-06
|
||||
sub_level: 98
|
||||
os_patch_level: 2025-09
|
||||
# uses: ./.github/workflows/gki-kernel-mock.yml when debugging
|
||||
uses: ./.github/workflows/gki-kernel.yml
|
||||
with:
|
||||
|
||||
7
.github/workflows/build-manager-manual.yml
vendored
@@ -203,13 +203,6 @@ jobs:
|
||||
run: |
|
||||
export ANDROID_HOME=/root/.android/sdk
|
||||
export PATH=$ANDROID_HOME/platform-tools:$ANDROID_HOME/tools:$ANDROID_HOME/tools/bin:$PATH
|
||||
{
|
||||
echo 'org.gradle.parallel=true'
|
||||
echo 'org.gradle.vfs.watch=true'
|
||||
echo 'org.gradle.jvmargs=-Xmx2048m'
|
||||
echo 'android.native.buildOutput=verbose'
|
||||
} >> gradle.properties
|
||||
sed -i 's/org.gradle.configuration-cache=true//g' gradle.properties
|
||||
./gradlew clean assembleRelease
|
||||
|
||||
- name: Upload build artifact
|
||||
|
||||
40
.github/workflows/build-manager.yml
vendored
@@ -10,6 +10,7 @@ on:
|
||||
- 'userspace/ksud/**'
|
||||
- 'userspace/susfs/**'
|
||||
- 'userspace/kpmmgr/**'
|
||||
- 'userspace/user_scanner/**'
|
||||
pull_request:
|
||||
branches: [ "main" ]
|
||||
paths:
|
||||
@@ -109,6 +110,19 @@ jobs:
|
||||
target: ${{ matrix.target }}
|
||||
os: ${{ matrix.os }}
|
||||
|
||||
build-user_scanner:
|
||||
if: ${{ always() }}
|
||||
needs: [ check-build-lkm, build-lkm ]
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
- target: All-linux-android
|
||||
os: ubuntu-latest
|
||||
uses: ./.github/workflows/user_scanner.yml
|
||||
with:
|
||||
target: ${{ matrix.target }}
|
||||
os: ${{ matrix.os }}
|
||||
|
||||
build-ksud:
|
||||
if: ${{ always() }}
|
||||
needs: [ check-build-lkm, build-lkm ]
|
||||
@@ -176,6 +190,12 @@ jobs:
|
||||
- name: Setup Android SDK
|
||||
uses: android-actions/setup-android@v3
|
||||
|
||||
- name: Download all userscanner artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: userscanner-all-linux-android
|
||||
path: .
|
||||
|
||||
- name: Download arm64 susfs
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
@@ -225,16 +245,18 @@ jobs:
|
||||
mkdir -p app/src/main/jniLibs/arm64-v8a
|
||||
cp -f ../arm64-v8a/zakozakozako ../manager/app/src/main/jniLibs/arm64-v8a/libzakozakozako.so
|
||||
|
||||
- name: Build with Gradle
|
||||
- name: Copy user_scanner to app jniLibs
|
||||
run: |
|
||||
{
|
||||
echo 'org.gradle.parallel=true'
|
||||
echo 'org.gradle.vfs.watch=true'
|
||||
echo 'org.gradle.jvmargs=-Xmx2048m'
|
||||
echo 'android.native.buildOutput=verbose'
|
||||
} >> gradle.properties
|
||||
sed -i 's/org.gradle.configuration-cache=true//g' gradle.properties
|
||||
./gradlew clean assembleRelease
|
||||
mkdir -p app/src/main/jniLibs/arm64-v8a
|
||||
mkdir -p app/src/main/jniLibs/x86_64
|
||||
mkdir -p app/src/main/jniLibs/armeabi-v7a
|
||||
cp -f ../arm64-v8a/uid_scanner ../manager/app/src/main/jniLibs/arm64-v8a/libuid_scanner.so
|
||||
cp -f ../x86_64/uid_scanner ../manager/app/src/main/jniLibs/x86_64/libuid_scanner.so
|
||||
cp -f ../armeabi-v7a/uid_scanner ../manager/app/src/main/jniLibs/armeabi-v7a/libuid_scanner.so
|
||||
|
||||
- name: Build with Gradle
|
||||
run: ./gradlew clean assembleRelease
|
||||
|
||||
- name: Upload build artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
if: ${{ ( github.event_name != 'pull_request' && github.ref == 'refs/heads/main' ) || github.ref_type == 'tag' }}
|
||||
|
||||
67
.github/workflows/deploy-website.yml
vendored
@@ -1,67 +0,0 @@
|
||||
name: Deploy Website
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- website
|
||||
paths:
|
||||
- '.github/workflows/deploy-website.yml'
|
||||
- 'website/**'
|
||||
workflow_dispatch:
|
||||
|
||||
# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages
|
||||
permissions:
|
||||
contents: read
|
||||
pages: write
|
||||
id-token: write
|
||||
|
||||
# Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued.
|
||||
# However, do NOT cancel in-progress runs as we want to allow these production deployments to complete.
|
||||
concurrency:
|
||||
group: pages
|
||||
cancel-in-progress: false
|
||||
|
||||
jobs:
|
||||
# Build job
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
defaults:
|
||||
run:
|
||||
working-directory: ./website
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0 # Not needed if lastUpdated is not enabled
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: latest
|
||||
cache: yarn # or pnpm / yarn
|
||||
cache-dependency-path: website/yarn.lock
|
||||
- name: Setup Pages
|
||||
uses: actions/configure-pages@v5
|
||||
- name: Install dependencies
|
||||
run: yarn install --frozen-lockfile
|
||||
- name: Build with VitePress
|
||||
run: |
|
||||
yarn docs:build
|
||||
touch docs/.vitepress/dist/.nojekyll
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-pages-artifact@v3
|
||||
with:
|
||||
path: website/docs/.vitepress/dist
|
||||
|
||||
# Deployment job
|
||||
deploy:
|
||||
environment:
|
||||
name: github-pages
|
||||
url: ${{ steps.deployment.outputs.page_url }}
|
||||
needs: build
|
||||
runs-on: ubuntu-latest
|
||||
name: Deploy
|
||||
steps:
|
||||
- name: Deploy to GitHub Pages
|
||||
id: deployment
|
||||
uses: actions/deploy-pages@v4
|
||||
40
.github/workflows/user_scanner.yml
vendored
Normal file
@@ -0,0 +1,40 @@
|
||||
name: Build user_scanner
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ "mian" ]
|
||||
paths:
|
||||
- '.github/workflows/user_scanner.yml'
|
||||
- 'userspace/user_scanner/**'
|
||||
workflow_dispatch:
|
||||
workflow_call:
|
||||
inputs:
|
||||
target:
|
||||
required: true
|
||||
type: string
|
||||
os:
|
||||
required: false
|
||||
type: string
|
||||
default: self-hosted
|
||||
|
||||
jobs:
|
||||
build-user_scanner:
|
||||
name: Build userspace user_scanner
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Build user_scanner
|
||||
working-directory: ./userspace/user_scanner
|
||||
run: |
|
||||
$ANDROID_NDK_HOME/ndk-build
|
||||
|
||||
- name: Upload a Build Artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: userscanner-all-linux-android
|
||||
path: ./userspace/user_scanner/libs
|
||||
89
Website/.gitignore
vendored
Normal file
@@ -0,0 +1,89 @@
|
||||
node_modules
|
||||
pnpm-lock.yaml
|
||||
package-lock.json
|
||||
yarn.lock
|
||||
.wrangler
|
||||
.DS_Store
|
||||
.vscode
|
||||
.idea
|
||||
.vite_opt_cache
|
||||
|
||||
# Build artifacts
|
||||
docs/.vitepress/dist/
|
||||
docs/.vitepress/cache/
|
||||
docs/.vitepress/.temp/
|
||||
dist/
|
||||
|
||||
# Generated files
|
||||
docs/public/sw.js
|
||||
docs/public/favicon*.png
|
||||
docs/public/favicon.ico
|
||||
docs/public/android-chrome-*.png
|
||||
docs/public/apple-touch-icon*.png
|
||||
docs/public/safari-pinned-tab.svg
|
||||
docs/public/browserconfig.xml
|
||||
docs/public/site.webmanifest
|
||||
docs/public/manifest.webmanifest
|
||||
docs/public/mstile-*.png
|
||||
docs/public/favicons.html
|
||||
favicon-data.json
|
||||
|
||||
# Logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
|
||||
# Runtime data
|
||||
pids
|
||||
*.pid
|
||||
*.seed
|
||||
*.pid.lock
|
||||
|
||||
# Coverage directory used by tools like istanbul
|
||||
coverage/
|
||||
*.lcov
|
||||
|
||||
# Dependency directories
|
||||
.pnpm-store/
|
||||
|
||||
# Optional npm cache directory
|
||||
.npm
|
||||
|
||||
# Optional eslint cache
|
||||
.eslintcache
|
||||
|
||||
# Microbundle cache
|
||||
.rpt2_cache/
|
||||
.rts2_cache_cjs/
|
||||
.rts2_cache_es/
|
||||
.rts2_cache_umd/
|
||||
|
||||
# Optional REPL history
|
||||
.node_repl_history
|
||||
|
||||
# Output of 'npm pack'
|
||||
*.tgz
|
||||
|
||||
# Yarn Integrity file
|
||||
.yarn-integrity
|
||||
|
||||
# parcel-bundler cache (https://parceljs.org/)
|
||||
.cache
|
||||
.parcel-cache
|
||||
|
||||
# TypeScript cache
|
||||
*.tsbuildinfo
|
||||
|
||||
# Optional npm cache directory
|
||||
.npm
|
||||
|
||||
# Optional REPL history
|
||||
.node_repl_history
|
||||
|
||||
# Output of 'npm pack'
|
||||
*.tgz
|
||||
|
||||
# Stores VSCode versions used for testing VSCode extensions
|
||||
.vscode-test
|
||||
14
Website/.prettierignore
Normal file
@@ -0,0 +1,14 @@
|
||||
# Build artifacts
|
||||
node_modules/
|
||||
dist/
|
||||
docs/.vitepress/cache/
|
||||
docs/.vitepress/dist/
|
||||
|
||||
# Logs
|
||||
*.log
|
||||
|
||||
# Lockfiles backup
|
||||
pnpm-debug.log*
|
||||
|
||||
# Generated
|
||||
.DS_Store
|
||||
10
Website/.prettierrc.json
Normal file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/prettierrc",
|
||||
"singleQuote": true,
|
||||
"semi": false,
|
||||
"trailingComma": "es5",
|
||||
"printWidth": 100,
|
||||
"tabWidth": 2,
|
||||
"arrowParens": "always",
|
||||
"endOfLine": "lf"
|
||||
}
|
||||
7
Website/README.md
Normal file
@@ -0,0 +1,7 @@
|
||||
# SukiSU-Ultra Documentation
|
||||
|
||||
[](https://sukisu.org)
|
||||
[](https://vitepress.dev)
|
||||
[](https://sukisu.org/guide/license)
|
||||
[](https://github.com/sukisu-ultra/sukisu-ultra/commits/main)
|
||||
[](https://github.com/sukisu-ultra/sukisu-ultra/stargazers)
|
||||
18
Website/docs/.gitignore
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
/coverage
|
||||
/src/client/shared.ts
|
||||
/src/node/shared.ts
|
||||
*.log
|
||||
*.tgz
|
||||
.DS_Store
|
||||
.idea
|
||||
.temp
|
||||
.vite_opt_cache
|
||||
.vscode
|
||||
dist
|
||||
cache
|
||||
temp
|
||||
examples-temp
|
||||
node_modules
|
||||
pnpm-global
|
||||
TODOs.md
|
||||
*.timestamp-*.mjs
|
||||
248
Website/docs/.vitepress/config.ts
Normal file
@@ -0,0 +1,248 @@
|
||||
import { defineConfig } from 'vitepress'
|
||||
import { groupIconMdPlugin, groupIconVitePlugin } from 'vitepress-plugin-group-icons'
|
||||
import {
|
||||
GitChangelog,
|
||||
GitChangelogMarkdownSection,
|
||||
} from '@nolebase/vitepress-plugin-git-changelog/vite'
|
||||
|
||||
import footnote from 'markdown-it-footnote'
|
||||
import mark from 'markdown-it-mark'
|
||||
import sub from 'markdown-it-sub'
|
||||
import taskLists from 'markdown-it-task-lists'
|
||||
|
||||
export default defineConfig({
|
||||
title: 'SukiSU-Ultra',
|
||||
description: 'Next-Generation Android root solution.',
|
||||
|
||||
lastUpdated: true,
|
||||
cleanUrls: true,
|
||||
metaChunk: true,
|
||||
|
||||
// Global performance optimizations
|
||||
cacheDir: './.vitepress/cache',
|
||||
ignoreDeadLinks: false,
|
||||
|
||||
// Enhanced markdown with performance focus
|
||||
markdown: {
|
||||
math: true,
|
||||
config(md) {
|
||||
md.use(groupIconMdPlugin)
|
||||
md.use(footnote)
|
||||
md.use(mark)
|
||||
md.use(sub)
|
||||
md.use(taskLists)
|
||||
},
|
||||
linkify: true,
|
||||
typographer: true,
|
||||
lineNumbers: true,
|
||||
image: {
|
||||
lazyLoading: true,
|
||||
},
|
||||
toc: {
|
||||
level: [1, 2, 3],
|
||||
},
|
||||
theme: {
|
||||
light: 'github-light',
|
||||
dark: 'github-dark',
|
||||
},
|
||||
},
|
||||
sitemap: {
|
||||
hostname: 'https://sukisu.org',
|
||||
transformItems(items) {
|
||||
return items
|
||||
.filter((item) => !item.url.includes('404'))
|
||||
.map((item) => ({
|
||||
...item,
|
||||
changefreq:
|
||||
item.url === '/' ? 'daily' : item.url.includes('/guide/') ? 'weekly' : 'monthly',
|
||||
priority: item.url === '/' ? 1.0 : item.url.includes('/guide/') ? 0.9 : 0.7,
|
||||
}))
|
||||
},
|
||||
},
|
||||
|
||||
// Critical performance transformations
|
||||
transformPageData(pageData) {
|
||||
const canonicalUrl = `https://sukisu.org${pageData.relativePath}`
|
||||
.replace(/index\.md$/, '')
|
||||
.replace(/\.md$/, '')
|
||||
|
||||
pageData.frontmatter.head ??= []
|
||||
pageData.frontmatter.head.push(
|
||||
['link', { rel: 'canonical', href: canonicalUrl }],
|
||||
['meta', { property: 'og:url', content: canonicalUrl }],
|
||||
['link', { rel: 'preload', href: '/logo.svg', as: 'image' }]
|
||||
)
|
||||
|
||||
return pageData
|
||||
},
|
||||
|
||||
head: [
|
||||
// Critical resource hints for global performance
|
||||
['link', { rel: 'dns-prefetch', href: '//github.com' }],
|
||||
['link', { rel: 'dns-prefetch', href: '//t.me' }],
|
||||
['link', { rel: 'dns-prefetch', href: '//sukisu.org' }],
|
||||
|
||||
// Essential favicon setup - synced from /favicon during build/dev
|
||||
['link', { rel: 'icon', type: 'image/x-icon', href: '/favicon.ico' }],
|
||||
['link', { rel: 'icon', type: 'image/svg+xml', href: '/favicon.svg' }],
|
||||
['link', { rel: 'icon', type: 'image/png', sizes: '96x96', href: '/favicon-96x96.png' }],
|
||||
['link', { rel: 'apple-touch-icon', sizes: '180x180', href: '/apple-touch-icon.png' }],
|
||||
['link', { rel: 'mask-icon', href: '/safari-pinned-tab.svg', color: '#64edff' }],
|
||||
// (Removed msapplication meta to avoid referencing non-existent files)
|
||||
|
||||
// Web App Manifest
|
||||
['link', { rel: 'manifest', href: '/site.webmanifest' }],
|
||||
|
||||
// Theme and app configuration
|
||||
['meta', { name: 'theme-color', content: '#64edff' }],
|
||||
['meta', { name: 'application-name', content: 'SukiSU-Ultra' }],
|
||||
['meta', { name: 'apple-mobile-web-app-title', content: 'SukiSU-Ultra' }],
|
||||
['meta', { name: 'apple-mobile-web-app-capable', content: 'yes' }],
|
||||
['meta', { name: 'apple-mobile-web-app-status-bar-style', content: 'default' }],
|
||||
|
||||
// Viewport and mobile optimization
|
||||
[
|
||||
'meta',
|
||||
{ name: 'viewport', content: 'width=device-width, initial-scale=1.0, viewport-fit=cover' },
|
||||
],
|
||||
['meta', { name: 'format-detection', content: 'telephone=no' }],
|
||||
['meta', { property: 'og:type', content: 'website' }],
|
||||
['meta', { property: 'og:site_name', content: 'SukiSU-Ultra' }],
|
||||
['meta', { property: 'og:url', content: 'https://sukisu.org/' }],
|
||||
['meta', { property: 'og:locale', content: 'en_US' }],
|
||||
['meta', { property: 'og:locale:alternate', content: 'zh_CN' }],
|
||||
|
||||
// Twitter optimization for global audience
|
||||
['meta', { property: 'twitter:card', content: 'summary_large_image' }],
|
||||
['meta', { property: 'twitter:site', content: '@sukisu_ultra' }],
|
||||
['meta', { property: 'twitter:creator', content: '@sukisu_ultra' }],
|
||||
// (Removed Twitter image as no PNG social image is provided)
|
||||
|
||||
// Additional SEO optimizations
|
||||
[
|
||||
'meta',
|
||||
{
|
||||
name: 'robots',
|
||||
content: 'index, follow, max-image-preview:large, max-snippet:-1, max-video-preview:-1',
|
||||
},
|
||||
],
|
||||
['meta', { name: 'bingbot', content: 'index, follow' }],
|
||||
['meta', { name: 'referrer', content: 'strict-origin-when-cross-origin' }],
|
||||
|
||||
// Global SEO optimization
|
||||
[
|
||||
'meta',
|
||||
{
|
||||
name: 'keywords',
|
||||
content:
|
||||
'Android root, KernelSU, SukiSU-Ultra, Android kernel, root management, 安卓 root, カーネル, рут',
|
||||
},
|
||||
],
|
||||
['meta', { name: 'author', content: 'SukiSU-Ultra Team' }],
|
||||
|
||||
// Enhanced structured data for global search engines
|
||||
[
|
||||
'script',
|
||||
{ type: 'application/ld+json' },
|
||||
JSON.stringify({
|
||||
'@context': 'https://schema.org',
|
||||
'@type': 'SoftwareApplication',
|
||||
name: 'SukiSU-Ultra',
|
||||
description: 'Next-Generation Android Root Solution',
|
||||
applicationCategory: 'SystemApplication',
|
||||
operatingSystem: 'Android',
|
||||
url: 'https://sukisu.org',
|
||||
downloadUrl: 'https://github.com/sukisu-ultra/sukisu-ultra/releases',
|
||||
supportingData: {
|
||||
'@type': 'DataCatalog',
|
||||
name: 'Compatibility Database',
|
||||
},
|
||||
offers: {
|
||||
'@type': 'Offer',
|
||||
price: '0',
|
||||
priceCurrency: 'USD',
|
||||
},
|
||||
author: {
|
||||
'@type': 'Organization',
|
||||
name: 'SukiSU-Ultra Team',
|
||||
url: 'https://github.com/sukisu-ultra',
|
||||
},
|
||||
}),
|
||||
],
|
||||
|
||||
// PWA optimization for global mobile users (manifest declared above)
|
||||
['meta', { name: 'apple-mobile-web-app-capable', content: 'yes' }],
|
||||
['meta', { name: 'apple-mobile-web-app-status-bar-style', content: 'black-translucent' }],
|
||||
['meta', { name: 'apple-mobile-web-app-title', content: 'SukiSU-Ultra' }],
|
||||
|
||||
// Cloudflare Web Analytics
|
||||
[
|
||||
'script',
|
||||
{
|
||||
defer: '',
|
||||
src: 'https://static.cloudflareinsights.com/beacon.min.js',
|
||||
'data-cf-beacon': '{"token": "dcc5feef58bf4c56a170a99f4cec4798"}',
|
||||
},
|
||||
],
|
||||
],
|
||||
|
||||
themeConfig: {
|
||||
logo: { src: '/logo.svg', width: 24, height: 24 },
|
||||
|
||||
socialLinks: [
|
||||
{ icon: 'github', link: 'https://github.com/sukisu-ultra/sukisu-ultra' },
|
||||
{
|
||||
icon: {
|
||||
svg: '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon icon-tabler icons-tabler-outline icon-tabler-brand-telegram"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M15 10l-4 4l6 6l4 -16l-18 7l4 2l2 6l3 -4" /></svg>',
|
||||
},
|
||||
link: 'https://t.me/sukiksu',
|
||||
},
|
||||
],
|
||||
search: {
|
||||
provider: 'local',
|
||||
},
|
||||
},
|
||||
|
||||
rewrites: {
|
||||
'en/:rest*': ':rest*',
|
||||
},
|
||||
|
||||
locales: {
|
||||
root: {
|
||||
label: 'English',
|
||||
},
|
||||
zh: {
|
||||
label: '简体中文',
|
||||
link: '/zh/',
|
||||
},
|
||||
},
|
||||
|
||||
vite: {
|
||||
plugins: [
|
||||
groupIconVitePlugin({
|
||||
customIcon: {
|
||||
bash: '<svg xmlns="http://www.w3.org/2000/svg" width="256" height="256" viewBox="0 0 256 256"><g fill="none"><rect width="256" height="256" fill="#242938" rx="60"/><path fill="#242938" fill-rule="evenodd" d="m203.819 68.835l-63.14-37.48a23.79 23.79 0 0 0-24.361 0l-63.14 37.48C45.642 73.31 41 81.575 41 90.522v74.961c0 8.945 4.643 17.215 12.18 21.689l63.14 37.473a23.8 23.8 0 0 0 12.179 3.354a23.8 23.8 0 0 0 12.178-3.354l63.14-37.473c7.536-4.474 12.182-12.744 12.182-21.689v-74.96c0-8.948-4.646-17.214-12.18-21.688" clip-rule="evenodd"/><path fill="#fff" fill-rule="evenodd" d="m118.527 220.808l-63.14-37.474c-6.176-3.666-10.013-10.506-10.013-17.852V90.523c0-7.346 3.837-14.186 10.01-17.85l63.143-37.48a19.55 19.55 0 0 1 9.972-2.747c3.495 0 6.943.95 9.973 2.747l63.14 37.48c5.204 3.089 8.714 8.438 9.701 14.437c-2.094-4.469-6.817-5.684-12.32-2.47l-59.734 36.897c-7.448 4.354-12.94 9.24-12.945 18.221v73.604c-.004 5.378 2.168 8.861 5.504 9.871c-1.096.19-2.201.322-3.319.322a19.55 19.55 0 0 1-9.972-2.747m85.292-151.974l-63.14-37.478A23.8 23.8 0 0 0 128.499 28a23.8 23.8 0 0 0-12.181 3.356l-63.14 37.478C45.642 73.308 41 81.576 41 90.524v74.958c0 8.945 4.643 17.215 12.18 21.689l63.14 37.475A23.84 23.84 0 0 0 128.499 228a23.83 23.83 0 0 0 12.178-3.354l63.142-37.475c7.536-4.474 12.18-12.744 12.18-21.689V90.523c0-8.947-4.644-17.215-12.18-21.689" clip-rule="evenodd"/><path fill="#47b353" fill-rule="evenodd" d="m187.267 172.729l-15.722 9.41c-.417.243-.723.516-.726 1.017v4.114c0 .503.338.712.754.467l15.966-9.703c.416-.243.48-.708.483-1.209v-3.629c0-.5-.338-.71-.755-.467" clip-rule="evenodd"/><path fill="#242938" fill-rule="evenodd" d="M153.788 138.098c.509-.258.928.059.935.725l.053 5.439c2.277-.906 4.255-1.148 6.047-.734c.389.104.561.633.402 1.261l-1.197 4.82c-.093.364-.298.732-.545.961a1.3 1.3 0 0 1-.315.234a.7.7 0 0 1-.472.077c-.818-.185-2.763-.61-5.823.94c-3.21 1.625-4.333 4.414-4.311 6.484c.027 2.472 1.295 3.221 5.673 3.296c5.834.097 8.355 2.646 8.416 8.522c.06 5.77-3.02 11.966-7.732 15.763l.104 5.384c.006.648-.415 1.391-.924 1.649l-3.189 1.837c-.511.258-.93-.06-.937-.708l-.055-5.296c-2.731 1.135-5.499 1.409-7.267.699c-.333-.13-.476-.622-.344-1.182l1.156-4.868c.092-.384.295-.768.571-1.012q.147-.142.299-.219c.183-.092.362-.112.514-.055c1.905.642 4.342.342 6.685-.844c2.977-1.506 4.968-4.542 4.937-7.558c-.029-2.737-1.51-3.874-5.113-3.901c-4.586.013-8.861-.891-8.932-7.642c-.057-5.558 2.833-11.342 7.408-14.999l-.057-5.435c-.007-.668.401-1.403.926-1.667z" clip-rule="evenodd"/></g></svg>',
|
||||
},
|
||||
}),
|
||||
GitChangelog({ repoURL: () => 'https://github.com/SukiSU-Ultra/Website' }),
|
||||
GitChangelogMarkdownSection({
|
||||
exclude: (id) => id.endsWith('index.md'),
|
||||
sections: { disableContributors: true },
|
||||
}),
|
||||
],
|
||||
build: {
|
||||
minify: 'terser',
|
||||
chunkSizeWarningLimit: 800,
|
||||
assetsInlineLimit: 8192,
|
||||
target: 'esnext',
|
||||
cssCodeSplit: true,
|
||||
sourcemap: false,
|
||||
},
|
||||
|
||||
server: {
|
||||
fs: {
|
||||
allow: ['..'],
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
195
Website/docs/.vitepress/theme/components/ArticleShare.vue
Normal file
@@ -0,0 +1,195 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, computed } from 'vue'
|
||||
import { useData } from 'vitepress'
|
||||
|
||||
const props = defineProps({
|
||||
shareText: {
|
||||
type: String,
|
||||
default: undefined,
|
||||
},
|
||||
copiedText: {
|
||||
type: String,
|
||||
default: undefined,
|
||||
},
|
||||
includeQuery: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
includeHash: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
copiedTimeout: {
|
||||
type: Number,
|
||||
default: 2000,
|
||||
},
|
||||
})
|
||||
|
||||
defineOptions({ name: 'ArticleShare' })
|
||||
|
||||
const copied = ref(false)
|
||||
const isClient = typeof window !== 'undefined' && typeof document !== 'undefined'
|
||||
|
||||
const { theme, lang } = useData()
|
||||
|
||||
const defaultShareText = computed(() =>
|
||||
lang.value?.toLowerCase().startsWith('zh') ? '分享链接' : 'Share link'
|
||||
)
|
||||
const defaultCopiedText = computed(() =>
|
||||
lang.value?.toLowerCase().startsWith('zh') ? '已复制!' : 'Copied!'
|
||||
)
|
||||
const defaultCopyFailedText = computed(() =>
|
||||
lang.value?.toLowerCase().startsWith('zh') ? '复制链接失败:' : 'Failed to copy link:'
|
||||
)
|
||||
|
||||
const i18nShareText = computed(
|
||||
() => props.shareText ?? (theme.value as any)?.articleShare?.shareText ?? defaultShareText.value
|
||||
)
|
||||
const i18nCopiedText = computed(
|
||||
() =>
|
||||
props.copiedText ?? (theme.value as any)?.articleShare?.copiedText ?? defaultCopiedText.value
|
||||
)
|
||||
const i18nCopyFailedText = computed(
|
||||
() => (theme.value as any)?.articleShare?.copyFailed ?? defaultCopyFailedText.value
|
||||
)
|
||||
|
||||
const shareLink = computed(() => {
|
||||
if (!isClient) return ''
|
||||
|
||||
const { origin, pathname, search, hash } = window.location
|
||||
const finalSearch = props.includeQuery ? search : ''
|
||||
const finalHash = props.includeHash ? hash : ''
|
||||
return `${origin}${pathname}${finalSearch}${finalHash}`
|
||||
})
|
||||
|
||||
async function copyToClipboard() {
|
||||
if (copied.value || !isClient) return
|
||||
|
||||
try {
|
||||
if (navigator.clipboard) {
|
||||
await navigator.clipboard.writeText(shareLink.value)
|
||||
} else {
|
||||
const input = document.createElement('input')
|
||||
input.setAttribute('readonly', 'readonly')
|
||||
input.setAttribute('value', shareLink.value)
|
||||
document.body.appendChild(input)
|
||||
input.select()
|
||||
document.execCommand('copy')
|
||||
document.body.removeChild(input)
|
||||
}
|
||||
|
||||
copied.value = true
|
||||
setTimeout(() => {
|
||||
copied.value = false
|
||||
}, props.copiedTimeout)
|
||||
} catch (error) {
|
||||
console.error(i18nCopyFailedText.value, error)
|
||||
}
|
||||
}
|
||||
|
||||
const shareIconSvg = `
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M4 12v8a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2v-8"></path>
|
||||
<polyline points="16 6 12 2 8 6"></polyline>
|
||||
<line x1="12" y1="2" x2="12" y2="15"></line>
|
||||
</svg>
|
||||
`
|
||||
|
||||
const copiedIconSvg = `
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M20 6 9 17l-5-5"></path>
|
||||
</svg>
|
||||
`
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="article-share">
|
||||
<button
|
||||
:class="['article-share__button', { copied: copied }]"
|
||||
:aria-label="copied ? i18nCopiedText : i18nShareText"
|
||||
aria-live="polite"
|
||||
@click="copyToClipboard"
|
||||
>
|
||||
<div v-if="!copied" class="content-wrapper">
|
||||
<span class="icon" v-html="shareIconSvg"></span>
|
||||
{{ i18nShareText }}
|
||||
</div>
|
||||
|
||||
<div v-else class="content-wrapper">
|
||||
<span class="icon" v-html="copiedIconSvg"></span>
|
||||
{{ i18nCopiedText }}
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.article-share {
|
||||
padding: 14px 0;
|
||||
}
|
||||
|
||||
.article-share__button {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
font-weight: 500;
|
||||
font-size: 14px;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
transition: all 0.4s var(--ease-out-cubic, cubic-bezier(0.33, 1, 0.68, 1));
|
||||
cursor: pointer;
|
||||
border: 1px solid transparent;
|
||||
border-radius: 14px;
|
||||
padding: 7px 14px;
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
color: var(--vp-c-text-1, #333);
|
||||
background-color: var(--vp-c-bg-alt, #f6f6f7);
|
||||
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.02);
|
||||
will-change: transform, box-shadow;
|
||||
}
|
||||
|
||||
.article-share__button::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: -100%;
|
||||
z-index: -1;
|
||||
transition: left 0.6s ease;
|
||||
background-color: var(--vp-c-brand-soft, #ddf4ff);
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.article-share__button:hover {
|
||||
transform: translateY(-1px);
|
||||
border-color: var(--vp-c-brand-soft, #ddf4ff);
|
||||
background-color: var(--vp-c-brand-soft, #ddf4ff);
|
||||
}
|
||||
|
||||
.article-share__button:active {
|
||||
transform: scale(0.9);
|
||||
}
|
||||
|
||||
.article-share__button.copied {
|
||||
color: var(--vp-c-brand-1, #007acc);
|
||||
background-color: var(--vp-c-brand-soft, #ddf4ff);
|
||||
}
|
||||
|
||||
.article-share__button.copied::before {
|
||||
left: 0;
|
||||
background-color: var(--vp-c-brand-soft, #ddf4ff);
|
||||
}
|
||||
|
||||
.content-wrapper {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.icon {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
margin-right: 6px;
|
||||
}
|
||||
</style>
|
||||
12
Website/docs/.vitepress/theme/components/Confetti.vue
Normal file
@@ -0,0 +1,12 @@
|
||||
<script setup lang="ts">
|
||||
import confetti from 'canvas-confetti'
|
||||
|
||||
const media = window.matchMedia('(prefers-reduced-motion: reduce)')
|
||||
if (!media.matches) {
|
||||
confetti({
|
||||
particleCount: 100,
|
||||
spread: 170,
|
||||
origin: { y: 0.6 },
|
||||
})
|
||||
}
|
||||
</script>
|
||||
150
Website/docs/.vitepress/theme/components/backtotop.vue
Normal file
@@ -0,0 +1,150 @@
|
||||
<script setup>
|
||||
import { onBeforeUnmount, onMounted, ref, computed } from 'vue'
|
||||
|
||||
const showBackTop = ref(false) // 初始状态设为false
|
||||
const scrollProgress = ref(0)
|
||||
|
||||
// 圆形进度条计算
|
||||
const radius = 42
|
||||
const circumference = computed(() => 2 * Math.PI * radius)
|
||||
|
||||
function scrollToTop() {
|
||||
window.scrollTo({
|
||||
top: 0,
|
||||
behavior: 'smooth',
|
||||
})
|
||||
}
|
||||
|
||||
// 使用更高效的节流函数
|
||||
function throttle(fn, delay = 50) {
|
||||
let timer = null
|
||||
return function (...args) {
|
||||
if (!timer) {
|
||||
timer = setTimeout(() => {
|
||||
fn.apply(this, args)
|
||||
timer = null
|
||||
}, delay)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const updateScrollProgress = () => {
|
||||
const { scrollY, innerHeight } = window
|
||||
const { scrollHeight } = document.documentElement
|
||||
const totalScroll = scrollHeight - innerHeight
|
||||
scrollProgress.value = totalScroll > 0 ? Math.min(scrollY / totalScroll, 1) : 0
|
||||
}
|
||||
|
||||
const handleScroll = throttle(() => {
|
||||
// 当滚动超过100px时显示,否则隐藏
|
||||
const shouldShow = window.scrollY > 100
|
||||
showBackTop.value = shouldShow
|
||||
updateScrollProgress()
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
window.addEventListener('scroll', handleScroll)
|
||||
updateScrollProgress()
|
||||
})
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
window.removeEventListener('scroll', handleScroll)
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Transition name="fade">
|
||||
<div class="back-top-container" v-show="showBackTop">
|
||||
<svg class="progress-ring" viewBox="0 0 100 100">
|
||||
<circle class="progress-ring-background" cx="50" cy="50" r="42" />
|
||||
<circle
|
||||
class="progress-ring-circle"
|
||||
cx="50"
|
||||
cy="50"
|
||||
r="42"
|
||||
:style="{ 'stroke-dashoffset': circumference - scrollProgress * circumference }"
|
||||
/>
|
||||
</svg>
|
||||
<div class="vitepress-backTop-main" title="返回顶部" @click="scrollToTop()">
|
||||
<svg class="icon" viewBox="0 0 1024 1024">
|
||||
<path
|
||||
d="M752.736 431.063C757.159 140.575 520.41 8.97 504.518 0.41V0l-0.45 0.205-0.41-0.205v0.41c-15.934 8.56-252.723 140.165-248.259 430.653-48.21 31.457-98.713 87.368-90.685 184.074 8.028 96.666 101.007 160.768 136.601 157.287 35.595-3.482 25.232-30.31 25.232-30.31l12.206-50.095s52.47 80.569 69.304 80.528c15.114-1.23 87-0.123 95.6 0h0.82c8.602-0.123 80.486-1.23 95.6 0 16.794 0 69.305-80.528 69.305-80.528l12.165 50.094s-10.322 26.83 25.272 30.31c35.595 3.482 128.574-60.62 136.602-157.286 8.028-96.665-42.475-152.617-90.685-184.074z m-248.669-4.26c-6.758-0.123-94.781-3.359-102.891-107.192 2.95-98.714 95.97-107.438 102.891-107.93 6.964 0.492 99.943 9.216 102.892 107.93-8.11 103.833-96.174 107.07-102.892 107.192z m-52.019 500.531c0 11.838-9.42 21.382-21.012 21.382a21.217 21.217 0 0 1-21.054-21.34V821.74c0-11.797 9.421-21.382 21.054-21.382 11.591 0 21.012 9.585 21.012 21.382v105.635z m77.333 57.222a21.504 21.504 0 0 1-21.34 21.626 21.504 21.504 0 0 1-21.34-21.626V827.474c0-11.96 9.543-21.668 21.299-21.668 11.796 0 21.38 9.708 21.38 21.668v157.082z m71.147-82.043c0 11.796-9.42 21.34-21.053 21.34a21.217 21.217 0 0 1-21.013-21.34v-75.367c0-11.755 9.421-21.299 21.013-21.299 11.632 0 21.053 9.544 21.053 21.3v75.366z"
|
||||
fill="#FFF"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
</Transition>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.back-top-container {
|
||||
position: fixed;
|
||||
bottom: 20px;
|
||||
right: 20px;
|
||||
width: 60px;
|
||||
height: 60px;
|
||||
z-index: 999;
|
||||
}
|
||||
|
||||
.vitepress-backTop-main {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
cursor: pointer;
|
||||
width: 44px;
|
||||
height: 44px;
|
||||
border-radius: 50%;
|
||||
background-color: #3eaf7c;
|
||||
padding: 8px;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
z-index: 2;
|
||||
transition: background-color 0.2s ease;
|
||||
}
|
||||
|
||||
.vitepress-backTop-main:hover {
|
||||
background-color: #71cda3;
|
||||
}
|
||||
|
||||
.progress-ring {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
transform: rotate(-90deg);
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.progress-ring-background {
|
||||
fill: none;
|
||||
stroke: rgba(62, 175, 124, 0.15);
|
||||
stroke-width: 3;
|
||||
}
|
||||
|
||||
.progress-ring-circle {
|
||||
fill: none;
|
||||
stroke: #3eaf7c;
|
||||
stroke-width: 3;
|
||||
stroke-dasharray: 264; /* 2 * π * 42 */
|
||||
stroke-linecap: round;
|
||||
transition: stroke-dashoffset 0.15s ease-out;
|
||||
}
|
||||
|
||||
.icon {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
}
|
||||
|
||||
.fade-enter-active,
|
||||
.fade-leave-active {
|
||||
transition: opacity 0.3s ease;
|
||||
}
|
||||
|
||||
.fade-enter-from,
|
||||
.fade-leave-to {
|
||||
opacity: 0;
|
||||
}
|
||||
</style>
|
||||
33
Website/docs/.vitepress/theme/index.ts
Normal file
@@ -0,0 +1,33 @@
|
||||
// .vitepress/theme/index.ts
|
||||
import DefaultTheme from 'vitepress/theme'
|
||||
import { NolebaseGitChangelogPlugin } from '@nolebase/vitepress-plugin-git-changelog/client'
|
||||
import 'virtual:group-icons.css'
|
||||
import { h, onMounted } from 'vue'
|
||||
import './style/style.css'
|
||||
import ArticleShare from './components/ArticleShare.vue'
|
||||
import backtotop from './components/backtotop.vue'
|
||||
import '@nolebase/vitepress-plugin-git-changelog/client/style.css'
|
||||
|
||||
export default {
|
||||
extends: DefaultTheme,
|
||||
Layout: () => {
|
||||
return h(DefaultTheme.Layout, null, {
|
||||
'aside-outline-before': () => h(ArticleShare),
|
||||
'doc-footer-before': () => h(backtotop),
|
||||
})
|
||||
},
|
||||
enhanceApp({ app }) {
|
||||
app.use(NolebaseGitChangelogPlugin)
|
||||
|
||||
// Register service worker in production for offline support and caching
|
||||
if (
|
||||
typeof window !== 'undefined' &&
|
||||
'serviceWorker' in navigator &&
|
||||
(import.meta as any).env?.PROD
|
||||
) {
|
||||
onMounted(() => {
|
||||
navigator.serviceWorker.register('/sw.js').catch(() => {})
|
||||
})
|
||||
}
|
||||
},
|
||||
}
|
||||
192
Website/docs/.vitepress/theme/style/custom-block.css
Normal file
@@ -0,0 +1,192 @@
|
||||
/* .vitepress/theme/style/custom-block.css */
|
||||
/* 深浅色卡 */
|
||||
:root {
|
||||
--custom-block-info-left: #cccccc;
|
||||
--custom-block-info-bg: #fafafa;
|
||||
|
||||
--custom-block-tip-left: #009400;
|
||||
--custom-block-tip-bg: #e6f6e6;
|
||||
|
||||
--custom-block-warning-left: #e6a700;
|
||||
--custom-block-warning-bg: #fff8e6;
|
||||
|
||||
--custom-block-danger-left: #e13238;
|
||||
--custom-block-danger-bg: #ffebec;
|
||||
|
||||
--custom-block-note-left: #4cb3d4;
|
||||
--custom-block-note-bg: #eef9fd;
|
||||
|
||||
--custom-block-important-left: #a371f7;
|
||||
--custom-block-important-bg: #f4eefe;
|
||||
|
||||
--custom-block-caution-left: #e0575b;
|
||||
--custom-block-caution-bg: #fde4e8;
|
||||
}
|
||||
|
||||
.dark {
|
||||
--custom-block-info-left: #cccccc;
|
||||
--custom-block-info-bg: #474748;
|
||||
|
||||
--custom-block-tip-left: #009400;
|
||||
--custom-block-tip-bg: #003100;
|
||||
|
||||
--custom-block-warning-left: #e6a700;
|
||||
--custom-block-warning-bg: #4d3800;
|
||||
|
||||
--custom-block-danger-left: #e13238;
|
||||
--custom-block-danger-bg: #4b1113;
|
||||
|
||||
--custom-block-note-left: #4cb3d4;
|
||||
--custom-block-note-bg: #193c47;
|
||||
|
||||
--custom-block-important-left: #a371f7;
|
||||
--custom-block-important-bg: #230555;
|
||||
|
||||
--custom-block-caution-left: #e0575b;
|
||||
--custom-block-caution-bg: #391c22;
|
||||
}
|
||||
|
||||
/* 标题字体大小 */
|
||||
.custom-block-title {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
/* info容器:背景色、左侧 */
|
||||
.custom-block.info {
|
||||
border-left: 5px solid var(--custom-block-info-left);
|
||||
background-color: var(--custom-block-info-bg);
|
||||
}
|
||||
|
||||
/* info容器:svg图 */
|
||||
.custom-block.info [class*='custom-block-title']::before {
|
||||
content: '';
|
||||
background-image: url("data:image/svg+xml;utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M12 22C6.477 22 2 17.523 2 12S6.477 2 12 2s10 4.477 10 10-4.477 10-10 10zm-1-11v6h2v-6h-2zm0-4v2h2V7h-2z' fill='%23ccc'/%3E%3C/svg%3E");
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
position: relative;
|
||||
margin-right: 4px;
|
||||
left: -5px;
|
||||
top: -1px;
|
||||
}
|
||||
|
||||
/* 提示容器:边框色、背景色、左侧 */
|
||||
.custom-block.tip {
|
||||
/* border-color: var(--custom-block-tip); */
|
||||
border-left: 5px solid var(--custom-block-tip-left);
|
||||
background-color: var(--custom-block-tip-bg);
|
||||
}
|
||||
|
||||
/* 提示容器:svg图 */
|
||||
.custom-block.tip [class*='custom-block-title']::before {
|
||||
content: '';
|
||||
background-image: url("data:image/svg+xml;utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath fill='%23009400' d='M7.941 18c-.297-1.273-1.637-2.314-2.187-3a8 8 0 1 1 12.49.002c-.55.685-1.888 1.726-2.185 2.998H7.94zM16 20v1a2 2 0 0 1-2 2h-4a2 2 0 0 1-2-2v-1h8zm-3-9.995V6l-4.5 6.005H11v4l4.5-6H13z'/%3E%3C/svg%3E");
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
position: relative;
|
||||
margin-right: 4px;
|
||||
left: -5px;
|
||||
top: -2px;
|
||||
}
|
||||
|
||||
/* 警告容器:背景色、左侧 */
|
||||
.custom-block.warning {
|
||||
border-left: 5px solid var(--custom-block-warning-left);
|
||||
background-color: var(--custom-block-warning-bg);
|
||||
}
|
||||
|
||||
/* 警告容器:svg图 */
|
||||
.custom-block.warning [class*='custom-block-title']::before {
|
||||
content: '';
|
||||
background-image: url("data:image/svg+xml;utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 1024 1024'%3E%3Cpath d='M576.286 752.57v-95.425q0-7.031-4.771-11.802t-11.3-4.772h-96.43q-6.528 0-11.3 4.772t-4.77 11.802v95.424q0 7.031 4.77 11.803t11.3 4.77h96.43q6.528 0 11.3-4.77t4.77-11.803zm-1.005-187.836 9.04-230.524q0-6.027-5.022-9.543-6.529-5.524-12.053-5.524H456.754q-5.524 0-12.053 5.524-5.022 3.516-5.022 10.547l8.538 229.52q0 5.023 5.022 8.287t12.053 3.265h92.913q7.032 0 11.803-3.265t5.273-8.287zM568.25 95.65l385.714 707.142q17.578 31.641-1.004 63.282-8.538 14.564-23.354 23.102t-31.892 8.538H126.286q-17.076 0-31.892-8.538T71.04 866.074q-18.582-31.641-1.004-63.282L455.75 95.65q8.538-15.57 23.605-24.61T512 62t32.645 9.04 23.605 24.61z' fill='%23e6a700'/%3E%3C/svg%3E");
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
position: relative;
|
||||
margin-right: 4px;
|
||||
left: -5px;
|
||||
}
|
||||
|
||||
/* 危险容器:背景色、左侧 */
|
||||
.custom-block.danger {
|
||||
border-left: 5px solid var(--custom-block-danger-left);
|
||||
background-color: var(--custom-block-danger-bg);
|
||||
}
|
||||
|
||||
/* 危险容器:svg图 */
|
||||
.custom-block.danger [class*='custom-block-title']::before {
|
||||
content: '';
|
||||
background-image: url("data:image/svg+xml;utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M12 2c5.523 0 10 4.477 10 10v3.764a2 2 0 0 1-1.106 1.789L18 19v1a3 3 0 0 1-2.824 2.995L14.95 23a2.5 2.5 0 0 0 .044-.33L15 22.5V22a2 2 0 0 0-1.85-1.995L13 20h-2a2 2 0 0 0-1.995 1.85L9 22v.5c0 .171.017.339.05.5H9a3 3 0 0 1-3-3v-1l-2.894-1.447A2 2 0 0 1 2 15.763V12C2 6.477 6.477 2 12 2zm-4 9a2 2 0 1 0 0 4 2 2 0 0 0 0-4zm8 0a2 2 0 1 0 0 4 2 2 0 0 0 0-4z' fill='%23e13238'/%3E%3C/svg%3E");
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
position: relative;
|
||||
margin-right: 4px;
|
||||
left: -5px;
|
||||
top: -1px;
|
||||
}
|
||||
|
||||
/* 提醒容器:背景色、左侧 */
|
||||
.custom-block.note {
|
||||
border-left: 5px solid var(--custom-block-note-left);
|
||||
background-color: var(--custom-block-note-bg);
|
||||
}
|
||||
|
||||
/* 提醒容器:svg图 */
|
||||
.custom-block.note [class*='custom-block-title']::before {
|
||||
content: '';
|
||||
background-image: url("data:image/svg+xml;utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M12 22C6.477 22 2 17.523 2 12S6.477 2 12 2s10 4.477 10 10-4.477 10-10 10zm-1-11v6h2v-6h-2zm0-4v2h2V7h-2z' fill='%234cb3d4'/%3E%3C/svg%3E");
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
position: relative;
|
||||
margin-right: 4px;
|
||||
left: -5px;
|
||||
top: -1px;
|
||||
}
|
||||
|
||||
/* 重要容器:背景色、左侧 */
|
||||
.custom-block.important {
|
||||
border-left: 5px solid var(--custom-block-important-left);
|
||||
background-color: var(--custom-block-important-bg);
|
||||
}
|
||||
|
||||
/* 重要容器:svg图 */
|
||||
.custom-block.important [class*='custom-block-title']::before {
|
||||
content: '';
|
||||
background-image: url("data:image/svg+xml;utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 1024 1024'%3E%3Cpath d='M512 981.333a84.992 84.992 0 0 1-84.907-84.906h169.814A84.992 84.992 0 0 1 512 981.333zm384-128H128v-42.666l85.333-85.334v-256A298.325 298.325 0 0 1 448 177.92V128a64 64 0 0 1 128 0v49.92a298.325 298.325 0 0 1 234.667 291.413v256L896 810.667v42.666zm-426.667-256v85.334h85.334v-85.334h-85.334zm0-256V512h85.334V341.333h-85.334z' fill='%23a371f7'/%3E%3C/svg%3E");
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
position: relative;
|
||||
margin-right: 4px;
|
||||
left: -5px;
|
||||
top: -1px;
|
||||
}
|
||||
|
||||
/* 注意容器:背景色、左侧 */
|
||||
.custom-block.caution {
|
||||
border-left: 5px solid var(--custom-block-caution-left);
|
||||
background-color: var(--custom-block-caution-bg);
|
||||
}
|
||||
|
||||
/* 注意容器:svg图 */
|
||||
.custom-block.caution [class*='custom-block-title']::before {
|
||||
content: '';
|
||||
background-image: url("data:image/svg+xml;utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M12 2c5.523 0 10 4.477 10 10v3.764a2 2 0 0 1-1.106 1.789L18 19v1a3 3 0 0 1-2.824 2.995L14.95 23a2.5 2.5 0 0 0 .044-.33L15 22.5V22a2 2 0 0 0-1.85-1.995L13 20h-2a2 2 0 0 0-1.995 1.85L9 22v.5c0 .171.017.339.05.5H9a3 3 0 0 1-3-3v-1l-2.894-1.447A2 2 0 0 1 2 15.763V12C2 6.477 6.477 2 12 2zm-4 9a2 2 0 1 0 0 4 2 2 0 0 0 0-4zm8 0a2 2 0 1 0 0 4 2 2 0 0 0 0-4z' fill='%23e13238'/%3E%3C/svg%3E");
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
position: relative;
|
||||
margin-right: 4px;
|
||||
left: -5px;
|
||||
top: -1px;
|
||||
}
|
||||
125
Website/docs/.vitepress/theme/style/doc-fade-in.css
Normal file
@@ -0,0 +1,125 @@
|
||||
#app a:focus-visible,
|
||||
#app button:focus-visible,
|
||||
#app input[type='checkbox']:focus-visible {
|
||||
--at-apply: outline-1 outline-primary ring-2 ring-primary;
|
||||
}
|
||||
|
||||
.VPSidebar::-webkit-scrollbar {
|
||||
background: transparent;
|
||||
height: 8px;
|
||||
width: 8px;
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
#app > div > div.VPLocalNav > div > div > div.outline {
|
||||
outline-style: none !important;
|
||||
}
|
||||
|
||||
.vp-doc .color-swatch {
|
||||
display: inline-block;
|
||||
width: 0.85em;
|
||||
height: 0.85em;
|
||||
min-width: 12px;
|
||||
min-height: 12px;
|
||||
font-size: inherit;
|
||||
border: 0;
|
||||
border-radius: 2px;
|
||||
margin: 0 3px 0 6px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.medium-zoom-overlay {
|
||||
z-index: 20;
|
||||
}
|
||||
|
||||
.medium-zoom-image {
|
||||
z-index: 21;
|
||||
}
|
||||
|
||||
@keyframes slide-enter {
|
||||
0% {
|
||||
transform: translateY(10px);
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
to {
|
||||
transform: translateY(0);
|
||||
opacity: 100;
|
||||
}
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: no-preference) {
|
||||
html:not(.no-sliding) [slide-enter],
|
||||
html:not(.no-sliding) .slide-enter,
|
||||
html:not(.no-sliding) .main > div > *,
|
||||
html:not(.no-sliding) #VPContent > div > div.VPFeatures.VPHomeFeatures > *,
|
||||
html:not(.no-sliding) .TeamPage > *,
|
||||
html:not(.no-sliding) .VPHomeHero > * {
|
||||
--enter-stage: 0;
|
||||
--enter-step: 90ms;
|
||||
--enter-initial: 0ms;
|
||||
animation: slide-enter 1s both 1;
|
||||
animation-delay: calc(var(--enter-initial) + var(--enter-stage) * var(--enter-step));
|
||||
}
|
||||
|
||||
.main > div > *:nth-child(1) {
|
||||
--enter-stage: 1;
|
||||
}
|
||||
.main > div > *:nth-child(2) {
|
||||
--enter-stage: 2;
|
||||
}
|
||||
.main > div > *:nth-child(3) {
|
||||
--enter-stage: 3;
|
||||
}
|
||||
.main > div > *:nth-child(4) {
|
||||
--enter-stage: 4;
|
||||
}
|
||||
.main > div > *:nth-child(5) {
|
||||
--enter-stage: 5;
|
||||
}
|
||||
.main > div > *:nth-child(6) {
|
||||
--enter-stage: 6;
|
||||
}
|
||||
.main > div > *:nth-child(7) {
|
||||
--enter-stage: 7;
|
||||
}
|
||||
.main > div > *:nth-child(8) {
|
||||
--enter-stage: 8;
|
||||
}
|
||||
.main > div > *:nth-child(9) {
|
||||
--enter-stage: 9;
|
||||
}
|
||||
.main > div > *:nth-child(10) {
|
||||
--enter-stage: 10;
|
||||
}
|
||||
.main > div > *:nth-child(11) {
|
||||
--enter-stage: 11;
|
||||
}
|
||||
.main > div > *:nth-child(12) {
|
||||
--enter-stage: 12;
|
||||
}
|
||||
.main > div > *:nth-child(13) {
|
||||
--enter-stage: 13;
|
||||
}
|
||||
.main > div > *:nth-child(14) {
|
||||
--enter-stage: 14;
|
||||
}
|
||||
.main > div > *:nth-child(15) {
|
||||
--enter-stage: 15;
|
||||
}
|
||||
.main > div > *:nth-child(16) {
|
||||
--enter-stage: 16;
|
||||
}
|
||||
.main > div > *:nth-child(17) {
|
||||
--enter-stage: 17;
|
||||
}
|
||||
.main > div > *:nth-child(18) {
|
||||
--enter-stage: 18;
|
||||
}
|
||||
.main > div > *:nth-child(19) {
|
||||
--enter-stage: 19;
|
||||
}
|
||||
.main > div > *:nth-child(20) {
|
||||
--enter-stage: 20;
|
||||
}
|
||||
}
|
||||
158
Website/docs/.vitepress/theme/style/style.css
Normal file
@@ -0,0 +1,158 @@
|
||||
@import './custom-block.css';
|
||||
@import './doc-fade-in.css';
|
||||
|
||||
:root:where(:lang(fa)) {
|
||||
--vp-font-family-base:
|
||||
'Vazirmatn', 'Inter', ui-sans-serif, system-ui, sans-serif, 'Apple Color Emoji',
|
||||
'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji';
|
||||
}
|
||||
|
||||
:root {
|
||||
--vp-home-hero-name-color: transparent;
|
||||
--vp-home-hero-name-background: -webkit-linear-gradient(120deg, #bd34fe 30%, #41d1ff);
|
||||
--vp-home-hero-image-background-image: linear-gradient(-45deg, #bd34fe 50%, #47caff 50%);
|
||||
--vp-home-hero-image-filter: blur(44px);
|
||||
|
||||
/* Enhanced brand color for better contrast */
|
||||
--vp-c-brand-1: #1e40af;
|
||||
--vp-c-brand-2: #2563eb;
|
||||
--vp-c-brand-3: #3b82f6;
|
||||
--vp-c-brand-soft: rgba(30, 64, 175, 0.14);
|
||||
|
||||
/* Button contrast improvements */
|
||||
--vp-button-brand-bg: #1e40af;
|
||||
--vp-button-brand-text: #ffffff;
|
||||
--vp-button-brand-hover-bg: #1d4ed8;
|
||||
--vp-button-brand-hover-text: #ffffff;
|
||||
--vp-button-brand-active-bg: #1e3a8a;
|
||||
--vp-button-brand-active-text: #ffffff;
|
||||
}
|
||||
|
||||
/* Dark mode color overrides for better contrast */
|
||||
.dark:root {
|
||||
--vp-c-brand-1: #60a5fa;
|
||||
--vp-c-brand-2: #3b82f6;
|
||||
--vp-c-brand-3: #2563eb;
|
||||
--vp-c-brand-soft: rgba(96, 165, 250, 0.16);
|
||||
|
||||
--vp-button-brand-bg: #3b82f6;
|
||||
--vp-button-brand-text: #000000;
|
||||
--vp-button-brand-hover-bg: #60a5fa;
|
||||
--vp-button-brand-hover-text: #000000;
|
||||
--vp-button-brand-active-bg: #2563eb;
|
||||
--vp-button-brand-active-text: #000000;
|
||||
}
|
||||
|
||||
@media (min-width: 640px) {
|
||||
:root {
|
||||
--vp-home-hero-image-filter: blur(56px);
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 960px) {
|
||||
:root {
|
||||
--vp-home-hero-image-filter: blur(68px);
|
||||
}
|
||||
}
|
||||
|
||||
.VPHero .VPImage {
|
||||
filter: drop-shadow(-2px 4px 6px rgba(0, 0, 0, 0.2));
|
||||
padding: 18px;
|
||||
}
|
||||
|
||||
/* used in reference/default-theme-search */
|
||||
img[src='/search.png'] {
|
||||
width: 100%;
|
||||
aspect-ratio: 1 / 1;
|
||||
}
|
||||
|
||||
/* Enhanced button contrast for accessibility */
|
||||
.VPButton.brand {
|
||||
background-color: var(--vp-button-brand-bg) !important;
|
||||
color: var(--vp-button-brand-text) !important;
|
||||
border: none;
|
||||
font-weight: 600;
|
||||
text-shadow: none;
|
||||
}
|
||||
|
||||
.VPButton.brand:hover {
|
||||
background-color: var(--vp-button-brand-hover-bg) !important;
|
||||
color: var(--vp-button-brand-hover-text) !important;
|
||||
transform: translateY(-1px);
|
||||
box-shadow: 0 4px 12px rgba(30, 64, 175, 0.4);
|
||||
}
|
||||
|
||||
.VPButton.brand:active {
|
||||
background-color: var(--vp-button-brand-active-bg) !important;
|
||||
color: var(--vp-button-brand-active-text) !important;
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
/* Dark mode support for buttons */
|
||||
.dark .VPButton.brand {
|
||||
background-color: #3b82f6 !important;
|
||||
color: #000000 !important;
|
||||
}
|
||||
|
||||
.dark .VPButton.brand:hover {
|
||||
background-color: #60a5fa !important;
|
||||
color: #000000 !important;
|
||||
box-shadow: 0 4px 12px rgba(59, 130, 246, 0.4);
|
||||
}
|
||||
|
||||
.dark .VPButton.brand:active {
|
||||
background-color: #2563eb !important;
|
||||
color: #000000 !important;
|
||||
}
|
||||
|
||||
/* Ensure proper contrast for all text elements */
|
||||
.VPButton.brand .text {
|
||||
color: inherit !important;
|
||||
}
|
||||
|
||||
/* Focus states for accessibility */
|
||||
.VPButton.brand:focus-visible {
|
||||
outline: 2px solid #ffffff;
|
||||
outline-offset: 2px;
|
||||
}
|
||||
|
||||
.dark .VPButton.brand:focus-visible {
|
||||
outline: 2px solid #000000;
|
||||
outline-offset: 2px;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'HarmonyOS Sans SC';
|
||||
src: url('/HarmonyOS_Sans_SC.ttf') format('truetype');
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: 'HarmonyOS Sans SC', sans-serif;
|
||||
line-height: 1.8;
|
||||
letter-spacing: 0.05em;
|
||||
word-spacing: 0.05em;
|
||||
}
|
||||
|
||||
p,
|
||||
li,
|
||||
a,
|
||||
span,
|
||||
div,
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4,
|
||||
h5,
|
||||
h6 {
|
||||
word-break: break-word;
|
||||
text-justify: inter-ideograph;
|
||||
-ms-text-autospace: ideograph-alpha;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
pre,
|
||||
code {
|
||||
letter-spacing: normal;
|
||||
word-spacing: normal;
|
||||
}
|
||||
101
Website/docs/config.ts
Normal file
@@ -0,0 +1,101 @@
|
||||
import { defineConfig, type DefaultTheme } from 'vitepress'
|
||||
import { groupIconMdPlugin, groupIconVitePlugin } from 'vitepress-plugin-group-icons'
|
||||
|
||||
export default defineConfig({
|
||||
lang: 'en-US',
|
||||
description:
|
||||
'Next-Generation Android Root Solution - Advanced kernel-based root management for Android devices with KernelSU integration',
|
||||
|
||||
themeConfig: {
|
||||
nav: nav(),
|
||||
|
||||
sidebar: {
|
||||
'/': { base: '/', items: sidebar() },
|
||||
},
|
||||
|
||||
search: { options: searchOptions() },
|
||||
editLink: {
|
||||
pattern: 'https://github.com/sukisu-ultra/sukisu-ultra/edit/main/docs/:path',
|
||||
text: 'Edit this page on GitHub',
|
||||
},
|
||||
|
||||
docFooter: {
|
||||
prev: 'Previous',
|
||||
next: 'Next',
|
||||
},
|
||||
|
||||
outline: {
|
||||
label: 'On this page',
|
||||
},
|
||||
|
||||
lastUpdated: {
|
||||
text: 'Last updated',
|
||||
},
|
||||
|
||||
notFound: {
|
||||
title: 'Page Not Found',
|
||||
quote: "Sorry, we couldn't find what you're looking for.",
|
||||
linkLabel: 'Go to home',
|
||||
linkText: 'Take me home',
|
||||
},
|
||||
|
||||
langMenuLabel: 'Languages',
|
||||
returnToTopLabel: 'Return to top',
|
||||
sidebarMenuLabel: 'Menu',
|
||||
darkModeSwitchLabel: 'Theme',
|
||||
lightModeSwitchTitle: 'Switch to light theme',
|
||||
darkModeSwitchTitle: 'Switch to dark theme',
|
||||
skipToContentLabel: 'Skip to content',
|
||||
},
|
||||
})
|
||||
|
||||
function nav(): DefaultTheme.NavItem[] {
|
||||
return [
|
||||
{ text: 'Home', link: '/' },
|
||||
{
|
||||
text: 'Getting Started',
|
||||
items: [
|
||||
{ text: 'Introduction', link: '/guide/' },
|
||||
{ text: 'Installation', link: '/guide/installation' },
|
||||
{ text: 'Compatibility', link: '/guide/compatibility' },
|
||||
{ text: 'Links', link: '/guide/links' },
|
||||
{ text: 'license', link: '/guide/license' },
|
||||
],
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
function sidebar(): DefaultTheme.SidebarItem[] {
|
||||
return [
|
||||
{
|
||||
text: 'Getting Started',
|
||||
items: [
|
||||
{ text: 'Introduction', link: '/guide/' },
|
||||
{ text: 'Installation', link: '/guide/installation' },
|
||||
{ text: 'Compatibility', link: '/guide/compatibility' },
|
||||
{ text: 'Links', link: '/guide/links' },
|
||||
{ text: 'license', link: '/guide/license' },
|
||||
],
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
function searchOptions(): Partial<DefaultTheme.LocalSearchOptions> {
|
||||
return {
|
||||
translations: {
|
||||
button: {
|
||||
buttonText: 'Search docs',
|
||||
buttonAriaLabel: 'Search docs',
|
||||
},
|
||||
modal: {
|
||||
noResultsText: 'No results found',
|
||||
resetButtonTitle: 'Clear query',
|
||||
footer: {
|
||||
selectText: 'Select',
|
||||
navigateText: 'Navigate',
|
||||
closeText: 'Close',
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
23
Website/docs/en/guide/compatibility.md
Normal file
@@ -0,0 +1,23 @@
|
||||
# Compatibility Status
|
||||
|
||||
::: info KernelSU
|
||||
KernelSU (versions prior to v0.9.5) officially supports Android GKI 2.0 devices (kernel 5.10+)
|
||||
:::
|
||||
|
||||
::: warning Legacy Kernel Support
|
||||
Older kernels (4.4+) are also compatible, but the kernel must be built manually
|
||||
:::
|
||||
|
||||
::: tip Extended Compatibility
|
||||
SukiSu-Ultra can support 3.x kernels (3.4-3.18) through additional back ports
|
||||
:::
|
||||
|
||||
## Architecture Support
|
||||
|
||||
Currently supports the following processor architectures:
|
||||
|
||||
| Architecture | Support Level | Notes |
|
||||
| --------------- | ------------------ | --------------------------- |
|
||||
| **arm64-v8a** | ✅ Full Support | Primary target architecture |
|
||||
| **armeabi-v7a** | ✅ Basic Support | Bare minimum functionality |
|
||||
| **X86_64** | 🟡 Partial Support | Some devices supported |
|
||||
85
Website/docs/en/guide/index.md
Normal file
@@ -0,0 +1,85 @@
|
||||
# Introduction
|
||||
|
||||
Welcome to SukiSU-Ultra, the next-generation Android root solution that provides advanced kernel-based root management for Android devices.
|
||||
|
||||
## What is SukiSU-Ultra?
|
||||
|
||||
SukiSU-Ultra is a modern, secure, and powerful root solution designed specifically for Android devices. It offers kernel-level root access management with enhanced security features and improved compatibility.
|
||||
|
||||
## Key Features
|
||||
|
||||
### 🔒 Kernel-based su and root access management
|
||||
|
||||
Secure root access management at the kernel level, providing better security and performance compared to traditional solutions.
|
||||
|
||||
### 🚫 Not based on OverlayFS module system
|
||||
|
||||
Built on Magic Mount technology from 5ec1cff, offering a more stable and reliable foundation.
|
||||
|
||||
### 📱 App Profile
|
||||
|
||||
Advanced application profiling system that allows you to lock root privileges in a controlled environment.
|
||||
|
||||
### 🔧 Enhanced Device Support
|
||||
|
||||
Bringing back support for non-GKI/GKI 1.0 devices, ensuring compatibility with older Android devices.
|
||||
|
||||
### ⚙️ Extensive Customization
|
||||
|
||||
Comprehensive customization options to tailor the root experience to your specific needs.
|
||||
|
||||
### 🔌 KPM Kernel Module Support
|
||||
|
||||
Full KernelPatch Module (KPM) functionality for advanced kernel modifications and enhancements.
|
||||
|
||||
## Why Choose SukiSU-Ultra?
|
||||
|
||||
- **Security First**: Advanced security features protect your device and data
|
||||
- **Modern Architecture**: Built with modern Android security models in mind
|
||||
- **Wide Compatibility**: Supports both GKI and non-GKI devices
|
||||
- **Active Development**: Continuously updated with latest Android versions
|
||||
- **Community Driven**: Open source with active community support
|
||||
|
||||
## Getting Started
|
||||
|
||||
Ready to get started with SukiSU-Ultra? Follow our step-by-step guide:
|
||||
|
||||
1. **[Installation](./installation)** - Learn how to install SukiSU-Ultra on your device
|
||||
2. **[Compatibility](./compatibility)** - Check device compatibility requirements
|
||||
3. **[Links](./links)** - Find additional resources and downloads
|
||||
|
||||
## System Requirements
|
||||
|
||||
Before installing SukiSU-Ultra, ensure your device meets these requirements:
|
||||
|
||||
- **Android Version**: Android 8.0 (API 26) or higher
|
||||
- **Bootloader**: Unlocked bootloader
|
||||
- **Recovery**: Custom recovery (TWRP recommended)
|
||||
- **Storage**: At least 100MB free space
|
||||
- **Knowledge**: Basic understanding of Android modding
|
||||
|
||||
## Safety Notice
|
||||
|
||||
::: danger Important
|
||||
⚠️ **Rooting your device can void your warranty and may cause permanent damage if done incorrectly.**
|
||||
|
||||
Always:
|
||||
|
||||
- Create a full backup before proceeding
|
||||
- Ensure your device is compatible
|
||||
- Follow instructions carefully
|
||||
- Have a recovery plan ready
|
||||
:::
|
||||
|
||||
## Support
|
||||
|
||||
Need help? We're here to assist:
|
||||
|
||||
- **📖 Documentation**: Comprehensive guides and tutorials
|
||||
- **💬 Community**: Active community forums and chat
|
||||
- **🐛 Bug Reports**: GitHub issue tracker
|
||||
- **📧 Direct Support**: Contact developers for critical issues
|
||||
|
||||
---
|
||||
|
||||
**Ready to unlock the full potential of your Android device?** Start with our [installation guide](./installation) and join thousands of users who trust SukiSU-Ultra for their root needs.
|
||||
121
Website/docs/en/guide/installation.md
Normal file
@@ -0,0 +1,121 @@
|
||||
# Installation Guide
|
||||
|
||||
This guide provides comprehensive instructions for installing SukiSU-Ultra on your Android device. Please follow the steps carefully.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
Before you begin, ensure you have the following:
|
||||
|
||||
- [ ] A compatible device. Check the [Compatibility Guide](./compatibility.md) for details.
|
||||
- [ ] Unlocked bootloader.
|
||||
- [ ] Custom recovery installed, such as TWRP.
|
||||
- [ ] Basic knowledge of flashing custom ROMs and kernels.
|
||||
- [ ] Your device's kernel source or a compatible pre-built kernel.
|
||||
|
||||
## Installation Methods
|
||||
|
||||
There are several ways to install SukiSU-Ultra, depending on your device and preference.
|
||||
|
||||
### Method 1: Using Pre-built GKI Packages
|
||||
|
||||
This is the recommended method for devices with Generic Kernel Image (GKI) 2.0, such as many Xiaomi, Redmi, and Samsung models.[^1]
|
||||
|
||||
[^1]: This method is not suitable for devices from manufacturers that heavily modify the kernel, like Meizu, OnePlus, Realme, and Oppo.
|
||||
|
||||
#### Steps:
|
||||
|
||||
1. **Download GKI Build**: Visit our [resources section](./links.md) to find the appropriate GKI build for your device's kernel version. Download the `.zip` file that includes `AnyKernel3` in its name.
|
||||
2. **Flash via Recovery**:
|
||||
- [ ] Boot your device into TWRP recovery.
|
||||
- [ ] Select "Install".
|
||||
- [ ] Navigate to the downloaded `AnyKernel3` zip file and select it.
|
||||
- [ ] Swipe to confirm the flash.
|
||||
- [ ] Once flashing is complete, reboot your system.
|
||||
3. **Verify Installation**:
|
||||
- [ ] Install the SukiSU-Ultra Manager app.
|
||||
- [ ] Open the app and check if root access is granted and working correctly.
|
||||
- [ ] You can also verify the new kernel version in your device's settings.
|
||||
|
||||
::: details File Format Guide
|
||||
The `.zip` archive without a suffix is uncompressed. The `.gz` suffix indicates compression used for specific models.
|
||||
:::
|
||||
|
||||
### Method 2: Custom Build for OnePlus Devices
|
||||
|
||||
For OnePlus devices, you'll need to create a custom build.
|
||||
|
||||
#### Steps:
|
||||
|
||||
1. **Gather Device Information**: You will need:
|
||||
- Your kernel version (e.g., `5.10`, `5.15`).
|
||||
- Your processor's codename.
|
||||
- The branch and configuration files from the OnePlus open-source kernel repository.
|
||||
2. **Create Custom Build**: Use the link in our [resources section](./links.md) to generate a custom build with your device's information.
|
||||
3. **Flash the Build**:
|
||||
- [ ] Download the generated `AnyKernel3` zip file.
|
||||
- [ ] Boot into recovery.
|
||||
- [ ] Flash the zip file.
|
||||
- [ ] Reboot and verify the installation.
|
||||
|
||||
### Method 3: Manual Kernel Integration (Advanced)
|
||||
|
||||
This method is for advanced users who are building a kernel from source.
|
||||
|
||||
#### Integration Scripts:
|
||||
|
||||
- **Main Branch (GKI)**:
|
||||
```sh [bash]
|
||||
curl -LSs "https://raw.githubusercontent.com/SukiSU-Ultra/SukiSU-Ultra/main/kernel/setup.sh" | bash -s main
|
||||
```
|
||||
- **Non-GKI Branch**:
|
||||
```sh [bash]
|
||||
curl -LSs "https://raw.githubusercontent.com/SukiSU-Ultra/SukiSU-Ultra/main/kernel/setup.sh" | bash -s nongki
|
||||
```
|
||||
- **SUSFS-Dev Branch (Recommended)**:
|
||||
```sh [bash]
|
||||
curl -LSs "https://raw.githubusercontent.com/SukiSU-Ultra/SukiSU-Ultra/main/kernel/setup.sh" | bash -s susfs-main
|
||||
```
|
||||
|
||||
::: warning Required Kernel Configs
|
||||
For KPM support, you must enable `CONFIG_KPM=y`.
|
||||
For non-GKI devices, you also need to enable `CONFIG_KALLSYMS=y` and `CONFIG_KALLSYMS_ALL=y`.
|
||||
:::
|
||||
|
||||
## Post-Installation
|
||||
|
||||
### Maintaining Root After OTA Updates
|
||||
|
||||
To keep root access after an Over-the-Air (OTA) update, follow these steps ==before rebooting==.
|
||||
|
||||
1. **Flash to Inactive Slot**:
|
||||
- [ ] After the OTA update is downloaded and installed, **do not reboot**.
|
||||
- [ ] Open the SukiSU-Ultra Manager.
|
||||
- [ ] Go to the flashing/patching interface.
|
||||
- [ ] Select your `AnyKernel3` kernel zip file.
|
||||
- [ ] Choose to install it to the inactive slot.
|
||||
- [ ] Once flashed, you can safely reboot.
|
||||
2. **Alternative: LKM Mode**: You can also use LKM mode to install to the unused slot after an OTA.
|
||||
|
||||
::: tip
|
||||
For non-GKI devices, the safest method to retain root after an OTA is to use TWRP to flash the kernel again.
|
||||
:::
|
||||
|
||||
## Verification Checklist
|
||||
|
||||
After installation, please verify the following:
|
||||
|
||||
- [ ] **Manager App**: The SukiSU-Ultra Manager app opens and shows a successful root status.
|
||||
- [ ] **Root Access**: Root checker apps confirm that root access is working.
|
||||
- [ ] **Kernel Version**: The kernel version in `Settings > About Phone` reflects the SukiSU-Ultra kernel.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
If you encounter any issues:
|
||||
|
||||
1. Double-check the [Compatibility Guide](./compatibility.md).
|
||||
2. Visit our [GitHub repository](https://github.com/sukisu-ultra/sukisu-ultra) for issues and solutions.
|
||||
3. Join our [Telegram community](https://t.me/sukiksu) for live support.
|
||||
|
||||
::: danger Safety Reminder
|
||||
⚠️ **Always have a backup!** Keep a copy of your original `boot.img` and be prepared to restore your device if something goes wrong.
|
||||
:::
|
||||
77
Website/docs/en/guide/license.md
Normal file
@@ -0,0 +1,77 @@
|
||||
# License
|
||||
|
||||
## 📄 Software Licensing
|
||||
|
||||
### Kernel Components
|
||||
|
||||
::: info GPL-2.0 License
|
||||
The files in the "kernel" directory are under GPL-2.0-only license
|
||||
:::
|
||||
|
||||
**License:** [GPL-2.0-only](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html)
|
||||
|
||||
### Application Core
|
||||
|
||||
::: tip GPL-3.0 License
|
||||
All other parts (except mentioned below) are under GPL-3.0 or later license
|
||||
:::
|
||||
|
||||
**License:** [GPL-3.0 or later](https://www.gnu.org/licenses/gpl-3.0.html)
|
||||
|
||||
## Artwork & Brand Assets
|
||||
|
||||
### Launcher Icons & Character Art
|
||||
|
||||
::: warning Copyright Notice
|
||||
Special licensing requirements for anime character artwork
|
||||
:::
|
||||
|
||||
The images of the files `ic_launcher(?!.*alt.*).*` with anime character emoticons have specific copyright terms:
|
||||
|
||||
**Copyright Holders:**
|
||||
|
||||
- **Anime Character Art:** [五十根大虾仁](https://space.bilibili.com/370927)
|
||||
- **Brand Intellectual Property:** [明风OuO](https://space.bilibili.com/274939213)
|
||||
- **Vectorization:** @MiRinChan
|
||||
|
||||
**License Requirements:**
|
||||
|
||||
1. **Creative Commons License:** [CC BY-NC-SA 4.0](https://creativecommons.org/licenses/by-nc-sa/4.0/legalcode.txt)
|
||||
2. **Author Authorization:** Required from both copyright holders
|
||||
3. **Attribution:** Must credit all contributors listed above
|
||||
|
||||
::: details Usage Requirements
|
||||
Before using these artistic assets, you must:
|
||||
|
||||
- Comply with Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International license
|
||||
- Obtain authorization from both original authors for use of artistic content
|
||||
- Provide proper attribution to all contributors
|
||||
:::
|
||||
|
||||
## 📋 License Summary
|
||||
|
||||
| Component | License | Notes |
|
||||
| -------------------- | ------------------------------------------------------------------------- | ------------------------------------- |
|
||||
| **Kernel Files** | [GPL-2.0-only](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html) | Files in `/kernel/` directory |
|
||||
| **Application Code** | [GPL-3.0+](https://www.gnu.org/licenses/gpl-3.0.html) | Main application components |
|
||||
| **Character Art** | [CC BY-NC-SA 4.0](https://creativecommons.org/licenses/by-nc-sa/4.0/) | + Author authorization required |
|
||||
| **Brand Assets** | Mixed Licensing | See specific attribution requirements |
|
||||
|
||||
## 🔗 License Links
|
||||
|
||||
- **GPL-2.0:** [Full License Text](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html)
|
||||
- **GPL-3.0:** [Full License Text](https://www.gnu.org/licenses/gpl-3.0.html)
|
||||
- **CC BY-NC-SA 4.0:** [Full License Text](https://creativecommons.org/licenses/by-nc-sa/4.0/legalcode.txt)
|
||||
|
||||
## 📞 Licensing Questions
|
||||
|
||||
For questions about licensing or usage permissions:
|
||||
|
||||
1. **Code Licensing:** Refer to respective GPL license terms
|
||||
2. **Artwork Usage:** Contact original authors for authorization
|
||||
3. **Commercial Use:** Review CC BY-NC-SA 4.0 restrictions
|
||||
4. **Distribution:** Ensure compliance with all applicable licenses
|
||||
|
||||
::: tip Compliance Note
|
||||
When redistributing or modifying SukiSU-Ultra, ensure you comply with all applicable licenses and attribution requirements for each component.
|
||||
:::
|
||||
77
Website/docs/en/guide/links.md
Normal file
@@ -0,0 +1,77 @@
|
||||
# More Links
|
||||
|
||||
## 🌐 Translation & Localization
|
||||
|
||||
::: info Contribute Translations
|
||||
If you need to submit a translation for the manager, please visit our Crowdin project
|
||||
:::
|
||||
|
||||
**Translation Platform:** [Crowdin - SukiSU-Ultra](https://crowdin.com/project/SukiSU-Ultra)
|
||||
|
||||
## 🔧 Projects & Builds
|
||||
|
||||
Projects compiled based on Sukisu and susfs:
|
||||
|
||||
### GKI Builds
|
||||
|
||||
::: tip Universal GKI Support
|
||||
Generic Kernel Image builds with KernelSU and SUSFS integration
|
||||
:::
|
||||
|
||||
**Repository:** [GKI_KernelSU_SUSFS](https://github.com/ShirkNeko/GKI_KernelSU_SUSFS)
|
||||
|
||||
### OnePlus Builds
|
||||
|
||||
::: tip Device-Specific Builds
|
||||
Automated OnePlus kernel builds with MKSU and SUSFS
|
||||
:::
|
||||
|
||||
**Repository:** [Action_OnePlus_MKSU_SUSFS](https://github.com/ShirkNeko/Action_OnePlus_MKSU_SUSFS)
|
||||
|
||||
## 📱 Community & Support
|
||||
|
||||
### Telegram Community
|
||||
|
||||
::: info Join Our Community
|
||||
Connect with other users, get support, and stay updated
|
||||
:::
|
||||
|
||||
**Main Group:** [Tg Group](https://t.me/sukiksu)
|
||||
|
||||
### Test Builds
|
||||
|
||||
::: warning Experimental Builds
|
||||
Test builds are experimental and may be unstable
|
||||
:::
|
||||
|
||||
**Test Builds Channel:** [Latest Test Build](https://t.me/Sukiksu/7114)
|
||||
|
||||
## Downloads & Releases
|
||||
|
||||
### Official Releases
|
||||
|
||||
::: tip Stable Releases
|
||||
Download the latest stable versions from our GitHub releases
|
||||
:::
|
||||
|
||||
**GitHub Releases:** [SukiSU-Ultra Releases](https://github.com/sukisu-ultra/sukisu-ultra/releases)
|
||||
|
||||
### Issue Reporting
|
||||
|
||||
::: info Bug Reports & Feature Requests
|
||||
Report bugs or request new features on our GitHub repository
|
||||
:::
|
||||
|
||||
**GitHub Issues:** [Report Issues](https://github.com/sukisu-ultra/sukisu-ultra/issues)
|
||||
|
||||
## 🔗 Quick Links Summary
|
||||
|
||||
| Resource | Link | Description |
|
||||
| ------------------ | ---------------------------------------------------------------------------- | ---------------------- |
|
||||
| **Translations** | [Crowdin](https://crowdin.com/project/SukiSU-Ultra) | Submit translations |
|
||||
| **Telegram Group** | [t.me/sukiksu](https://t.me/sukiksu) | Community support |
|
||||
| **Test Builds** | [Test Channel](https://t.me/Sukiksu/7114) | Experimental builds |
|
||||
| **Releases** | [GitHub Releases](https://github.com/sukisu-ultra/sukisu-ultra/releases) | Stable downloads |
|
||||
| **Issues** | [GitHub Issues](https://github.com/sukisu-ultra/sukisu-ultra/issues) | Bug reports |
|
||||
| **GKI Builds** | [GKI Repository](https://github.com/ShirkNeko/GKI_KernelSU_SUSFS) | Universal builds |
|
||||
| **OnePlus Builds** | [OnePlus Repository](https://github.com/ShirkNeko/Action_OnePlus_MKSU_SUSFS) | Device-specific builds |
|
||||
37
Website/docs/en/index.md
Normal file
@@ -0,0 +1,37 @@
|
||||
---
|
||||
layout: home
|
||||
|
||||
hero:
|
||||
name: 'SukiSU-Ultra'
|
||||
text: 'Next-Generation Android Root Solution'
|
||||
tagline: Advanced kernel-based root management for Android devices
|
||||
image:
|
||||
src: /logo.svg
|
||||
alt: SukiSU-Ultra
|
||||
actions:
|
||||
- theme: brand
|
||||
text: Get Started
|
||||
link: /guide/
|
||||
- theme: alt
|
||||
text: View on GitHub
|
||||
link: https://github.com/sukisu-ultra/sukisu-ultra
|
||||
|
||||
features:
|
||||
- title: Kernel-based su and root access management
|
||||
details: Secure root access management at the kernel level.
|
||||
|
||||
- title: Not based on OverlayFS module system
|
||||
details: Based on Magic Mount from 5ec1cff.
|
||||
|
||||
- title: App Profile
|
||||
details: Lock root privileges in a cage.
|
||||
|
||||
- title: Bringing back non-GKI/GKI 1.0 support
|
||||
details: Enhanced compatibility for older devices.
|
||||
|
||||
- title: More customization
|
||||
details: Extensive customization options available.
|
||||
|
||||
- title: Support for KPM kernel modules
|
||||
details: Full KernelPatch Module functionality.
|
||||
---
|
||||
BIN
Website/docs/public/HarmonyOS_Sans_SC.ttf
Normal file
188
Website/docs/public/SukiSU.svg
Normal file
|
After Width: | Height: | Size: 200 KiB |
60
Website/docs/public/_headers
Normal file
@@ -0,0 +1,60 @@
|
||||
# Global headers for security and performance
|
||||
/*
|
||||
X-Frame-Options: DENY
|
||||
X-Content-Type-Options: nosniff
|
||||
Referrer-Policy: strict-origin-when-cross-origin
|
||||
Strict-Transport-Security: "max-age=31536000; includeSubDomains; preload"
|
||||
Permissions-Policy: "camera=(), microphone=(), geolocation=()"
|
||||
Content-Security-Policy: "default-src 'self'; script-src 'self' https://static.cloudflareinsights.com; img-src 'self' data: https:; style-src 'self' 'unsafe-inline'; font-src 'self' data:; connect-src 'self'; object-src 'none'; base-uri 'self'; frame-ancestors 'none'"
|
||||
|
||||
# Cache static assets for maximum performance
|
||||
/assets/*
|
||||
Cache-Control: public, max-age=31536000, immutable
|
||||
|
||||
/*.js
|
||||
Cache-Control: public, max-age=31536000, immutable
|
||||
|
||||
/*.css
|
||||
Cache-Control: public, max-age=31536000, immutable
|
||||
|
||||
/*.woff2
|
||||
Cache-Control: public, max-age=31536000, immutable
|
||||
|
||||
/*.woff
|
||||
Cache-Control: public, max-age=31536000, immutable
|
||||
|
||||
# Images - 30 days cache
|
||||
/*.svg
|
||||
Cache-Control: public, max-age=2592000
|
||||
|
||||
/*.png
|
||||
Cache-Control: public, max-age=2592000
|
||||
|
||||
/*.jpg
|
||||
Cache-Control: public, max-age=2592000
|
||||
|
||||
/*.jpeg
|
||||
Cache-Control: public, max-age=2592000
|
||||
|
||||
/*.webp
|
||||
Cache-Control: public, max-age=2592000
|
||||
|
||||
/*.avif
|
||||
Cache-Control: public, max-age=2592000
|
||||
|
||||
/*.ico
|
||||
Cache-Control: public, max-age=2592000
|
||||
|
||||
# Manifest and service worker
|
||||
/site.webmanifest
|
||||
Cache-Control: public, max-age=86400
|
||||
|
||||
/sw.js
|
||||
Cache-Control: public, max-age=0, must-revalidate
|
||||
|
||||
# Offline page and HTML caching
|
||||
/*.html
|
||||
Cache-Control: public, max-age=60, must-revalidate
|
||||
|
||||
/offline.html
|
||||
Cache-Control: public, max-age=3600
|
||||
3
Website/docs/public/favicon.svg
Normal file
|
After Width: | Height: | Size: 58 KiB |
535
Website/docs/public/logo.svg
Normal file
@@ -0,0 +1,535 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
width="512"
|
||||
height="512"
|
||||
viewBox="0 0 512 512"
|
||||
version="1.1"
|
||||
id="svg1"
|
||||
xml:space="preserve"
|
||||
inkscape:version="1.4 (e7c3feb100, 2024-10-09)"
|
||||
sodipodi:docname="zako.optizmism.svg"
|
||||
inkscape:export-filename="zako.plain.svg"
|
||||
inkscape:export-xdpi="96"
|
||||
inkscape:export-ydpi="96"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"><sodipodi:namedview
|
||||
id="namedview1"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#999999"
|
||||
borderopacity="1"
|
||||
inkscape:showpageshadow="2"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#d1d1d1"
|
||||
inkscape:document-units="px"
|
||||
showguides="false"
|
||||
inkscape:zoom="2"
|
||||
inkscape:cx="219"
|
||||
inkscape:cy="370"
|
||||
inkscape:window-width="1280"
|
||||
inkscape:window-height="702"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="false"><inkscape:page
|
||||
x="0"
|
||||
y="0"
|
||||
width="512"
|
||||
height="512"
|
||||
id="page2"
|
||||
margin="0"
|
||||
bleed="0" /></sodipodi:namedview><defs
|
||||
id="defs1"><inkscape:path-effect
|
||||
effect="simplify"
|
||||
id="path-effect79"
|
||||
is_visible="true"
|
||||
lpeversion="1.3"
|
||||
threshold="32.467532"
|
||||
steps="1"
|
||||
smooth_angles="360"
|
||||
helper_size="0"
|
||||
simplify_individual_paths="false"
|
||||
simplify_just_coalesce="false" /><inkscape:path-effect
|
||||
effect="simplify"
|
||||
id="path-effect78"
|
||||
is_visible="true"
|
||||
lpeversion="1.3"
|
||||
threshold="32.467532"
|
||||
steps="1"
|
||||
smooth_angles="360"
|
||||
helper_size="0"
|
||||
simplify_individual_paths="false"
|
||||
simplify_just_coalesce="false" /><inkscape:path-effect
|
||||
effect="simplify"
|
||||
id="path-effect77"
|
||||
is_visible="true"
|
||||
lpeversion="1.3"
|
||||
threshold="32.467532"
|
||||
steps="1"
|
||||
smooth_angles="360"
|
||||
helper_size="0"
|
||||
simplify_individual_paths="false"
|
||||
simplify_just_coalesce="false" /><inkscape:path-effect
|
||||
effect="simplify"
|
||||
id="path-effect76"
|
||||
is_visible="true"
|
||||
lpeversion="1.3"
|
||||
threshold="32.467532"
|
||||
steps="1"
|
||||
smooth_angles="360"
|
||||
helper_size="0"
|
||||
simplify_individual_paths="false"
|
||||
simplify_just_coalesce="false" /><inkscape:path-effect
|
||||
effect="simplify"
|
||||
id="path-effect75"
|
||||
is_visible="true"
|
||||
lpeversion="1.3"
|
||||
threshold="32.467532"
|
||||
steps="1"
|
||||
smooth_angles="360"
|
||||
helper_size="0"
|
||||
simplify_individual_paths="false"
|
||||
simplify_just_coalesce="false" /><inkscape:path-effect
|
||||
effect="simplify"
|
||||
id="path-effect61"
|
||||
is_visible="true"
|
||||
lpeversion="1.3"
|
||||
threshold="32.467532"
|
||||
steps="1"
|
||||
smooth_angles="360"
|
||||
helper_size="0"
|
||||
simplify_individual_paths="false"
|
||||
simplify_just_coalesce="false" /><inkscape:path-effect
|
||||
effect="simplify"
|
||||
id="path-effect42"
|
||||
is_visible="true"
|
||||
lpeversion="1.3"
|
||||
threshold="32.467532"
|
||||
steps="1"
|
||||
smooth_angles="360"
|
||||
helper_size="0"
|
||||
simplify_individual_paths="false"
|
||||
simplify_just_coalesce="false" /></defs><g
|
||||
inkscape:label="main"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"><path
|
||||
style="display:inline;opacity:1;fill:#fff9f6;fill-opacity:1;stroke:#4c4f59;stroke-width:6.5;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
||||
d="m 164.23888,424.64081 -31.07097,26.05282 c 0,0 -5.19317,18.17613 25.96589,28.93342 31.15907,10.7573 213.29123,1.48377 213.29123,1.48377 0,0 52.30272,11.87012 80.86519,-37.09413 -0.37094,0.74188 -8.90259,19.28895 -8.90259,19.28895 l 1.48377,19.28894 c 0,0 57.73516,-9.41425 30.45647,-148.95524 4.19672,-9.44262 8.39344,-15.21311 8.39344,-15.21311 l -3.14754,-21.77049 c 0,0 5.65758,-40.33533 7.88323,-45.52851 2.22564,-5.19317 9.64447,-11.12823 9.64447,-11.12823 0,0 -2.78206,-20.40177 -6.67694,-26.15136 -0.74189,-3.33847 6.86241,-35.05395 2.04017,-58.05231 -7.04788,-11.87012 -4.82223,-11.87012 -18.918,-25.59495 0,0.37094 8.90259,-44.88389 -0.74188,-73.446367 -2.96753,0 -32.27189,-10.757296 -96.81567,23.740241 -0.37094,1.112824 -26.33683,-30.046242 -153.56969,-15.950474 -0.74188,0 -43.40012,-37.836009 -66.76942,-40.432598 -1.11283,-0.370941 -22.62742,-4.080354 -25.22401,48.964247 0,1.112824 -44.14201,15.950474 -56.01213,64.914721 -11.87012,48.96425 -11.128238,80.86519 -11.128238,80.86519 0,0 -18.904912,27.6632 -9.798121,25.03308 5.346826,-1.54421 4.975885,23.56023 4.975885,23.56023 l -2.225648,39.31977 c 0,0 -14.466709,35.23942 -14.83765,59.72155 -0.370942,24.48212 -0.741883,34.12659 4.822236,48.5933 5.564119,14.46671 20.772711,46.7386 42.658245,63.06002 8.902591,0.37094 18.918001,1.8547 18.918001,1.8547 l 3.70942,-20.77271 c 0,0 18.918,15.95048 32.27189,14.83765 13.35388,-1.11282 -0.74189,-0.74188 -0.74189,-0.74188 l -13.72482,-17.80518 z"
|
||||
id="path2"
|
||||
sodipodi:nodetypes="ccscccccccscccccccccscsccsscccsccc"
|
||||
inkscape:label="整个人的边框" /><path
|
||||
id="path91"
|
||||
style="opacity:1;fill:#93d4fa;fill-opacity:1;stroke:#4c4f59;stroke-width:6.9;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
||||
d="m 133.91016,110.16992 -69.738285,47.48047 18.263976,30.75433 -31.617492,55.67536 28.5625,17.0625 14.837891,-1.48438 2.226562,-55.64062 2.577791,-4.39109 18.193697,-21.94485 23.74023,-49.70703 z"
|
||||
sodipodi:nodetypes="ccccccccccc" /><path
|
||||
style="display:inline;opacity:1;fill:#ace0fe;fill-opacity:1;stroke:#4c4f59;stroke-width:6.54357;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
||||
d="m 217.01397,405.82758 c 0,0 7.41728,-25.94462 41.16595,-27.07265 33.74866,-1.12802 53.77534,4.1361 53.77534,4.1361 0,0 11.12594,6.39215 17.80149,14.66435 6.67557,8.2722 28.92743,40.60895 41.53682,48.88115 12.60939,8.27219 15.20545,15.41636 15.20545,15.41636 l -1.85433,11.28027 c 0,0 -14.83458,10.15224 -30.78175,11.65627 -15.94717,1.50404 -147.60404,0 -147.60404,0 l -30.04002,-3.00807"
|
||||
id="path71"
|
||||
inkscape:label="衣服底色" /><path
|
||||
style="fill:#ffffff;fill-opacity:1;stroke-width:1.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.3;paint-order:fill markers stroke"
|
||||
d="m 363.54127,444.68178 c 0,0 -6.89429,4.06586 -4.86135,6.8059 2.03293,2.74004 5.92201,3.44715 5.92201,3.44715 0,0 4.5962,0.26516 5.12653,-3.53554 0.53033,-3.8007 -3.00521,-6.8059 -3.00521,-6.8059"
|
||||
id="path60" /><path
|
||||
style="fill:#ffffff;fill-opacity:1;stroke-width:1.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.3;paint-order:fill markers stroke"
|
||||
d="m 349.67678,459.82583 c 0,0 4.5,-7.25 7,-5.5 2.5,1.75 -4.25,7.25 -4.25,7.25"
|
||||
id="path59" /><path
|
||||
id="path73-8"
|
||||
style="fill:#ffffff;stroke-width:8.7;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.3;paint-order:fill markers stroke"
|
||||
d="m 228.96985,465.10941 a 8.0341015,5.6995392 0 0 1 -8.0341,5.69953 8.0341015,5.6995392 0 0 1 -8.03411,-5.69953 8.0341015,5.6995392 0 0 1 8.03411,-5.69954 8.0341015,5.6995392 0 0 1 8.0341,5.69954 z" /><path
|
||||
style="opacity:1;fill:none;fill-opacity:1;stroke:#6c9cb2;stroke-width:3;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
||||
d="m 220.85246,408.91803 c -0.22678,0.7374 0.77024,1.47887 0.20184,0.3709 -0.55918,-1.08999 0.45864,1.0858 0.58075,1.39596 2.10089,5.33666 1.96407,11.16153 3.60742,16.6426 0.30205,1.00744 0.58829,2.039 0.5936,3.09874"
|
||||
id="path75"
|
||||
inkscape:path-effect="#path-effect75"
|
||||
inkscape:original-d="m 220.85246,408.91803 c 0.0874,0.34973 -0.0874,1.13662 0.26229,1.04918 0.34973,-0.0874 -0.42351,-1.37161 -0.26229,-1.04918 2.70315,5.4063 2.32215,10.60009 3.67213,16 0.44415,1.77662 1.31148,3.74741 1.31148,5.5082" /><path
|
||||
style="opacity:1;fill:none;fill-opacity:1;stroke:#6c9cb2;stroke-width:6.9;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
||||
d="m 225.57377,436.98361 c -1.22213,4.37272 0.0774,9.22771 2.41807,12.85195 2.34183,3.626 0.85866,8.30929 2.56554,12.06608 1.71568,3.77616 2.21068,8.39349 -0.30117,12.16042 -1.9802,2.96963 -5.23922,5.13567 -6.38674,8.61546 0,0 -0.0736,0.66095 -0.0736,0.66095"
|
||||
id="path76"
|
||||
inkscape:path-effect="#path-effect76"
|
||||
inkscape:original-d="m 225.57377,436.98361 c -1.27879,3.14891 0.64852,9.42818 3.40984,14.95082 0.92316,1.84632 0.76302,8.34571 1.57377,9.96721 1.05661,2.11323 2.18032,6.57378 1.31147,9.18033 -1.55473,4.66419 -8.07296,8.77008 -8.07296,12.2565"
|
||||
sodipodi:nodetypes="csssc" /><path
|
||||
style="opacity:1;fill:#fbf3ef;fill-opacity:1;stroke:none;stroke-width:5.3;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
||||
d="m 259.14754,381.90164 c 0,0 8.91803,15.47541 9.18033,20.98361 0.26229,5.50819 32.26229,-1.83607 32.26229,-1.83607 0,0 -4.45901,-16.78688 -7.34426,-19.40983 -2.88524,-2.62296 -34.09836,0.26229 -34.09836,0.26229 z"
|
||||
id="path89" /><path
|
||||
style="opacity:1;fill:none;fill-opacity:1;stroke:#6c9cb2;stroke-width:4.6;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
||||
d="m 247.86885,386.36066 c 0.62439,0.76152 1.31219,1.32242 0.35251,0.10604 -0.98535,-1.24891 -0.63523,-0.56343 0.0935,0.24171 1.40155,1.54849 2.30926,3.40606 3.10338,5.34166 1.48771,3.62621 2.15649,7.53104 4.02075,11.02472 1.24886,2.3404 2.74915,4.53757 3.97084,6.89242"
|
||||
id="path77"
|
||||
inkscape:path-effect="#path-effect77"
|
||||
inkscape:original-d="m 247.86885,386.36066 c 3.34102,3.56326 -3.68025,-4.79705 1.83607,2.09836 1.23825,1.54782 3.18087,7.73986 3.67213,9.70492 1.00623,4.02493 4.53306,8.80383 6.03279,11.80327" /><path
|
||||
style="opacity:1;fill:none;fill-opacity:1;stroke:#6c9cb2;stroke-width:4.6;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
||||
d="m 306.62295,386.88525 c 1.62958,6.07854 4.04229,11.93568 6.81967,17.57377"
|
||||
id="path78"
|
||||
inkscape:path-effect="#path-effect78"
|
||||
inkscape:original-d="m 306.62295,386.88525 c 1.57569,6.03983 4.44925,12.83291 6.81967,17.57377" /><path
|
||||
style="opacity:1;fill:none;fill-opacity:1;stroke:#6c9cb2;stroke-width:5.3;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
||||
d="m 335.73771,431.21312 c 0.7696,3.76393 3.24862,7.26774 5.48726,10.89168 2.09754,3.39553 3.33574,7.13199 4.87926,10.7955 2.06975,4.91248 6.75146,8.7424 7.699,13.95386 0.92029,5.06165 5.24206,8.89669 5.27874,14.19502"
|
||||
id="path79"
|
||||
inkscape:path-effect="#path-effect79"
|
||||
inkscape:original-d="m 335.73771,431.21312 c -1.15649,1.03916 3.66917,4.97179 4.45901,8.13114 0.96754,3.87016 3.8848,6.88344 4.72131,10.22951 1.09547,4.38186 5.88097,12.49359 8.39345,16.2623 0.80744,1.21116 0.89968,3.11084 1.57377,4.45901 1.7539,3.50782 4.19672,6.9619 4.19672,10.7541" /><path
|
||||
style="opacity:1;fill:none;fill-opacity:1;stroke:#494d55;stroke-width:5.3;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
||||
d="m 258.62295,384.2623 c 0,0 8.13115,6.81967 11.27869,27.01639"
|
||||
id="path80" /><path
|
||||
style="opacity:1;fill:#fbf3ef;fill-opacity:1;stroke:#4c4f59;stroke-width:6.9;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
||||
d="m 127.23285,329.0249 c 0,0 -2.59659,60.46343 13.72483,75.30108 8.16071,-8.16071 10.01541,-10.38636 10.01541,-10.38636 l 12.24107,8.53165 5.19317,-4.45129 2.22565,-19.65989 5.19318,-10.01542 5.93506,-12.24106 -8.53165,-8.53165 -15.95047,-19.28894 -3.33848,-4.82224 -4.45129,9.27353 c 0,0 -12.24106,0 -15.95048,-2.59659 -3.70941,-2.59658 -6.306,-1.11282 -6.306,-1.11282 z"
|
||||
id="path90" /><path
|
||||
style="opacity:1;fill:none;fill-opacity:1;stroke:#494d55;stroke-width:5.3;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
||||
d="m 291.67213,381.37705 c 0,0 7.08197,6.55738 10.22951,26.4918"
|
||||
id="path81" /><path
|
||||
style="opacity:1;fill:none;fill-opacity:1;stroke:#494d55;stroke-width:5.3;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
||||
d="m 270.16393,404.19672 c 0,0 16.00001,-3.93442 27.80328,-2.62295"
|
||||
id="path82" /><path
|
||||
style="opacity:1;fill:none;fill-opacity:1;stroke:#494d55;stroke-width:5.3;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
||||
d="m 253.90164,417.83607 c 0,0 37.2459,-13.11476 69.77049,-9.18033 2.09836,3.67213 5.77049,16.78688 -5.77049,20.98361 -4.72131,0 -19.14754,-1.57378 -22.55738,-0.5246 -3.40983,1.04919 -12.59016,0.5246 -18.36065,4.45902 -5.77049,3.93443 -14.68853,8.91803 -18.36066,3.14754 -3.67213,-5.77049 -4.72131,-12.32787 -4.72131,-12.32787 z"
|
||||
id="path83" /><path
|
||||
style="opacity:1;fill:none;fill-opacity:1;stroke:#494d55;stroke-width:5.3;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
||||
d="m 276.45902,482.62295 c 0,0 -9.70492,-1.57377 1.04918,-46.42623"
|
||||
id="path84" /><path
|
||||
style="opacity:1;fill:#fcf6fa;fill-opacity:1;stroke:#494d55;stroke-width:5.3;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
||||
d="m 287.21312,432.78689 6.29508,50.88524 13.90164,-0.78688 -6.81968,-54.03279 z"
|
||||
id="path88" /><path
|
||||
style="opacity:1;fill:none;fill-opacity:1;stroke:#494d55;stroke-width:5.3;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
||||
d="m 287.73771,430.95082 5.77049,50.88525"
|
||||
id="path85" /><path
|
||||
style="opacity:1;fill:none;fill-opacity:1;stroke:#494d55;stroke-width:5.3;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
||||
d="m 300.32787,428.06557 c 0,0 2.09836,37.7705 7.34426,52.98361"
|
||||
id="path86" /><path
|
||||
style="opacity:1;fill:none;fill-opacity:1;stroke:#494d55;stroke-width:5.3;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
||||
d="m 313.18033,431.21312 c 0,0 7.60656,27.54098 10.22951,35.93442 2.62295,8.39344 -8.13115,15.73771 -8.13115,15.73771"
|
||||
id="path87" /><path
|
||||
style="opacity:1;fill:#bce4fd;fill-opacity:1;stroke:none;stroke-width:8.7;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
||||
d="m 164.23888,424.64081 -31.07097,26.05282 5.00771,13.72483 20.95818,15.20859 17.08508,2.15431 -11.1287,-20.55238 -1.54637,-22.78395 z"
|
||||
id="path74" /><path
|
||||
id="path73"
|
||||
style="opacity:1;fill:#ffffff;stroke-width:8.7;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.3;paint-order:fill markers stroke"
|
||||
d="m 163.81755,451.10144 a 8.0341015,5.6995392 0 0 1 -8.0341,5.69954 8.0341015,5.6995392 0 0 1 -8.0341,-5.69954 8.0341015,5.6995392 0 0 1 8.0341,-5.69954 8.0341015,5.6995392 0 0 1 8.0341,5.69954 z" /><path
|
||||
style="opacity:1;fill:#fff9f6;fill-opacity:1;stroke:none;stroke-width:8.7;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
||||
d="m 338.36066,376.13115 c 0,0 1.83605,30.95081 -6.29509,51.40983 2.88525,1.04918 14.48107,0.26606 26.91267,-7.34238 12.4316,-7.60844 11.38242,-22.03467 11.38242,-22.03467 l 2.09836,28.85246 c 0,0 24.31867,-28.09873 24.2249,-52.60674 -0.0938,-24.50801 -1.66753,-26.08178 -1.66753,-26.08178 l -11.83407,-1.12685 z"
|
||||
id="path72" /><path
|
||||
style="opacity:1;fill:#fde9e7;fill-opacity:1;stroke:none;stroke-width:2.845;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
||||
d="m 339.7822,389.85927 c 0,0 38.20695,-7.78977 56.38307,-25.96589 5.56412,8.53165 8.16071,32.64283 8.16071,32.64283 l 2.59659,12.98294 16.32141,-62.68907 -6.67694,-5.19318 -5.93506,-17.43424 c 0,0 -15.20859,15.2086 -27.44966,22.99836 -12.24106,7.78977 -34.12659,19.28895 -34.12659,19.28895 l -10.7573,3.70941 z"
|
||||
id="path70" /><path
|
||||
style="opacity:1;fill:none;fill-opacity:1;stroke:#c3b4b0;stroke-width:2.845;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
||||
d="m 347.01555,369.64297 c 0,0 2.22565,10.01541 -1.8547,19.84536 -4.08036,9.82994 -5.00771,9.459 -5.00771,9.459"
|
||||
id="path69" /><path
|
||||
style="opacity:1;fill:#fde9e7;fill-opacity:1;stroke:none;stroke-width:6.945;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
||||
d="m 380.85246,480 c 0,0 44.59016,-17.31147 59.27869,-87.08197 6.81967,-2.62295 34.09836,-14.68852 39.34426,-25.70491 2.62295,-9.96722 -3.67213,-32 -3.67213,-32 l -24.65574,0.52459 -26.7541,4.72131 -4.72131,11.54098 -8.39344,52.45902 -13.11475,28.32787 -15.73771,36.19672 z"
|
||||
id="path68" /><path
|
||||
style="fill:none;fill-opacity:1;stroke:#4c4f59;stroke-width:8.74203;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
||||
d="m 147.6556,277.03379 c 0,0 -11.12655,104.13607 127.58449,103.38689 138.71104,-0.74917 149.83759,-100.39017 149.83759,-100.39017"
|
||||
id="path41"
|
||||
inkscape:label="脸蛋" /><path
|
||||
style="opacity:1;fill:#fff9f6;fill-opacity:1;stroke:none;stroke-width:6.5;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
||||
d="m 123.27869,147.93443 304.78688,21.50819 -24.65573,-40.39344 c 0,0 44.06557,-58.754098 50.88524,-60.327868 6.81967,-1.573771 15.73771,-9.967214 15.73771,-9.967214 l -13.11476,-3.672131 -77.11475,22.032787 c 0,0 -122.7541,-33.57377 -168.39344,-6.295082 -45.63935,27.278689 -69.7705,46.163938 -69.7705,46.163938 z"
|
||||
id="path33"
|
||||
inkscape:label="头发底色2" /><path
|
||||
style="fill:#fde9e7;fill-opacity:1;stroke:none;stroke-width:6.5;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
||||
d="m 331.44501,66.651771 16.22882,30.84274 47.82991,31.367399 6.30629,1.07795 26.88325,-35.335399 -32.10312,-22.211018 -18.88524,5.245901 -30.42623,-11.540983 z"
|
||||
id="path35"
|
||||
inkscape:label="头发部件111" /><path
|
||||
style="opacity:1;fill:#ace0fe;fill-opacity:1;stroke:none;stroke-width:14.5;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
||||
d="m 431.03375,175.82616 17.06329,-17.0633 50.44802,71.96261 -5.93506,17.80518 -33.38472,18.54706 -12.612,-16.32142 17.80518,-11.12823 z"
|
||||
id="path29"
|
||||
inkscape:label="蓝色发卡上" /><path
|
||||
style="opacity:1;fill:#ace0fe;fill-opacity:1;stroke:#4c4f59;stroke-width:6.945;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
||||
d="m 465.90223,261.88453 c 0,0 35.61036,54.52837 2.96753,73.44637 -25.22401,4.08036 -48.22237,2.22565 -48.22237,2.22565 l 10.38636,-26.33683 21.51459,-41.91636 z"
|
||||
id="path66"
|
||||
inkscape:label="蓝色发卡下" /><path
|
||||
style="opacity:1;fill:#febdc7;fill-opacity:1;stroke:none;stroke-width:4.6;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
||||
d="m 471.86885,57.704918 c 0,0 17.04918,68.983602 -11.01639,110.426232 -7.34426,0.26229 -9.18033,-10.49181 -9.18033,-10.49181 l 2.09836,-6.55737 -1.04918,-9.18033 2.36066,-10.7541 1.83606,-5.5082 -6.29508,-6.03278 -6.55738,-5.2459 -2.88524,-5.24591 2.88524,-5.2459 -2.62295,-5.245899 -4.19672,-2.885246 -3.14754,-1.57377 5.2459,-11.540984 11.80328,-7.344262 z"
|
||||
id="path64" /><path
|
||||
id="path57"
|
||||
style="opacity:1;fill:#ff1c1c;fill-opacity:0.0509804;stroke-width:3.92397;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.3;paint-order:fill markers stroke"
|
||||
d="m 244.26691,326.81357 a 35.587568,21.480417 0 0 1 -35.58757,21.48042 35.587568,21.480417 0 0 1 -35.58757,-21.48042 35.587568,21.480417 0 0 1 35.58757,-21.48042 35.587568,21.480417 0 0 1 35.58757,21.48042 z" /><path
|
||||
id="path57-1"
|
||||
style="fill:#ff1c1c;fill-opacity:0.05044398;stroke-width:3.65169;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.3;paint-order:fill markers stroke"
|
||||
d="m 355.20429,336.75188 a 31.611719,20.942554 0 0 1 -31.61171,20.94255 31.611719,20.942554 0 0 1 -31.61172,-20.94255 31.611719,20.942554 0 0 1 31.61172,-20.94255 31.611719,20.942554 0 0 1 31.61171,20.94255 z" /><path
|
||||
style="display:inline;opacity:1;fill:#fbf3ef;fill-opacity:1;stroke:#4c4f59;stroke-width:6.5;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
||||
d="m 188.32787,343.60656 c 0,0 12.06557,-28.32787 15.7377,6.29508 5.24591,-8.91803 9.96722,-17.31148 17.31148,-8.39344 6.81967,2.09836 15.21311,7.34426 4.72131,22.03278 -10.4918,14.68853 16.33814,-18.37136 15.445,-1.63289 -0.72099,13.51206 -24.52214,46.81129 -24.52214,46.81129 0,0 -6.33782,64.18547 -33.41466,70.23144 -36.5485,8.16089 -12.06558,-103.86885 -12.06558,-103.86885 z"
|
||||
id="path1"
|
||||
sodipodi:nodetypes="cccsscscc"
|
||||
inkscape:label="右手" /><path
|
||||
id="path5"
|
||||
style="fill:#fbe7e5;stroke-width:10.4;stroke-linecap:round;stroke-miterlimit:1.3;paint-order:fill markers stroke;fill-opacity:0.40581462"
|
||||
inkscape:label="右手踝阴影"
|
||||
d="m 193.63133,383.92419 a 12.612,12.241061 0 0 1 -12.612,12.24107 12.612,12.241061 0 0 1 -12.612,-12.24107 12.612,12.241061 0 0 1 12.612,-12.24106 12.612,12.241061 0 0 1 12.612,12.24106 z" /><path
|
||||
style="fill:#fee4e0;fill-opacity:0.42411327;stroke:none;stroke-width:10.4;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
||||
d="m 179.09741,356.52608 14.09576,-20.77271 7.04789,1.48377 0.74188,13.72482 11.49918,-11.49918 9.27353,4.08036 8.53165,5.93506 -5.56412,14.83765 13.72483,-4.82224 -3.33847,17.80518 -47.85143,-27.44965 z"
|
||||
id="path9"
|
||||
inkscape:label="右手手指阴影" /><path
|
||||
style="display:inline;fill:none;fill-opacity:1;stroke:#4c4f59;stroke-width:6.5;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
||||
d="m 188.32787,343.60656 c 0,0 12.06557,-28.32787 15.7377,6.29508 5.24591,-8.91803 9.96722,-17.31148 17.31148,-8.39344 6.81967,2.09836 15.21311,7.34426 4.72131,22.03278 -10.4918,14.68853 16.33814,-18.37136 15.445,-1.63289 -0.72099,13.51206 -24.52214,46.81129 -24.52214,46.81129 0,0 -6.33782,64.18547 -33.41466,70.23144 -36.5485,8.16089 -12.06558,-103.86885 -12.06558,-103.86885 z"
|
||||
id="path1-6"
|
||||
sodipodi:nodetypes="cccsscscc"
|
||||
inkscape:label="右手遮挡" /><path
|
||||
style="opacity:1;fill:#fff9f6;fill-opacity:1;stroke:#4c4f59;stroke-width:6.5;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
||||
d="m 137.44262,123.2787 c 0,0 49.33852,-48.649769 86.97684,-58.734919 0.10513,0.76769 -120.16719,-122.15824 -86.97684,58.734919 z"
|
||||
id="path3"
|
||||
inkscape:label="头发部件19 左猫耳" /><path
|
||||
style="fill:#fde9e7;fill-opacity:1;stroke-width:1.18016;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.3;paint-order:fill markers stroke"
|
||||
d="m 198.85246,42 -63.6342,39.016573 3.84123,40.939237 L 184.82787,87 224,64.5 Z"
|
||||
id="path58"
|
||||
sodipodi:nodetypes="cccccc" /><path
|
||||
style="opacity:1;fill:#1a1a1a;fill-opacity:0.465732;stroke:#a18f90;stroke-width:6.5;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
||||
id="path4"
|
||||
d="m 404.96447,131.7729 c 0.52512,-0.76931 1.0357,-1.54875 1.57536,-2.30793 2.82418,-3.97291 5.82035,-7.82036 8.67758,-11.76948 2.39309,-3.30762 3.93978,-5.54326 6.29133,-8.89998 5.32612,-7.58044 11.18023,-14.760796 17.20894,-21.78699 3.02173,-3.55064 6.06655,-7.098864 9.47662,-10.289082 1.96198,-1.835492 4.21906,-3.672787 6.27415,-5.392122 5.10823,-4.357455 10.66931,-8.109574 16.22169,-11.862072 0,0 -2.92504,-1.864176 -2.92504,-1.864176 v 0 c -5.41592,3.900149 -10.90348,7.705144 -15.99184,12.040062 -1.99887,1.645368 -4.44389,3.611151 -6.36286,5.369248 -3.45237,3.162941 -6.48944,6.731577 -9.48752,10.316639 -5.95526,7.087894 -11.63254,14.399231 -17.00034,21.944173 -5.47138,7.72885 -10.96322,15.45542 -17.11251,22.66867 z"
|
||||
inkscape:label="头发部件18" /><path
|
||||
style="opacity:1;fill:#1a1a1a;fill-opacity:0.465732;stroke:#a18f90;stroke-width:6.5;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
||||
id="path6"
|
||||
d="m 431.7055,96.885136 c 0.68345,-0.01683 1.36993,0.008 2.05351,0.03239 1.26616,0.04274 2.52519,0.175792 3.77564,0.374299 1.34943,0.219358 2.67703,0.547853 3.99756,0.89747 1.30448,0.342355 2.58658,0.760225 3.8587,1.206247 0.34787,0.132122 0.88919,0.332358 1.23164,0.483411 0.1893,0.0835 0.73774,0.373687 0.5597,0.268307 -4.92017,-2.912189 -2.83776,-1.86942 -2.01609,-0.911603 0.4665,0.942833 -0.25414,1.810973 -0.83258,2.515753 -1.05305,1.11546 -2.37988,1.92349 -3.65077,2.76486 -0.70455,0.52564 -1.84263,0.99551 -2.02707,1.9792 -0.13577,0.72412 0.14689,1.00706 0.45808,1.64499 0.25805,0.31484 0.49646,0.64686 0.77415,0.94453 0.63085,0.67625 1.54885,1.45348 2.25243,2.03623 1.28413,1.06359 2.61277,2.07518 3.92585,3.10243 2.22765,1.73231 4.46676,3.44711 6.4408,5.4696 1.24472,1.36548 2.33589,2.86124 3.10757,4.54226 0.57402,1.31239 0.61894,2.68201 0.46471,4.08008 -0.15236,1.542 -1.01177,2.74753 -1.96982,3.90485 -1.25092,1.39253 -2.70297,2.57966 -3.98322,3.94302 -0.61776,0.76458 -1.19371,1.60995 -1.3359,2.60746 -0.0756,0.53059 -0.0152,0.87319 0.031,1.40055 0.18192,1.34638 0.59785,2.64226 1.03422,3.92358 0.46172,1.36123 1.10075,2.65093 1.71056,3.94949 0.57934,1.12566 0.88081,2.32829 0.97901,3.58284 0.0285,1.01273 -0.14083,2.01608 -0.35002,3.00302 -0.18321,0.8594 -0.39147,1.62037 -0.92358,2.32955 -0.69384,0.72591 -1.52468,1.30006 -2.31082,1.91913 0,0 3.13155,1.96912 3.13155,1.96912 v 0 c 0.80747,-0.64335 1.66352,-1.23803 2.37418,-1.9921 0.58364,-0.77755 0.80933,-1.52536 1.00495,-2.4688 0.21825,-1.01967 0.38801,-2.05558 0.38421,-3.10149 -0.0761,-1.29512 -0.34601,-2.55043 -0.93895,-3.71574 -0.60728,-1.2882 -1.24849,-2.56502 -1.72185,-3.91075 -0.43768,-1.25837 -0.85635,-2.529 -1.07055,-3.84806 -0.053,-0.43231 -0.12968,-0.85315 -0.0855,-1.29165 0.0948,-0.94059 0.64999,-1.73394 1.21835,-2.45227 1.27656,-1.3752 2.72864,-2.57131 3.98645,-3.96542 1.00742,-1.20382 1.92485,-2.46496 2.104,-4.07351 0.17452,-1.45227 0.1673,-2.88462 -0.39573,-4.26425 -0.74413,-1.73945 -1.85028,-3.26977 -3.10544,-4.6809 -0.76822,-0.80351 -0.91233,-0.98548 -1.76144,-1.74089 -1.5106,-1.3439 -3.15419,-2.52947 -4.72414,-3.80078 -1.30934,-1.02359 -2.63319,-2.03185 -3.9193,-3.08473 -0.71006,-0.5813 -1.57278,-1.30278 -2.22387,-1.96185 -0.27049,-0.2738 -0.51217,-0.57463 -0.76826,-0.86194 -0.2502,-0.39692 -0.62512,-0.79772 -0.57512,-1.31981 0.0792,-0.82671 1.33713,-1.38331 1.899,-1.81756 1.29884,-0.86671 2.65474,-1.69887 3.73107,-2.84377 0.6873,-0.83746 1.4042,-1.79633 1.075,-2.93575 -0.0816,-0.13585 -0.14456,-0.28482 -0.24481,-0.40755 -0.1097,-0.13429 -0.23095,-0.26613 -0.3791,-0.356228 -1.82458,-1.109627 -3.48552,-2.166773 -5.4739,-2.822822 -1.27815,-0.438878 -2.56689,-0.847162 -3.87638,-1.183261 -1.32827,-0.341807 -2.66392,-0.6605 -4.021,-0.867076 -1.26646,-0.185139 -2.54065,-0.306124 -3.82011,-0.354118 -0.68835,-0.02484 -1.39159,-0.0088 -2.07079,-0.120664 z"
|
||||
inkscape:label="头发部件17" /><path
|
||||
style="display:inline;opacity:1;fill:#1a1a1a;fill-opacity:0.465732;stroke:#4c4f59;stroke-width:6.5;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
||||
id="path7"
|
||||
d="m 472.56515,132.89174 c 0.0148,0.77317 -0.10076,1.54619 -0.19578,2.31209 -0.22663,1.66469 -0.65036,3.29152 -1.09324,4.90909 -0.61155,2.16519 -1.31643,4.30298 -2.07388,6.4211 -0.85906,2.40245 -1.88628,4.73722 -3.04362,7.0095 -1.12519,2.18837 -2.40662,4.28829 -3.77255,6.3331 -1.20044,1.78353 -2.51232,3.48695 -3.85961,5.16088 -0.77567,0.94691 -1.56273,1.88443 -2.34905,2.82249 0,0 3.09439,1.85764 3.09439,1.85764 v 0 c 0.78058,-0.94434 1.56325,-1.88703 2.331,-2.84187 1.33972,-1.69155 2.6509,-3.40675 3.84535,-5.20534 1.36483,-2.06191 2.64248,-4.17929 3.77385,-6.37958 1.16439,-2.28454 2.19746,-4.6314 3.07763,-7.04092 0.77266,-2.11878 1.49327,-4.25705 2.12518,-6.4224 0.46749,-1.62041 0.91079,-3.25003 1.1854,-4.9163 0.11547,-0.76691 0.21504,-1.53597 0.32143,-2.30416 z"
|
||||
inkscape:label="头发部件16" /><path
|
||||
style="display:inline;opacity:1;fill:#fff9f6;fill-opacity:1;stroke:#4c4f59;stroke-width:6.5;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
||||
d="m 322.62295,86.032787 c 0,0 75.43341,26.120553 110.16394,89.180323 33.44261,60.72132 33.04918,62.42623 33.04918,62.42623 0,0 0.52459,8.39345 -19.93443,11.54099 -2.62295,0 14.68852,30.95082 19.93443,37.2459 -14.68853,7.34426 -16.78689,8.91803 -16.78689,8.91803 0,0 6.29508,7.86885 4.72131,15.73771 -17.31147,-0.52459 -22.55738,-1.04918 -22.55738,-1.04918 0,0 -4.19672,17.31147 -15.7377,33.04918 C 410.22951,333.11475 410.22951,320 410.22951,320 c 0,0 -24.13115,36.72131 -72.39344,41.44262 -1.57377,-2.62295 12.59016,-14.16393 17.83606,-28.32787 -11.54098,1.57377 -14.68852,2.09836 -14.68852,2.09836 l 10.4918,-16.78688 c 0,0 11.01639,-24.13115 9.44262,-28.32787 -1.04918,-2.62295 -32,-57.70492 -32,-57.70492 0,0 -24.65573,33.04918 -43.54098,49.83607 -6.81967,-4.19672 -13.63934,-14.68853 -13.63934,-14.68853 l -12.59017,11.54099 c 0,0 -48.43778,-50.76436 -42.0945,-90.18318 -3.14754,12.06557 0.12729,56.60941 -5.6432,62.3799 -5.24591,-2.09836 -16.2623,-14.68853 -14.16394,-40.91803 -0.52459,-0.52459 -29.90164,41.44262 -29.90164,41.44262 0,0 -4.19672,48.78688 16.2623,73.44262 1.57377,2.62295 -1.57377,16.2623 -31.47541,-2.62295 1.04918,-1.57377 -1.04918,9.96721 -1.04918,9.96721 0,0 -46.68853,2.09836 -45.63935,-60.85245 -2.62295,-2.62296 -13.639341,34.09836 -13.639341,34.09836 0,0 -24.131151,-32 -0.52459,-92.85246 11.803281,-30.42623 23.344261,-52.85246 31.934421,-67.67213 8.59017,-14.81968 14.22951,-22.03279 14.22951,-22.03279"
|
||||
id="path8"
|
||||
sodipodi:nodetypes="csccccccccccccccccccccccccccssc"
|
||||
inkscape:label="头发部件15 头发底色" /><path
|
||||
id="path36"
|
||||
style="fill:#fffdfc;stroke-width:7.4278;stroke-linecap:round;stroke-miterlimit:1.3;paint-order:fill markers stroke"
|
||||
inkscape:label="头发打光"
|
||||
d="m 355.67214,146.88524 a 91.803276,41.967213 0 0 1 -91.80327,41.96721 91.803276,41.967213 0 0 1 -91.80328,-41.96721 91.803276,41.967213 0 0 1 91.80328,-41.96721 91.803276,41.967213 0 0 1 91.80327,41.96721 z" /><path
|
||||
style="fill:#fde9e7;fill-opacity:1;stroke:none;stroke-width:6.5;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
||||
d="m 349.37705,329.44262 c 0,0 71.34426,-41.96721 78.68852,-91.27868 0,1.04918 26.22951,28.32786 26.22951,28.32786 l 11.54099,24.13115 -15.73771,7.34426 2.09836,13.63935 -22.03279,-1.04918 -12.59016,30.42623 -12.59016,-17.83607 -23.08197,26.22951 -45.11475,14.68852 z"
|
||||
id="path37"
|
||||
inkscape:label="头发阴影"
|
||||
sodipodi:nodetypes="cccccccccccc" /><path
|
||||
style="fill:#fde9e7;fill-opacity:1;stroke:none;stroke-width:6.5;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
||||
d="m 325.2459,144.78689 c 0,0 10.49181,44.06557 2.09836,78.68852 0,10.4918 33.57377,60.85246 33.57377,60.85246 0,0 14.68853,-71.34426 -25.18032,-132.19672 -8.39345,-5.2459 -10.49181,-7.34426 -10.49181,-7.34426 z"
|
||||
id="path38"
|
||||
inkscape:label="头发阴影2" /><path
|
||||
style="fill:#fde9e7;fill-opacity:1;stroke:none;stroke-width:6.5;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
||||
d="m 186.7541,166.81967 23.08197,1.04918 c 0,0 -14.68853,26.22951 -13.63935,46.16394 -2.09836,-3.14754 -22.03279,31.47541 -22.03279,31.47541 0,0 -9.44262,-14.68853 -3.14754,-50.36066 9.44263,-16.78688 15.73771,-28.32787 15.73771,-28.32787 z"
|
||||
id="path39"
|
||||
inkscape:label="头发阴影" /><path
|
||||
id="path36-5"
|
||||
style="fill:#fffdfc;stroke-width:6.94619;stroke-linecap:round;stroke-miterlimit:1.3;paint-order:fill markers stroke"
|
||||
inkscape:label="头发打光"
|
||||
transform="matrix(0.99965843,-0.02613486,0,1,0,0)"
|
||||
d="m 340.28017,112.23744 a 80.289719,41.967213 0 0 1 -80.28972,41.96721 80.289719,41.967213 0 0 1 -80.28972,-41.96721 80.289719,41.967213 0 0 1 80.28972,-41.967211 80.289719,41.967213 0 0 1 80.28972,41.967211 z" /><path
|
||||
style="opacity:1;fill:none;fill-opacity:1;stroke:#a18f90;stroke-width:7.7;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
||||
d="m 138.62295,122.4918 c 0,0 53.63935,-52.983604 92.98361,-58.229505"
|
||||
id="path65"
|
||||
sodipodi:nodetypes="cc" /><path
|
||||
style="opacity:1;fill:#1a1a1a;fill-opacity:0;stroke:#c3b4b0;stroke-width:6.5;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
||||
d="m 436.45902,268.06557 c 0,0 3.67213,18.36066 -2.62295,42.49181"
|
||||
id="path17"
|
||||
inkscape:label="头发部件7" /><path
|
||||
style="opacity:1;fill:#1a1a1a;fill-opacity:0;stroke:#c3b4b0;stroke-width:6.5;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
||||
d="m 418.09836,305.83607 -8.91803,17.83606"
|
||||
id="path16"
|
||||
inkscape:label="头发部件8" /><path
|
||||
style="opacity:1;fill:#1a1a1a;fill-opacity:0;stroke:#c3b4b0;stroke-width:6.5;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
||||
d="m 377.70492,300.59016 c 0,0 2.62295,12.06558 -22.03279,32"
|
||||
id="path15"
|
||||
inkscape:label="头发部件9" /><path
|
||||
style="opacity:1;fill:#1a1a1a;fill-opacity:0;stroke:#c3b4b0;stroke-width:4.2;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
||||
d="m 284.32787,97.573771 c 0,0 12.06557,39.344259 -27.80328,119.081969 0.52459,0 53.5082,-38.81967 56.13115,-89.18033"
|
||||
id="path10"
|
||||
inkscape:label="头发部件14" /><path
|
||||
style="opacity:1;fill:#1a1a1a;fill-opacity:0;stroke:#c3b4b0;stroke-width:5.1;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
||||
d="m 272.78689,87.081967 c 0,0 13.63934,-8.393442 18.88524,-0.52459 17.17267,10.839383 13.03281,30.236103 24.19672,44.393443 1.79263,1.85342 7.1718,-0.78131 7.80328,1.77049 4.88126,19.72513 14.81582,50.80617 4.32401,100.11764"
|
||||
id="path11"
|
||||
sodipodi:nodetypes="csssc"
|
||||
inkscape:label="头发部件13" /><path
|
||||
style="opacity:1;fill:#1a1a1a;fill-opacity:0;stroke:#c3b4b0;stroke-width:4.5;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
||||
d="m 171.00392,241.8537 c 0,0 -28.56248,-60.09248 92.36438,-147.634619 2.22564,0.741882 -59.35061,54.157419 -66.02755,111.282379"
|
||||
id="path12"
|
||||
inkscape:label="头发部件12" /><path
|
||||
style="opacity:1;fill:#1a1a1a;fill-opacity:0;stroke:#c3b4b0;stroke-width:3;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
||||
d="m 327.34426,143.7377 c 0,0 25.18033,20.45902 32,65.57378"
|
||||
id="path13"
|
||||
inkscape:label="头发部件11" /><path
|
||||
style="opacity:1;fill:#1a1a1a;fill-opacity:0;stroke:#4c4f59;stroke-width:6.5;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
||||
d="m 362.4918,268.59016 -3.14754,19.93443"
|
||||
id="path14"
|
||||
inkscape:label="头发部件10" /><path
|
||||
style="opacity:1;fill:#1a1a1a;fill-opacity:0;stroke:#4c4f59;stroke-width:6.5;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
||||
d="m 421.77049,344.13115 c 0,0 -2.62295,58.22951 -33.57377,111.7377 -30.95082,53.5082 38.29508,15.73771 38.29508,15.73771"
|
||||
id="path18"
|
||||
inkscape:label="头发部件6" /><path
|
||||
style="opacity:1;fill:#1a1a1a;fill-opacity:0;stroke:#4c4f59;stroke-width:6.5;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
||||
d="m 395.01639,348.32787 12.06558,60.32787"
|
||||
id="path19"
|
||||
inkscape:label="头发部件5" /><path
|
||||
style="opacity:1;fill:#1a1a1a;fill-opacity:0;stroke:#4c4f59;stroke-width:6.5;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
||||
d="m 371.93443,423.86885 c 0,0 30.95082,-25.70492 24.65573,-73.96721"
|
||||
id="path20"
|
||||
inkscape:label="头发部件4" /><path
|
||||
style="opacity:1;fill:#1a1a1a;fill-opacity:0;stroke:#4c4f59;stroke-width:6.5;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
||||
d="m 332.06557,427.54098 c 0,0 36.72132,1.57377 38.81968,-29.37705"
|
||||
id="path21"
|
||||
inkscape:label="头发部件3" /><path
|
||||
style="opacity:1;fill:#1a1a1a;fill-opacity:0;stroke:#4c4f59;stroke-width:6.5;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
||||
d="m 372.45902,427.01639 -2.09836,-28.85246"
|
||||
id="path22"
|
||||
inkscape:label="头发部件2" /><path
|
||||
style="fill:#93cefc;fill-opacity:1;stroke:#4d4e59;stroke-width:6.5;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
||||
d="m 377.50929,228.61046 c 0,0 24.13115,1.57377 24.13115,10.49181 -0.52459,3.67213 -11.54099,17.83606 -23.60656,18.88524 -2.09836,-1.57377 -0.52459,-29.37705 -0.52459,-29.37705 z"
|
||||
id="path34"
|
||||
sodipodi:nodetypes="cccc"
|
||||
inkscape:label="兔子发卡后" /><path
|
||||
style="opacity:1;fill:#fffdfe;fill-opacity:1;stroke:#4c4f59;stroke-width:6.5;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
||||
d="m 350.95082,235.54098 c 0,0 -6.55738,-22.03278 1.04918,-22.03278 7.60656,0 13.37705,13.90164 13.37705,13.90164 0,0 5.05941,-17.72738 9.96722,-15.21311 11.79758,6.04389 3.40983,21.77048 3.40983,21.77048 0,0 18.62295,25.96722 -12.06558,27.27869 -30.16717,1.28919 -15.7377,-25.70492 -15.7377,-25.70492 z"
|
||||
id="path24"
|
||||
sodipodi:nodetypes="cscscsc"
|
||||
inkscape:label="兔子发卡" /><path
|
||||
style="display:inline;opacity:1;fill:#1a1a1a;fill-opacity:0;stroke:#4c4f59;stroke-width:6.5;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
||||
d="m 332.06557,425.96721 c 0,0 10.49181,-25.18032 6.29509,-49.83606"
|
||||
id="path23"
|
||||
inkscape:label="头发部件" /><path
|
||||
style="display:inline;opacity:1;fill:#e85240;fill-opacity:1;stroke:#4c4f59;stroke-width:6.5;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
||||
d="m 192.2623,268.85246 c 0,0 -15.47541,22.03279 -2.62296,35.40984 42.7541,0.26229 43.27869,0.52459 43.27869,0.52459 0,0 11.27869,-8.39345 5.5082,-32 -25.44262,-9.96722 -46.16393,-3.93443 -46.16393,-3.93443 z"
|
||||
id="path25"
|
||||
inkscape:label="左眼白" /><path
|
||||
style="opacity:1;fill:#fbb579;fill-opacity:1;stroke:none;stroke-width:6.5;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
||||
d="m 188.06557,301.11475 c -0.44151,-8.78192 13.4528,-14.01636 22.29509,-14.42623 8.67244,-0.402 23.2398,3.38526 23.08196,12.06558 -0.0775,4.26198 -11.54098,5.5082 -11.54098,5.5082 0,0 -33.2673,8.16555 -33.83607,-3.14755 z"
|
||||
id="path27"
|
||||
sodipodi:nodetypes="saacs"
|
||||
inkscape:label="左眼瞳" /><path
|
||||
id="path26"
|
||||
style="display:inline;opacity:1;fill:#ffffff;stroke-width:6.5;stroke-linecap:round;stroke-miterlimit:1.3;paint-order:fill markers stroke"
|
||||
inkscape:label="眼光"
|
||||
d="m 217.96721,273.04919 a 7.8688526,7.0819674 0 0 1 -7.86885,7.08197 7.8688526,7.0819674 0 0 1 -7.86885,-7.08197 7.8688526,7.0819674 0 0 1 7.86885,-7.08196 7.8688526,7.0819674 0 0 1 7.86885,7.08196 z" /><path
|
||||
style="fill:none;fill-opacity:1;stroke:#ebc2bf;stroke-width:2.5;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
||||
d="m 174.16393,245.5082 c 15.08644,-4.76649 31.53934,-7.45293 47.09358,-3.51527 3.29779,0.83486 6.51821,1.99257 9.56216,3.51527"
|
||||
id="path42"
|
||||
inkscape:path-effect="#path-effect42"
|
||||
inkscape:original-d="m 174.16393,245.5082 c 11.60252,-3.79212 36.99244,-9.83166 56.65574,0"
|
||||
transform="translate(0,4)" /><path
|
||||
style="opacity:1;fill:#fde9e7;fill-opacity:1;stroke:none;stroke-width:6.945;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
||||
d="m 477.77235,132.79697 c 0,0 13.35388,10.01542 14.83765,17.43424 1.48376,7.41883 1.48376,57.4959 1.48376,57.4959 l -4.45129,1.11282 -30.78813,-37.46507 c 0,0 8.90259,-14.46671 11.12824,-22.62741 2.22565,-8.16071 7.78977,-15.95048 7.78977,-15.95048 z"
|
||||
id="path67" /><path
|
||||
style="fill:#e85240;fill-opacity:0;stroke:#4c4f59;stroke-width:6.5;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
||||
d="m 192.2623,268.85246 c 0,0 -15.47541,22.03279 -2.62296,35.40984 42.7541,0.26229 43.27869,0.52459 43.27869,0.52459 0,0 11.27869,-8.39345 5.5082,-32 -25.44262,-9.96722 -46.16393,-3.93443 -46.16393,-3.93443 z"
|
||||
id="path25-9"
|
||||
inkscape:label="左眼遮挡上" /><path
|
||||
style="display:inline;fill:#e85240;fill-opacity:1;stroke:#4c4f59;stroke-width:6.5;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
||||
d="m 325.52138,272.81871 c 17.02505,1.06627 38.81967,23.08196 17.83607,48.26229 -1.57377,0.52459 -48.98481,-5.30599 -49.57378,-9.18033 -0.80561,-5.29953 -7.95666,-41.56799 31.73771,-39.08196 z"
|
||||
id="path40"
|
||||
sodipodi:nodetypes="scss"
|
||||
inkscape:label="右眼白" /><path
|
||||
style="fill:#fbb579;fill-opacity:1;stroke:none;stroke-width:7.14054;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
||||
d="m 293.04592,306.32136 c 2.55323,-8.53469 20.43105,-9.52962 30.78942,-7.36618 10.1594,2.12188 25.66784,9.9581 22.45763,18.22228 -1.5762,4.05766 -15.25453,1.93723 -15.25453,1.93723 0,0 -41.28165,-1.79873 -37.99252,-12.79333 z"
|
||||
id="path27-0"
|
||||
sodipodi:nodetypes="saacs"
|
||||
inkscape:label="左眼瞳" /><path
|
||||
style="display:inline;fill:none;fill-opacity:1;stroke:#4c4f59;stroke-width:6.5;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
||||
d="m 325.52138,272.81871 c 17.02505,1.06627 38.81967,23.08196 17.83607,48.26229 -1.57377,0.52459 -48.98481,-5.30599 -49.57378,-9.18033 -0.80561,-5.29953 -7.95666,-41.56799 31.73771,-39.08196 z"
|
||||
id="path40-2"
|
||||
sodipodi:nodetypes="scss"
|
||||
inkscape:label="右眼遮挡上" /><path
|
||||
style="fill:none;fill-opacity:1;stroke:#ebc2bf;stroke-width:2.5;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
||||
d="m 309.32819,258.43459 c 0,0 26.2295,-5.2459 36.72131,4.19672"
|
||||
id="path43" /><path
|
||||
id="path26-9"
|
||||
style="display:inline;fill:#ffffff;stroke-width:6.5;stroke-linecap:round;stroke-miterlimit:1.3;paint-order:fill markers stroke"
|
||||
inkscape:label="眼光"
|
||||
d="m 329.6342,284.99301 a 7.8688526,7.0819674 0 0 1 -7.86885,7.08197 7.8688526,7.0819674 0 0 1 -7.86885,-7.08197 7.8688526,7.0819674 0 0 1 7.86885,-7.08197 7.8688526,7.0819674 0 0 1 7.86885,7.08197 z" /><path
|
||||
style="opacity:1;fill:none;fill-opacity:1;stroke:#4c4f59;stroke-width:14.5;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
||||
d="m 170.4918,266.4918 c 0,0 32,-19.93442 70.29509,-2.09836"
|
||||
id="path28"
|
||||
inkscape:label="眉毛" /><path
|
||||
style="fill:none;fill-opacity:1;stroke:#4c4f59;stroke-width:14.5;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
||||
d="m 292.9971,274.17683 c 0,0 35.55098,-12.55026 69.08832,13.13797"
|
||||
id="path28-2"
|
||||
inkscape:label="眉毛" /><path
|
||||
style="display:inline;opacity:1;fill:none;fill-opacity:0;stroke:#4c4f59;stroke-width:6.5;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
||||
d="m 431.03375,176.1971 18.17612,-17.43424 41.54542,54.15742"
|
||||
id="path30"
|
||||
inkscape:label="蓝色发卡遮挡上" /><path
|
||||
style="opacity:1;fill:none;fill-opacity:0;stroke:#4c4f59;stroke-width:6.5;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
||||
d="m 454.0321,267.44865 43.21466,-23.92571"
|
||||
id="path31"
|
||||
sodipodi:nodetypes="cc"
|
||||
inkscape:label="蓝色发卡遮挡下" /><path
|
||||
style="display:inline;fill:#fff9f6;fill-opacity:0;stroke:#4c4f59;stroke-width:6.5;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
||||
d="m 164.23888,424.64081 -31.07097,26.05282 c 0,0 -5.19317,18.17613 25.96589,28.93342 31.15907,10.7573 213.29123,1.48377 213.29123,1.48377 0,0 52.30272,11.87012 80.86519,-37.09413 -0.37094,0.74188 -8.90259,19.28895 -8.90259,19.28895 l 1.48377,19.28894 c 0,0 57.73516,-9.41425 30.45647,-148.95524 4.19672,-9.44262 8.39344,-15.21311 8.39344,-15.21311 l -3.14754,-21.77049 c 0,0 5.65758,-40.33533 7.88323,-45.52851 2.22564,-5.19317 8.51558,-6.35116 9.64447,-11.12823 2.06907,-8.75561 -2.78206,-20.40177 -6.67694,-26.15136 -0.74189,-3.33847 6.86241,-35.05395 2.04017,-58.05231 -7.04788,-11.87012 -4.82223,-11.87012 -18.918,-25.59495 0,0.37094 8.90259,-44.88389 -0.74188,-73.446367 -2.96753,0 -32.27189,-10.757296 -96.81567,23.740241 -0.37094,1.112824 -26.33683,-30.046242 -153.56969,-15.950474 -0.74188,0 -43.40012,-37.836009 -66.76942,-40.432598 -1.11283,-0.370941 -22.62742,-4.080354 -25.22401,48.964247 0,1.112824 -44.14201,15.950474 -56.01213,64.914721 -11.87012,48.96425 -11.128238,80.86519 -11.128238,80.86519 0,0 -18.904912,27.6632 -9.798121,25.03308 5.346826,-1.54421 4.975885,23.56023 4.975885,23.56023 l -2.225648,39.31977 c 0,0 -14.466709,35.23942 -14.83765,59.72155 -0.370942,24.48212 -0.741883,34.12659 4.822236,48.5933 5.564119,14.46671 20.772711,46.7386 42.658245,63.06002 8.902591,0.37094 18.918001,1.8547 18.918001,1.8547 l 3.70942,-20.77271 c 0,0 18.918,15.95048 32.27189,14.83765 13.35388,-1.11282 -0.74189,-0.74188 -0.74189,-0.74188 l -13.72482,-17.80518 z"
|
||||
id="path2-4"
|
||||
sodipodi:nodetypes="ccscccccccsaccccccccscsccsscccsccc"
|
||||
inkscape:label="上发层" /><path
|
||||
style="fill:none;fill-opacity:1;stroke:#c3b4b0;stroke-width:6.5;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
||||
d="m 197.5082,220.06557 18.36065,-12.32786"
|
||||
id="path46" /><path
|
||||
style="fill:none;fill-opacity:1;stroke:#c3b4b0;stroke-width:6.5;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
||||
d="m 327.54114,234.24941 c 0,0 -6.27192,4.90452 -15.85821,-0.991"
|
||||
id="path47"
|
||||
sodipodi:nodetypes="cc" /><path
|
||||
style="display:inline;fill:none;fill-opacity:1;stroke:#4c4f59;stroke-width:6.5;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
||||
d="m 322.62295,86.03279 c 0,0 75.43341,26.12055 110.16394,89.18032 33.44261,60.72132 33.04918,62.42623 33.04918,62.42623 0,0 0.52459,8.39345 -19.93443,11.54099 -2.62295,0 14.68852,30.95082 19.93443,37.2459 -14.68853,7.34426 -16.78689,8.91803 -16.78689,8.91803 0,0 6.29508,7.86885 4.72131,15.73771 -17.31147,-0.52459 -22.55738,-1.04918 -22.55738,-1.04918 0,0 -4.19672,17.31147 -15.7377,33.04918 C 410.22951,333.11475 410.22951,320 410.22951,320 c 0,0 -24.13115,36.72131 -72.39344,41.44262 -1.57377,-2.62295 12.59016,-14.16393 17.83606,-28.32787 -11.54098,1.57377 -14.68852,2.09836 -14.68852,2.09836 l 10.4918,-16.78688 c 0,0 11.01639,-24.13115 9.44262,-28.32787 -1.04918,-2.62295 -32,-57.70492 -32,-57.70492 0,0 -24.65573,33.04918 -43.54098,49.83607 -6.81967,-4.19672 -13.63934,-14.68853 -13.63934,-14.68853 l -12.59017,11.54099 c 0,0 -48.43778,-50.76436 -42.0945,-90.18318 -3.14754,12.06557 0.12729,56.60941 -5.6432,62.3799 -5.24591,-2.09836 -16.2623,-14.68853 -14.16394,-40.91803 -0.52459,-0.52459 -29.90164,41.44262 -29.90164,41.44262 0,0 -4.19672,48.78688 16.2623,73.44262 1.57377,2.62295 -1.57377,16.2623 -31.47541,-2.62295 1.04918,-1.57377 -1.04918,9.96721 -1.04918,9.96721 0,0 -46.68853,2.09836 -45.63935,-60.85245 -2.62295,-2.62296 -13.63934,34.09836 -13.63934,34.09836 0,0 -24.13115,-32 -0.52459,-92.85246 11.80328,-30.42623 23.34426,-52.85246 31.93442,-67.67213 8.59017,-14.81968 14.22951,-22.03279 14.22951,-22.03279"
|
||||
id="path8-0"
|
||||
sodipodi:nodetypes="csccccccccccccccccccccccccccssc"
|
||||
inkscape:label="头发部件15 头发底色" /><path
|
||||
id="path44-3"
|
||||
style="fill:#a88a8f;stroke:#4c4f59;stroke-width:2.8623;stroke-linecap:round;stroke-miterlimit:1.3;paint-order:fill markers stroke"
|
||||
d="m 216.29183,255.53888 a 8.8904896,7.4928694 0 0 1 -8.89049,7.49287 8.8904896,7.4928694 0 0 1 -8.89049,-7.49287 8.8904896,7.4928694 0 0 1 8.89049,-7.49287 8.8904896,7.4928694 0 0 1 8.89049,7.49287 z" /><path
|
||||
id="path44"
|
||||
style="fill:#a88a8f;stroke:#4c4f59;stroke-width:2.8623;stroke-linecap:round;stroke-miterlimit:1.3;paint-order:fill markers stroke"
|
||||
d="m 336.16933,270.5473 a 8.8904896,7.4928694 0 0 1 -8.89049,7.49287 8.8904896,7.4928694 0 0 1 -8.89049,-7.49287 8.8904896,7.4928694 0 0 1 8.89049,-7.49287 8.8904896,7.4928694 0 0 1 8.89049,7.49287 z" /><path
|
||||
style="opacity:1;fill:none;fill-opacity:0;stroke:#a18f90;stroke-width:10.4;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
||||
d="m 318.7189,85.461784 c 0,0 50.44801,17.063296 76.78484,43.400126 26.33683,26.33683 32.64283,39.31978 32.64283,39.31978"
|
||||
id="path32"
|
||||
inkscape:label="上方头发边缘加厚" /><path
|
||||
style="fill:none;fill-opacity:1;stroke:#4c4f59;stroke-width:6.5;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
||||
d="m 186.7541,224.52459 10.4918,1.04918"
|
||||
id="path45" /><path
|
||||
style="opacity:1;fill:#fcbeb5;fill-opacity:1;stroke:#e99c9b;stroke-width:3.4;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
||||
d="m 250.94176,349.98308 c 0,0 -13.91029,-8.90259 -10.0154,-16.69235 3.89488,-7.78977 17.80517,-4.4513 17.80517,-4.4513 0,0 12.98295,-1.29829 20.03083,-3.89488 7.04788,-2.59659 9.64447,5.0077 9.64447,5.0077 0,0 4.76461,6.06544 -4.26583,16.50689 -5.93505,6.86241 -21.14364,7.04788 -21.14364,7.04788"
|
||||
id="path48"
|
||||
sodipodi:nodetypes="cscscsc" /><path
|
||||
style="opacity:1;fill:#ffffff;fill-opacity:1;stroke:#e99c9b;stroke-width:1.8;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
||||
d="m 276.20884,325.76493 c 0,0 4.35031,3.01904 5.37456,7.95235 0.57119,0.10629 3.33355,-3.08729 2.97647,-6.87263 -0.67968,-1.48531 -8.35103,-1.07972 -8.35103,-1.07972 z"
|
||||
id="path49" /><path
|
||||
style="opacity:1;fill:#ffffff;fill-opacity:1;stroke:#e99c9b;stroke-width:1.8;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
||||
d="m 245.0067,329.39584 -4.17308,2.78206 -0.0927,6.0278 -0.0956,-0.0355 7.07328,-8.8174 z"
|
||||
id="path50"
|
||||
sodipodi:nodetypes="cccccc" /><path
|
||||
style="fill:none;fill-opacity:1;stroke:#e99c9b;stroke-width:3.4;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
||||
d="m 250.94176,349.98308 c 0,0 -13.91029,-8.90259 -10.0154,-16.69235 3.89488,-7.78977 17.80517,-4.4513 17.80517,-4.4513 0,0 12.98295,-1.29829 20.03083,-3.89488 7.04788,-2.59659 9.64447,5.0077 9.64447,5.0077 0,0 4.76461,6.06544 -4.26583,16.50689 -5.93505,6.86241 -21.14364,7.04788 -21.14364,7.04788"
|
||||
id="path48-2"
|
||||
sodipodi:nodetypes="cscscsc" /><path
|
||||
style="opacity:1;fill:#ffffff;fill-opacity:1;stroke:#e89493;stroke-width:2.8;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
||||
d="m 190.84928,314.92913 6.12053,15.02312"
|
||||
id="path51" /><path
|
||||
style="opacity:1;fill:#ffffff;fill-opacity:1;stroke:#e89493;stroke-width:2.8;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
||||
d="m 204.94505,316.96931 5.56412,14.28124"
|
||||
id="path52" /><path
|
||||
style="opacity:1;fill:#ffffff;fill-opacity:1;stroke:#e89493;stroke-width:2.8;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
||||
d="m 217.92799,317.34025 7.41883,16.50689"
|
||||
id="path53" /><path
|
||||
style="opacity:1;fill:#ffffff;fill-opacity:1;stroke:#e89493;stroke-width:2.8;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
||||
d="m 300.64789,328.46849 6.86242,18.17612"
|
||||
id="path54" /><path
|
||||
style="opacity:1;fill:#ffffff;fill-opacity:1;stroke:#e89493;stroke-width:2.8;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
||||
d="m 312.70348,329.21037 6.67695,17.0633"
|
||||
id="path55" /><path
|
||||
style="opacity:1;fill:#ffffff;fill-opacity:1;stroke:#e89493;stroke-width:2.8;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
||||
d="m 326.24284,328.65396 7.04788,17.99065"
|
||||
id="path56" /><path
|
||||
style="opacity:1;fill:none;fill-opacity:1;stroke:#e49589;stroke-width:4.6;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
||||
d="m 218.4918,348.98361 c -1.87307,1.30472 -3.33608,3.11401 -4.70333,4.91603 -1.02497,1.35089 -2.00187,2.75003 -2.77208,4.2643"
|
||||
id="path61"
|
||||
inkscape:path-effect="#path-effect61"
|
||||
inkscape:original-d="m 218.4918,348.98361 c -1.70592,0.99645 -5.82749,5.88449 -7.47541,9.18033"
|
||||
transform="rotate(-3.3120949,219.55256,344.63259)" /><path
|
||||
style="opacity:1;fill:none;fill-opacity:1;stroke:#4c4f59;stroke-width:3.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
||||
d="m 127.47541,375.08197 c 0,0 -14.42623,-19.14754 -12.06557,-59.54099"
|
||||
id="path92" /><path
|
||||
style="opacity:1;fill:#ffffff;fill-opacity:0.734285;stroke:#c1545a;stroke-width:7.8;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
||||
d="m 136.39344,322.09836 c 0,0 9.44263,-18.88525 -4.19672,-39.86885 -3.14754,-0.52459 27.80328,-27.80328 5.2459,-77.11476 -1.57377,0.52459 23.60656,-55.60655 -24.13115,-82.88524 -4.19672,-8.39344 -45.639336,-69.245904 -89.180316,13.11475 0,11.54099 -1.04918,36.19672 -1.04918,36.19672 0,0 -14.6885299,38.81968 -6.29509,71.86886 2.09837,3.14754 -20.9835999,49.83606 11.0164,106.4918 0.52459,0.52459 -16.78689,57.70492 12.59016,82.88525 1.04918,4.72131 13.63935,33.57377 42.49181,39.34426 12.59016,0.52459 19.409826,-0.52459 19.409826,-0.52459 0,0 31.47541,-4.72131 47.73771,-67.14754 -2.09836,-27.80328 -4.19672,-50.36066 -11.0164,-70.81968"
|
||||
id="path93" /><path
|
||||
id="path101"
|
||||
style="opacity:1;fill:#ffffff;fill-opacity:0.734285;stroke:#000000;stroke-width:9;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
||||
d="m 109.69769,219.48859 c 0,0 -15.208592,-11.87012 -21.143652,-19.65989 m -37.09413,20.03083 22.25648,-21.88553 m 0.37094,-14.09577 5.93506,41.91636 m -35.23942,-24.11118 56.012132,-4.82224 m 11.12824,-17.43424 -19.288952,0.37094 m -0.37094,-0.74188 -1.48376,-29.6753 m -38.94883,3.33847 37.836,-2.96753 m -24.85306,-11.49918 0.74188,39.69072"
|
||||
inkscape:label="杂" /><path
|
||||
id="path111"
|
||||
style="opacity:1;fill:#ffffff;fill-opacity:0.734285;stroke:#000000;stroke-width:9;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
||||
d="m 58.507798,352.28556 54.157422,-3.33847 m -46.367662,-13.72483 35.610372,-2.96753 m -21.514602,-32.27189 2.59659,33.38472 m -18.918,-16.32142 37.094122,-3.33847 m 1.48377,18.54706 0.74188,-33.75565 m -0.37094,0.37094 -40.803542,2.22565 m 0,-0.37094 4.4513,33.38471 m 27.44965,-48.22237 3.33847,-14.4667 m 0,-1.48377 -27.44965,1.48377 m -1.48377,14.83765 0.74188,-20.03083"
|
||||
inkscape:label="鱼" /><path
|
||||
d="m 69.453989,376.46984 c -4.220665,0.22086 -8.079406,1.8518 -10.783191,4.54882 -2.980547,2.98155 -4.241957,7.10139 -3.534076,11.54824 0.904812,5.67433 4.763552,11.7394 11.384623,17.90216 1.405115,1.30393 2.39508,2.14912 4.390984,3.74184 3.507462,2.79468 6.679618,5.01174 11.04931,7.70876 1.559468,0.96838 4.635815,2.76496 5.796096,3.38931 l 0.335314,0.18263 0.48966,-0.26758 c 1.783005,-0.98111 4.960482,-2.86264 6.844609,-4.05189 7.126702,-4.49782 12.832312,-9.01266 17.063632,-13.50625 6.86057,-7.28827 9.77725,-14.38543 8.43068,-20.49298 -0.93142,-4.20475 -4.04502,-7.68327 -8.46261,-9.45012 -1.90011,-0.76026 -3.74699,-1.15525 -5.83869,-1.24869 -2.7091,-0.12316 -5.37562,0.33977 -7.91973,1.37185 -4.332435,1.75413 -7.951675,5.17315 -10.229661,9.66674 -0.159681,0.3143 -0.308701,0.59038 -0.329985,0.60737 -0.0692,0.0553 -0.143707,-0.051 -0.431122,-0.62859 -0.681263,-1.35913 -2.01719,-3.3341 -3.065699,-4.52331 -1.07513,-1.22747 -2.804909,-2.72251 -4.092931,-3.54224 -3.326503,-2.11937 -7.222503,-3.15569 -11.097213,-2.95607 z"
|
||||
id="path1-4"
|
||||
style="fill:#d96477;fill-opacity:1;stroke:none;stroke-width:0.00475452" /></g></svg>
|
||||
|
After Width: | Height: | Size: 60 KiB |
80
Website/docs/public/offline.html
Normal file
@@ -0,0 +1,80 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>Offline | SukiSU-Ultra</title>
|
||||
<meta name="theme-color" content="#64edff" />
|
||||
<style>
|
||||
html,
|
||||
body {
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
font-family:
|
||||
system-ui,
|
||||
-apple-system,
|
||||
Segoe UI,
|
||||
Roboto,
|
||||
Ubuntu,
|
||||
Cantarell,
|
||||
'Noto Sans',
|
||||
sans-serif;
|
||||
}
|
||||
.wrap {
|
||||
min-height: 100%;
|
||||
display: grid;
|
||||
place-items: center;
|
||||
padding: 2rem;
|
||||
}
|
||||
.card {
|
||||
max-width: 560px;
|
||||
background: #fff;
|
||||
color: #111;
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 6px 30px rgba(0, 0, 0, 0.06);
|
||||
padding: 24px;
|
||||
}
|
||||
.card h1 {
|
||||
margin: 0 0 8px;
|
||||
font-size: 1.4rem;
|
||||
}
|
||||
.card p {
|
||||
margin: 0.5rem 0;
|
||||
line-height: 1.5;
|
||||
}
|
||||
.muted {
|
||||
color: #666;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
.actions {
|
||||
margin-top: 1rem;
|
||||
display: flex;
|
||||
gap: 0.75rem;
|
||||
}
|
||||
.btn {
|
||||
padding: 0.6rem 0.9rem;
|
||||
border-radius: 8px;
|
||||
border: 1px solid #d0d0d0;
|
||||
background: #f7f7f7;
|
||||
color: #111;
|
||||
text-decoration: none;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="wrap">
|
||||
<div class="card">
|
||||
<h1>You're offline</h1>
|
||||
<p>We couldn't load this page because your device has no internet connection.</p>
|
||||
<p class="muted">
|
||||
When you're back online, try again. Some pages you visited before may still work from
|
||||
cache.
|
||||
</p>
|
||||
<div class="actions">
|
||||
<a class="btn" href="/">Go to home</a>
|
||||
<a class="btn" href="/guide/">Docs guide</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
76
Website/docs/public/robots.txt
Normal file
@@ -0,0 +1,76 @@
|
||||
User-agent: *
|
||||
Allow: /
|
||||
Disallow: /admin/
|
||||
Disallow: /.git/
|
||||
Disallow: /node_modules/
|
||||
Disallow: /api/
|
||||
Disallow: /.vitepress/
|
||||
|
||||
# Sitemap
|
||||
Sitemap: https://sukisu.org/sitemap.xml
|
||||
|
||||
# Crawl-delay for high-traffic optimization
|
||||
Crawl-delay: 0.5
|
||||
|
||||
# Major search engines (global optimization)
|
||||
User-agent: Googlebot
|
||||
Allow: /
|
||||
Crawl-delay: 0.5
|
||||
|
||||
User-agent: Bingbot
|
||||
Allow: /
|
||||
Crawl-delay: 1
|
||||
|
||||
User-agent: Slurp
|
||||
Allow: /
|
||||
Crawl-delay: 1
|
||||
|
||||
User-agent: DuckDuckBot
|
||||
Allow: /
|
||||
Crawl-delay: 0.5
|
||||
|
||||
User-agent: Baiduspider
|
||||
Allow: /
|
||||
Crawl-delay: 2
|
||||
|
||||
# Asian search engines (for China, Japan, etc.)
|
||||
User-agent: YandexBot
|
||||
Allow: /
|
||||
Crawl-delay: 1
|
||||
|
||||
User-agent: NaverBot
|
||||
Allow: /
|
||||
Crawl-delay: 1
|
||||
|
||||
User-agent: SogouSpider
|
||||
Allow: /
|
||||
Crawl-delay: 2
|
||||
|
||||
# Block resource-intensive bots for performance
|
||||
User-agent: AhrefsBot
|
||||
Disallow: /
|
||||
|
||||
User-agent: MJ12bot
|
||||
Disallow: /
|
||||
|
||||
User-agent: SemrushBot
|
||||
Disallow: /
|
||||
|
||||
User-agent: DotBot
|
||||
Disallow: /
|
||||
|
||||
# Block AI training crawlers to save bandwidth
|
||||
User-agent: GPTBot
|
||||
Disallow: /
|
||||
|
||||
User-agent: ChatGPT-User
|
||||
Disallow: /
|
||||
|
||||
User-agent: CCBot
|
||||
Disallow: /
|
||||
|
||||
User-agent: anthropic-ai
|
||||
Disallow: /
|
||||
|
||||
User-agent: Claude-Web
|
||||
Disallow: /
|
||||
535
Website/docs/public/safari-pinned-tab.svg
Normal file
@@ -0,0 +1,535 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
width="512"
|
||||
height="512"
|
||||
viewBox="0 0 512 512"
|
||||
version="1.1"
|
||||
id="svg1"
|
||||
xml:space="preserve"
|
||||
inkscape:version="1.4 (e7c3feb100, 2024-10-09)"
|
||||
sodipodi:docname="zako.optizmism.svg"
|
||||
inkscape:export-filename="zako.plain.svg"
|
||||
inkscape:export-xdpi="96"
|
||||
inkscape:export-ydpi="96"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"><sodipodi:namedview
|
||||
id="namedview1"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#999999"
|
||||
borderopacity="1"
|
||||
inkscape:showpageshadow="2"
|
||||
inkscape:pageopacity="1"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#d1d1d1"
|
||||
inkscape:document-units="px"
|
||||
showguides="false"
|
||||
inkscape:zoom="2"
|
||||
inkscape:cx="219"
|
||||
inkscape:cy="370"
|
||||
inkscape:window-width="1280"
|
||||
inkscape:window-height="702"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="false"><inkscape:page
|
||||
x="0"
|
||||
y="0"
|
||||
width="512"
|
||||
height="512"
|
||||
id="page2"
|
||||
margin="0"
|
||||
bleed="0" /></sodipodi:namedview><defs
|
||||
id="defs1"><inkscape:path-effect
|
||||
effect="simplify"
|
||||
id="path-effect79"
|
||||
is_visible="true"
|
||||
lpeversion="1.3"
|
||||
threshold="32.467532"
|
||||
steps="1"
|
||||
smooth_angles="360"
|
||||
helper_size="0"
|
||||
simplify_individual_paths="false"
|
||||
simplify_just_coalesce="false" /><inkscape:path-effect
|
||||
effect="simplify"
|
||||
id="path-effect78"
|
||||
is_visible="true"
|
||||
lpeversion="1.3"
|
||||
threshold="32.467532"
|
||||
steps="1"
|
||||
smooth_angles="360"
|
||||
helper_size="0"
|
||||
simplify_individual_paths="false"
|
||||
simplify_just_coalesce="false" /><inkscape:path-effect
|
||||
effect="simplify"
|
||||
id="path-effect77"
|
||||
is_visible="true"
|
||||
lpeversion="1.3"
|
||||
threshold="32.467532"
|
||||
steps="1"
|
||||
smooth_angles="360"
|
||||
helper_size="0"
|
||||
simplify_individual_paths="false"
|
||||
simplify_just_coalesce="false" /><inkscape:path-effect
|
||||
effect="simplify"
|
||||
id="path-effect76"
|
||||
is_visible="true"
|
||||
lpeversion="1.3"
|
||||
threshold="32.467532"
|
||||
steps="1"
|
||||
smooth_angles="360"
|
||||
helper_size="0"
|
||||
simplify_individual_paths="false"
|
||||
simplify_just_coalesce="false" /><inkscape:path-effect
|
||||
effect="simplify"
|
||||
id="path-effect75"
|
||||
is_visible="true"
|
||||
lpeversion="1.3"
|
||||
threshold="32.467532"
|
||||
steps="1"
|
||||
smooth_angles="360"
|
||||
helper_size="0"
|
||||
simplify_individual_paths="false"
|
||||
simplify_just_coalesce="false" /><inkscape:path-effect
|
||||
effect="simplify"
|
||||
id="path-effect61"
|
||||
is_visible="true"
|
||||
lpeversion="1.3"
|
||||
threshold="32.467532"
|
||||
steps="1"
|
||||
smooth_angles="360"
|
||||
helper_size="0"
|
||||
simplify_individual_paths="false"
|
||||
simplify_just_coalesce="false" /><inkscape:path-effect
|
||||
effect="simplify"
|
||||
id="path-effect42"
|
||||
is_visible="true"
|
||||
lpeversion="1.3"
|
||||
threshold="32.467532"
|
||||
steps="1"
|
||||
smooth_angles="360"
|
||||
helper_size="0"
|
||||
simplify_individual_paths="false"
|
||||
simplify_just_coalesce="false" /></defs><g
|
||||
inkscape:label="main"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"><path
|
||||
style="display:inline;opacity:1;fill:#fff9f6;fill-opacity:1;stroke:#4c4f59;stroke-width:6.5;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
||||
d="m 164.23888,424.64081 -31.07097,26.05282 c 0,0 -5.19317,18.17613 25.96589,28.93342 31.15907,10.7573 213.29123,1.48377 213.29123,1.48377 0,0 52.30272,11.87012 80.86519,-37.09413 -0.37094,0.74188 -8.90259,19.28895 -8.90259,19.28895 l 1.48377,19.28894 c 0,0 57.73516,-9.41425 30.45647,-148.95524 4.19672,-9.44262 8.39344,-15.21311 8.39344,-15.21311 l -3.14754,-21.77049 c 0,0 5.65758,-40.33533 7.88323,-45.52851 2.22564,-5.19317 9.64447,-11.12823 9.64447,-11.12823 0,0 -2.78206,-20.40177 -6.67694,-26.15136 -0.74189,-3.33847 6.86241,-35.05395 2.04017,-58.05231 -7.04788,-11.87012 -4.82223,-11.87012 -18.918,-25.59495 0,0.37094 8.90259,-44.88389 -0.74188,-73.446367 -2.96753,0 -32.27189,-10.757296 -96.81567,23.740241 -0.37094,1.112824 -26.33683,-30.046242 -153.56969,-15.950474 -0.74188,0 -43.40012,-37.836009 -66.76942,-40.432598 -1.11283,-0.370941 -22.62742,-4.080354 -25.22401,48.964247 0,1.112824 -44.14201,15.950474 -56.01213,64.914721 -11.87012,48.96425 -11.128238,80.86519 -11.128238,80.86519 0,0 -18.904912,27.6632 -9.798121,25.03308 5.346826,-1.54421 4.975885,23.56023 4.975885,23.56023 l -2.225648,39.31977 c 0,0 -14.466709,35.23942 -14.83765,59.72155 -0.370942,24.48212 -0.741883,34.12659 4.822236,48.5933 5.564119,14.46671 20.772711,46.7386 42.658245,63.06002 8.902591,0.37094 18.918001,1.8547 18.918001,1.8547 l 3.70942,-20.77271 c 0,0 18.918,15.95048 32.27189,14.83765 13.35388,-1.11282 -0.74189,-0.74188 -0.74189,-0.74188 l -13.72482,-17.80518 z"
|
||||
id="path2"
|
||||
sodipodi:nodetypes="ccscccccccscccccccccscsccsscccsccc"
|
||||
inkscape:label="整个人的边框" /><path
|
||||
id="path91"
|
||||
style="opacity:1;fill:#93d4fa;fill-opacity:1;stroke:#4c4f59;stroke-width:6.9;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
||||
d="m 133.91016,110.16992 -69.738285,47.48047 18.263976,30.75433 -31.617492,55.67536 28.5625,17.0625 14.837891,-1.48438 2.226562,-55.64062 2.577791,-4.39109 18.193697,-21.94485 23.74023,-49.70703 z"
|
||||
sodipodi:nodetypes="ccccccccccc" /><path
|
||||
style="display:inline;opacity:1;fill:#ace0fe;fill-opacity:1;stroke:#4c4f59;stroke-width:6.54357;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
||||
d="m 217.01397,405.82758 c 0,0 7.41728,-25.94462 41.16595,-27.07265 33.74866,-1.12802 53.77534,4.1361 53.77534,4.1361 0,0 11.12594,6.39215 17.80149,14.66435 6.67557,8.2722 28.92743,40.60895 41.53682,48.88115 12.60939,8.27219 15.20545,15.41636 15.20545,15.41636 l -1.85433,11.28027 c 0,0 -14.83458,10.15224 -30.78175,11.65627 -15.94717,1.50404 -147.60404,0 -147.60404,0 l -30.04002,-3.00807"
|
||||
id="path71"
|
||||
inkscape:label="衣服底色" /><path
|
||||
style="fill:#ffffff;fill-opacity:1;stroke-width:1.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.3;paint-order:fill markers stroke"
|
||||
d="m 363.54127,444.68178 c 0,0 -6.89429,4.06586 -4.86135,6.8059 2.03293,2.74004 5.92201,3.44715 5.92201,3.44715 0,0 4.5962,0.26516 5.12653,-3.53554 0.53033,-3.8007 -3.00521,-6.8059 -3.00521,-6.8059"
|
||||
id="path60" /><path
|
||||
style="fill:#ffffff;fill-opacity:1;stroke-width:1.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.3;paint-order:fill markers stroke"
|
||||
d="m 349.67678,459.82583 c 0,0 4.5,-7.25 7,-5.5 2.5,1.75 -4.25,7.25 -4.25,7.25"
|
||||
id="path59" /><path
|
||||
id="path73-8"
|
||||
style="fill:#ffffff;stroke-width:8.7;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.3;paint-order:fill markers stroke"
|
||||
d="m 228.96985,465.10941 a 8.0341015,5.6995392 0 0 1 -8.0341,5.69953 8.0341015,5.6995392 0 0 1 -8.03411,-5.69953 8.0341015,5.6995392 0 0 1 8.03411,-5.69954 8.0341015,5.6995392 0 0 1 8.0341,5.69954 z" /><path
|
||||
style="opacity:1;fill:none;fill-opacity:1;stroke:#6c9cb2;stroke-width:3;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
||||
d="m 220.85246,408.91803 c -0.22678,0.7374 0.77024,1.47887 0.20184,0.3709 -0.55918,-1.08999 0.45864,1.0858 0.58075,1.39596 2.10089,5.33666 1.96407,11.16153 3.60742,16.6426 0.30205,1.00744 0.58829,2.039 0.5936,3.09874"
|
||||
id="path75"
|
||||
inkscape:path-effect="#path-effect75"
|
||||
inkscape:original-d="m 220.85246,408.91803 c 0.0874,0.34973 -0.0874,1.13662 0.26229,1.04918 0.34973,-0.0874 -0.42351,-1.37161 -0.26229,-1.04918 2.70315,5.4063 2.32215,10.60009 3.67213,16 0.44415,1.77662 1.31148,3.74741 1.31148,5.5082" /><path
|
||||
style="opacity:1;fill:none;fill-opacity:1;stroke:#6c9cb2;stroke-width:6.9;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
||||
d="m 225.57377,436.98361 c -1.22213,4.37272 0.0774,9.22771 2.41807,12.85195 2.34183,3.626 0.85866,8.30929 2.56554,12.06608 1.71568,3.77616 2.21068,8.39349 -0.30117,12.16042 -1.9802,2.96963 -5.23922,5.13567 -6.38674,8.61546 0,0 -0.0736,0.66095 -0.0736,0.66095"
|
||||
id="path76"
|
||||
inkscape:path-effect="#path-effect76"
|
||||
inkscape:original-d="m 225.57377,436.98361 c -1.27879,3.14891 0.64852,9.42818 3.40984,14.95082 0.92316,1.84632 0.76302,8.34571 1.57377,9.96721 1.05661,2.11323 2.18032,6.57378 1.31147,9.18033 -1.55473,4.66419 -8.07296,8.77008 -8.07296,12.2565"
|
||||
sodipodi:nodetypes="csssc" /><path
|
||||
style="opacity:1;fill:#fbf3ef;fill-opacity:1;stroke:none;stroke-width:5.3;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
||||
d="m 259.14754,381.90164 c 0,0 8.91803,15.47541 9.18033,20.98361 0.26229,5.50819 32.26229,-1.83607 32.26229,-1.83607 0,0 -4.45901,-16.78688 -7.34426,-19.40983 -2.88524,-2.62296 -34.09836,0.26229 -34.09836,0.26229 z"
|
||||
id="path89" /><path
|
||||
style="opacity:1;fill:none;fill-opacity:1;stroke:#6c9cb2;stroke-width:4.6;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
||||
d="m 247.86885,386.36066 c 0.62439,0.76152 1.31219,1.32242 0.35251,0.10604 -0.98535,-1.24891 -0.63523,-0.56343 0.0935,0.24171 1.40155,1.54849 2.30926,3.40606 3.10338,5.34166 1.48771,3.62621 2.15649,7.53104 4.02075,11.02472 1.24886,2.3404 2.74915,4.53757 3.97084,6.89242"
|
||||
id="path77"
|
||||
inkscape:path-effect="#path-effect77"
|
||||
inkscape:original-d="m 247.86885,386.36066 c 3.34102,3.56326 -3.68025,-4.79705 1.83607,2.09836 1.23825,1.54782 3.18087,7.73986 3.67213,9.70492 1.00623,4.02493 4.53306,8.80383 6.03279,11.80327" /><path
|
||||
style="opacity:1;fill:none;fill-opacity:1;stroke:#6c9cb2;stroke-width:4.6;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
||||
d="m 306.62295,386.88525 c 1.62958,6.07854 4.04229,11.93568 6.81967,17.57377"
|
||||
id="path78"
|
||||
inkscape:path-effect="#path-effect78"
|
||||
inkscape:original-d="m 306.62295,386.88525 c 1.57569,6.03983 4.44925,12.83291 6.81967,17.57377" /><path
|
||||
style="opacity:1;fill:none;fill-opacity:1;stroke:#6c9cb2;stroke-width:5.3;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
||||
d="m 335.73771,431.21312 c 0.7696,3.76393 3.24862,7.26774 5.48726,10.89168 2.09754,3.39553 3.33574,7.13199 4.87926,10.7955 2.06975,4.91248 6.75146,8.7424 7.699,13.95386 0.92029,5.06165 5.24206,8.89669 5.27874,14.19502"
|
||||
id="path79"
|
||||
inkscape:path-effect="#path-effect79"
|
||||
inkscape:original-d="m 335.73771,431.21312 c -1.15649,1.03916 3.66917,4.97179 4.45901,8.13114 0.96754,3.87016 3.8848,6.88344 4.72131,10.22951 1.09547,4.38186 5.88097,12.49359 8.39345,16.2623 0.80744,1.21116 0.89968,3.11084 1.57377,4.45901 1.7539,3.50782 4.19672,6.9619 4.19672,10.7541" /><path
|
||||
style="opacity:1;fill:none;fill-opacity:1;stroke:#494d55;stroke-width:5.3;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
||||
d="m 258.62295,384.2623 c 0,0 8.13115,6.81967 11.27869,27.01639"
|
||||
id="path80" /><path
|
||||
style="opacity:1;fill:#fbf3ef;fill-opacity:1;stroke:#4c4f59;stroke-width:6.9;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
||||
d="m 127.23285,329.0249 c 0,0 -2.59659,60.46343 13.72483,75.30108 8.16071,-8.16071 10.01541,-10.38636 10.01541,-10.38636 l 12.24107,8.53165 5.19317,-4.45129 2.22565,-19.65989 5.19318,-10.01542 5.93506,-12.24106 -8.53165,-8.53165 -15.95047,-19.28894 -3.33848,-4.82224 -4.45129,9.27353 c 0,0 -12.24106,0 -15.95048,-2.59659 -3.70941,-2.59658 -6.306,-1.11282 -6.306,-1.11282 z"
|
||||
id="path90" /><path
|
||||
style="opacity:1;fill:none;fill-opacity:1;stroke:#494d55;stroke-width:5.3;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
||||
d="m 291.67213,381.37705 c 0,0 7.08197,6.55738 10.22951,26.4918"
|
||||
id="path81" /><path
|
||||
style="opacity:1;fill:none;fill-opacity:1;stroke:#494d55;stroke-width:5.3;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
||||
d="m 270.16393,404.19672 c 0,0 16.00001,-3.93442 27.80328,-2.62295"
|
||||
id="path82" /><path
|
||||
style="opacity:1;fill:none;fill-opacity:1;stroke:#494d55;stroke-width:5.3;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
||||
d="m 253.90164,417.83607 c 0,0 37.2459,-13.11476 69.77049,-9.18033 2.09836,3.67213 5.77049,16.78688 -5.77049,20.98361 -4.72131,0 -19.14754,-1.57378 -22.55738,-0.5246 -3.40983,1.04919 -12.59016,0.5246 -18.36065,4.45902 -5.77049,3.93443 -14.68853,8.91803 -18.36066,3.14754 -3.67213,-5.77049 -4.72131,-12.32787 -4.72131,-12.32787 z"
|
||||
id="path83" /><path
|
||||
style="opacity:1;fill:none;fill-opacity:1;stroke:#494d55;stroke-width:5.3;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
||||
d="m 276.45902,482.62295 c 0,0 -9.70492,-1.57377 1.04918,-46.42623"
|
||||
id="path84" /><path
|
||||
style="opacity:1;fill:#fcf6fa;fill-opacity:1;stroke:#494d55;stroke-width:5.3;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
||||
d="m 287.21312,432.78689 6.29508,50.88524 13.90164,-0.78688 -6.81968,-54.03279 z"
|
||||
id="path88" /><path
|
||||
style="opacity:1;fill:none;fill-opacity:1;stroke:#494d55;stroke-width:5.3;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
||||
d="m 287.73771,430.95082 5.77049,50.88525"
|
||||
id="path85" /><path
|
||||
style="opacity:1;fill:none;fill-opacity:1;stroke:#494d55;stroke-width:5.3;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
||||
d="m 300.32787,428.06557 c 0,0 2.09836,37.7705 7.34426,52.98361"
|
||||
id="path86" /><path
|
||||
style="opacity:1;fill:none;fill-opacity:1;stroke:#494d55;stroke-width:5.3;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
||||
d="m 313.18033,431.21312 c 0,0 7.60656,27.54098 10.22951,35.93442 2.62295,8.39344 -8.13115,15.73771 -8.13115,15.73771"
|
||||
id="path87" /><path
|
||||
style="opacity:1;fill:#bce4fd;fill-opacity:1;stroke:none;stroke-width:8.7;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
||||
d="m 164.23888,424.64081 -31.07097,26.05282 5.00771,13.72483 20.95818,15.20859 17.08508,2.15431 -11.1287,-20.55238 -1.54637,-22.78395 z"
|
||||
id="path74" /><path
|
||||
id="path73"
|
||||
style="opacity:1;fill:#ffffff;stroke-width:8.7;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.3;paint-order:fill markers stroke"
|
||||
d="m 163.81755,451.10144 a 8.0341015,5.6995392 0 0 1 -8.0341,5.69954 8.0341015,5.6995392 0 0 1 -8.0341,-5.69954 8.0341015,5.6995392 0 0 1 8.0341,-5.69954 8.0341015,5.6995392 0 0 1 8.0341,5.69954 z" /><path
|
||||
style="opacity:1;fill:#fff9f6;fill-opacity:1;stroke:none;stroke-width:8.7;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
||||
d="m 338.36066,376.13115 c 0,0 1.83605,30.95081 -6.29509,51.40983 2.88525,1.04918 14.48107,0.26606 26.91267,-7.34238 12.4316,-7.60844 11.38242,-22.03467 11.38242,-22.03467 l 2.09836,28.85246 c 0,0 24.31867,-28.09873 24.2249,-52.60674 -0.0938,-24.50801 -1.66753,-26.08178 -1.66753,-26.08178 l -11.83407,-1.12685 z"
|
||||
id="path72" /><path
|
||||
style="opacity:1;fill:#fde9e7;fill-opacity:1;stroke:none;stroke-width:2.845;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
||||
d="m 339.7822,389.85927 c 0,0 38.20695,-7.78977 56.38307,-25.96589 5.56412,8.53165 8.16071,32.64283 8.16071,32.64283 l 2.59659,12.98294 16.32141,-62.68907 -6.67694,-5.19318 -5.93506,-17.43424 c 0,0 -15.20859,15.2086 -27.44966,22.99836 -12.24106,7.78977 -34.12659,19.28895 -34.12659,19.28895 l -10.7573,3.70941 z"
|
||||
id="path70" /><path
|
||||
style="opacity:1;fill:none;fill-opacity:1;stroke:#c3b4b0;stroke-width:2.845;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
||||
d="m 347.01555,369.64297 c 0,0 2.22565,10.01541 -1.8547,19.84536 -4.08036,9.82994 -5.00771,9.459 -5.00771,9.459"
|
||||
id="path69" /><path
|
||||
style="opacity:1;fill:#fde9e7;fill-opacity:1;stroke:none;stroke-width:6.945;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
||||
d="m 380.85246,480 c 0,0 44.59016,-17.31147 59.27869,-87.08197 6.81967,-2.62295 34.09836,-14.68852 39.34426,-25.70491 2.62295,-9.96722 -3.67213,-32 -3.67213,-32 l -24.65574,0.52459 -26.7541,4.72131 -4.72131,11.54098 -8.39344,52.45902 -13.11475,28.32787 -15.73771,36.19672 z"
|
||||
id="path68" /><path
|
||||
style="fill:none;fill-opacity:1;stroke:#4c4f59;stroke-width:8.74203;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
||||
d="m 147.6556,277.03379 c 0,0 -11.12655,104.13607 127.58449,103.38689 138.71104,-0.74917 149.83759,-100.39017 149.83759,-100.39017"
|
||||
id="path41"
|
||||
inkscape:label="脸蛋" /><path
|
||||
style="opacity:1;fill:#fff9f6;fill-opacity:1;stroke:none;stroke-width:6.5;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
||||
d="m 123.27869,147.93443 304.78688,21.50819 -24.65573,-40.39344 c 0,0 44.06557,-58.754098 50.88524,-60.327868 6.81967,-1.573771 15.73771,-9.967214 15.73771,-9.967214 l -13.11476,-3.672131 -77.11475,22.032787 c 0,0 -122.7541,-33.57377 -168.39344,-6.295082 -45.63935,27.278689 -69.7705,46.163938 -69.7705,46.163938 z"
|
||||
id="path33"
|
||||
inkscape:label="头发底色2" /><path
|
||||
style="fill:#fde9e7;fill-opacity:1;stroke:none;stroke-width:6.5;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
||||
d="m 331.44501,66.651771 16.22882,30.84274 47.82991,31.367399 6.30629,1.07795 26.88325,-35.335399 -32.10312,-22.211018 -18.88524,5.245901 -30.42623,-11.540983 z"
|
||||
id="path35"
|
||||
inkscape:label="头发部件111" /><path
|
||||
style="opacity:1;fill:#ace0fe;fill-opacity:1;stroke:none;stroke-width:14.5;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
||||
d="m 431.03375,175.82616 17.06329,-17.0633 50.44802,71.96261 -5.93506,17.80518 -33.38472,18.54706 -12.612,-16.32142 17.80518,-11.12823 z"
|
||||
id="path29"
|
||||
inkscape:label="蓝色发卡上" /><path
|
||||
style="opacity:1;fill:#ace0fe;fill-opacity:1;stroke:#4c4f59;stroke-width:6.945;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
||||
d="m 465.90223,261.88453 c 0,0 35.61036,54.52837 2.96753,73.44637 -25.22401,4.08036 -48.22237,2.22565 -48.22237,2.22565 l 10.38636,-26.33683 21.51459,-41.91636 z"
|
||||
id="path66"
|
||||
inkscape:label="蓝色发卡下" /><path
|
||||
style="opacity:1;fill:#febdc7;fill-opacity:1;stroke:none;stroke-width:4.6;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
||||
d="m 471.86885,57.704918 c 0,0 17.04918,68.983602 -11.01639,110.426232 -7.34426,0.26229 -9.18033,-10.49181 -9.18033,-10.49181 l 2.09836,-6.55737 -1.04918,-9.18033 2.36066,-10.7541 1.83606,-5.5082 -6.29508,-6.03278 -6.55738,-5.2459 -2.88524,-5.24591 2.88524,-5.2459 -2.62295,-5.245899 -4.19672,-2.885246 -3.14754,-1.57377 5.2459,-11.540984 11.80328,-7.344262 z"
|
||||
id="path64" /><path
|
||||
id="path57"
|
||||
style="opacity:1;fill:#ff1c1c;fill-opacity:0.0509804;stroke-width:3.92397;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.3;paint-order:fill markers stroke"
|
||||
d="m 244.26691,326.81357 a 35.587568,21.480417 0 0 1 -35.58757,21.48042 35.587568,21.480417 0 0 1 -35.58757,-21.48042 35.587568,21.480417 0 0 1 35.58757,-21.48042 35.587568,21.480417 0 0 1 35.58757,21.48042 z" /><path
|
||||
id="path57-1"
|
||||
style="fill:#ff1c1c;fill-opacity:0.05044398;stroke-width:3.65169;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.3;paint-order:fill markers stroke"
|
||||
d="m 355.20429,336.75188 a 31.611719,20.942554 0 0 1 -31.61171,20.94255 31.611719,20.942554 0 0 1 -31.61172,-20.94255 31.611719,20.942554 0 0 1 31.61172,-20.94255 31.611719,20.942554 0 0 1 31.61171,20.94255 z" /><path
|
||||
style="display:inline;opacity:1;fill:#fbf3ef;fill-opacity:1;stroke:#4c4f59;stroke-width:6.5;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
||||
d="m 188.32787,343.60656 c 0,0 12.06557,-28.32787 15.7377,6.29508 5.24591,-8.91803 9.96722,-17.31148 17.31148,-8.39344 6.81967,2.09836 15.21311,7.34426 4.72131,22.03278 -10.4918,14.68853 16.33814,-18.37136 15.445,-1.63289 -0.72099,13.51206 -24.52214,46.81129 -24.52214,46.81129 0,0 -6.33782,64.18547 -33.41466,70.23144 -36.5485,8.16089 -12.06558,-103.86885 -12.06558,-103.86885 z"
|
||||
id="path1"
|
||||
sodipodi:nodetypes="cccsscscc"
|
||||
inkscape:label="右手" /><path
|
||||
id="path5"
|
||||
style="fill:#fbe7e5;stroke-width:10.4;stroke-linecap:round;stroke-miterlimit:1.3;paint-order:fill markers stroke;fill-opacity:0.40581462"
|
||||
inkscape:label="右手踝阴影"
|
||||
d="m 193.63133,383.92419 a 12.612,12.241061 0 0 1 -12.612,12.24107 12.612,12.241061 0 0 1 -12.612,-12.24107 12.612,12.241061 0 0 1 12.612,-12.24106 12.612,12.241061 0 0 1 12.612,12.24106 z" /><path
|
||||
style="fill:#fee4e0;fill-opacity:0.42411327;stroke:none;stroke-width:10.4;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
||||
d="m 179.09741,356.52608 14.09576,-20.77271 7.04789,1.48377 0.74188,13.72482 11.49918,-11.49918 9.27353,4.08036 8.53165,5.93506 -5.56412,14.83765 13.72483,-4.82224 -3.33847,17.80518 -47.85143,-27.44965 z"
|
||||
id="path9"
|
||||
inkscape:label="右手手指阴影" /><path
|
||||
style="display:inline;fill:none;fill-opacity:1;stroke:#4c4f59;stroke-width:6.5;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
||||
d="m 188.32787,343.60656 c 0,0 12.06557,-28.32787 15.7377,6.29508 5.24591,-8.91803 9.96722,-17.31148 17.31148,-8.39344 6.81967,2.09836 15.21311,7.34426 4.72131,22.03278 -10.4918,14.68853 16.33814,-18.37136 15.445,-1.63289 -0.72099,13.51206 -24.52214,46.81129 -24.52214,46.81129 0,0 -6.33782,64.18547 -33.41466,70.23144 -36.5485,8.16089 -12.06558,-103.86885 -12.06558,-103.86885 z"
|
||||
id="path1-6"
|
||||
sodipodi:nodetypes="cccsscscc"
|
||||
inkscape:label="右手遮挡" /><path
|
||||
style="opacity:1;fill:#fff9f6;fill-opacity:1;stroke:#4c4f59;stroke-width:6.5;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
||||
d="m 137.44262,123.2787 c 0,0 49.33852,-48.649769 86.97684,-58.734919 0.10513,0.76769 -120.16719,-122.15824 -86.97684,58.734919 z"
|
||||
id="path3"
|
||||
inkscape:label="头发部件19 左猫耳" /><path
|
||||
style="fill:#fde9e7;fill-opacity:1;stroke-width:1.18016;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.3;paint-order:fill markers stroke"
|
||||
d="m 198.85246,42 -63.6342,39.016573 3.84123,40.939237 L 184.82787,87 224,64.5 Z"
|
||||
id="path58"
|
||||
sodipodi:nodetypes="cccccc" /><path
|
||||
style="opacity:1;fill:#1a1a1a;fill-opacity:0.465732;stroke:#a18f90;stroke-width:6.5;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
||||
id="path4"
|
||||
d="m 404.96447,131.7729 c 0.52512,-0.76931 1.0357,-1.54875 1.57536,-2.30793 2.82418,-3.97291 5.82035,-7.82036 8.67758,-11.76948 2.39309,-3.30762 3.93978,-5.54326 6.29133,-8.89998 5.32612,-7.58044 11.18023,-14.760796 17.20894,-21.78699 3.02173,-3.55064 6.06655,-7.098864 9.47662,-10.289082 1.96198,-1.835492 4.21906,-3.672787 6.27415,-5.392122 5.10823,-4.357455 10.66931,-8.109574 16.22169,-11.862072 0,0 -2.92504,-1.864176 -2.92504,-1.864176 v 0 c -5.41592,3.900149 -10.90348,7.705144 -15.99184,12.040062 -1.99887,1.645368 -4.44389,3.611151 -6.36286,5.369248 -3.45237,3.162941 -6.48944,6.731577 -9.48752,10.316639 -5.95526,7.087894 -11.63254,14.399231 -17.00034,21.944173 -5.47138,7.72885 -10.96322,15.45542 -17.11251,22.66867 z"
|
||||
inkscape:label="头发部件18" /><path
|
||||
style="opacity:1;fill:#1a1a1a;fill-opacity:0.465732;stroke:#a18f90;stroke-width:6.5;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
||||
id="path6"
|
||||
d="m 431.7055,96.885136 c 0.68345,-0.01683 1.36993,0.008 2.05351,0.03239 1.26616,0.04274 2.52519,0.175792 3.77564,0.374299 1.34943,0.219358 2.67703,0.547853 3.99756,0.89747 1.30448,0.342355 2.58658,0.760225 3.8587,1.206247 0.34787,0.132122 0.88919,0.332358 1.23164,0.483411 0.1893,0.0835 0.73774,0.373687 0.5597,0.268307 -4.92017,-2.912189 -2.83776,-1.86942 -2.01609,-0.911603 0.4665,0.942833 -0.25414,1.810973 -0.83258,2.515753 -1.05305,1.11546 -2.37988,1.92349 -3.65077,2.76486 -0.70455,0.52564 -1.84263,0.99551 -2.02707,1.9792 -0.13577,0.72412 0.14689,1.00706 0.45808,1.64499 0.25805,0.31484 0.49646,0.64686 0.77415,0.94453 0.63085,0.67625 1.54885,1.45348 2.25243,2.03623 1.28413,1.06359 2.61277,2.07518 3.92585,3.10243 2.22765,1.73231 4.46676,3.44711 6.4408,5.4696 1.24472,1.36548 2.33589,2.86124 3.10757,4.54226 0.57402,1.31239 0.61894,2.68201 0.46471,4.08008 -0.15236,1.542 -1.01177,2.74753 -1.96982,3.90485 -1.25092,1.39253 -2.70297,2.57966 -3.98322,3.94302 -0.61776,0.76458 -1.19371,1.60995 -1.3359,2.60746 -0.0756,0.53059 -0.0152,0.87319 0.031,1.40055 0.18192,1.34638 0.59785,2.64226 1.03422,3.92358 0.46172,1.36123 1.10075,2.65093 1.71056,3.94949 0.57934,1.12566 0.88081,2.32829 0.97901,3.58284 0.0285,1.01273 -0.14083,2.01608 -0.35002,3.00302 -0.18321,0.8594 -0.39147,1.62037 -0.92358,2.32955 -0.69384,0.72591 -1.52468,1.30006 -2.31082,1.91913 0,0 3.13155,1.96912 3.13155,1.96912 v 0 c 0.80747,-0.64335 1.66352,-1.23803 2.37418,-1.9921 0.58364,-0.77755 0.80933,-1.52536 1.00495,-2.4688 0.21825,-1.01967 0.38801,-2.05558 0.38421,-3.10149 -0.0761,-1.29512 -0.34601,-2.55043 -0.93895,-3.71574 -0.60728,-1.2882 -1.24849,-2.56502 -1.72185,-3.91075 -0.43768,-1.25837 -0.85635,-2.529 -1.07055,-3.84806 -0.053,-0.43231 -0.12968,-0.85315 -0.0855,-1.29165 0.0948,-0.94059 0.64999,-1.73394 1.21835,-2.45227 1.27656,-1.3752 2.72864,-2.57131 3.98645,-3.96542 1.00742,-1.20382 1.92485,-2.46496 2.104,-4.07351 0.17452,-1.45227 0.1673,-2.88462 -0.39573,-4.26425 -0.74413,-1.73945 -1.85028,-3.26977 -3.10544,-4.6809 -0.76822,-0.80351 -0.91233,-0.98548 -1.76144,-1.74089 -1.5106,-1.3439 -3.15419,-2.52947 -4.72414,-3.80078 -1.30934,-1.02359 -2.63319,-2.03185 -3.9193,-3.08473 -0.71006,-0.5813 -1.57278,-1.30278 -2.22387,-1.96185 -0.27049,-0.2738 -0.51217,-0.57463 -0.76826,-0.86194 -0.2502,-0.39692 -0.62512,-0.79772 -0.57512,-1.31981 0.0792,-0.82671 1.33713,-1.38331 1.899,-1.81756 1.29884,-0.86671 2.65474,-1.69887 3.73107,-2.84377 0.6873,-0.83746 1.4042,-1.79633 1.075,-2.93575 -0.0816,-0.13585 -0.14456,-0.28482 -0.24481,-0.40755 -0.1097,-0.13429 -0.23095,-0.26613 -0.3791,-0.356228 -1.82458,-1.109627 -3.48552,-2.166773 -5.4739,-2.822822 -1.27815,-0.438878 -2.56689,-0.847162 -3.87638,-1.183261 -1.32827,-0.341807 -2.66392,-0.6605 -4.021,-0.867076 -1.26646,-0.185139 -2.54065,-0.306124 -3.82011,-0.354118 -0.68835,-0.02484 -1.39159,-0.0088 -2.07079,-0.120664 z"
|
||||
inkscape:label="头发部件17" /><path
|
||||
style="display:inline;opacity:1;fill:#1a1a1a;fill-opacity:0.465732;stroke:#4c4f59;stroke-width:6.5;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
||||
id="path7"
|
||||
d="m 472.56515,132.89174 c 0.0148,0.77317 -0.10076,1.54619 -0.19578,2.31209 -0.22663,1.66469 -0.65036,3.29152 -1.09324,4.90909 -0.61155,2.16519 -1.31643,4.30298 -2.07388,6.4211 -0.85906,2.40245 -1.88628,4.73722 -3.04362,7.0095 -1.12519,2.18837 -2.40662,4.28829 -3.77255,6.3331 -1.20044,1.78353 -2.51232,3.48695 -3.85961,5.16088 -0.77567,0.94691 -1.56273,1.88443 -2.34905,2.82249 0,0 3.09439,1.85764 3.09439,1.85764 v 0 c 0.78058,-0.94434 1.56325,-1.88703 2.331,-2.84187 1.33972,-1.69155 2.6509,-3.40675 3.84535,-5.20534 1.36483,-2.06191 2.64248,-4.17929 3.77385,-6.37958 1.16439,-2.28454 2.19746,-4.6314 3.07763,-7.04092 0.77266,-2.11878 1.49327,-4.25705 2.12518,-6.4224 0.46749,-1.62041 0.91079,-3.25003 1.1854,-4.9163 0.11547,-0.76691 0.21504,-1.53597 0.32143,-2.30416 z"
|
||||
inkscape:label="头发部件16" /><path
|
||||
style="display:inline;opacity:1;fill:#fff9f6;fill-opacity:1;stroke:#4c4f59;stroke-width:6.5;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
||||
d="m 322.62295,86.032787 c 0,0 75.43341,26.120553 110.16394,89.180323 33.44261,60.72132 33.04918,62.42623 33.04918,62.42623 0,0 0.52459,8.39345 -19.93443,11.54099 -2.62295,0 14.68852,30.95082 19.93443,37.2459 -14.68853,7.34426 -16.78689,8.91803 -16.78689,8.91803 0,0 6.29508,7.86885 4.72131,15.73771 -17.31147,-0.52459 -22.55738,-1.04918 -22.55738,-1.04918 0,0 -4.19672,17.31147 -15.7377,33.04918 C 410.22951,333.11475 410.22951,320 410.22951,320 c 0,0 -24.13115,36.72131 -72.39344,41.44262 -1.57377,-2.62295 12.59016,-14.16393 17.83606,-28.32787 -11.54098,1.57377 -14.68852,2.09836 -14.68852,2.09836 l 10.4918,-16.78688 c 0,0 11.01639,-24.13115 9.44262,-28.32787 -1.04918,-2.62295 -32,-57.70492 -32,-57.70492 0,0 -24.65573,33.04918 -43.54098,49.83607 -6.81967,-4.19672 -13.63934,-14.68853 -13.63934,-14.68853 l -12.59017,11.54099 c 0,0 -48.43778,-50.76436 -42.0945,-90.18318 -3.14754,12.06557 0.12729,56.60941 -5.6432,62.3799 -5.24591,-2.09836 -16.2623,-14.68853 -14.16394,-40.91803 -0.52459,-0.52459 -29.90164,41.44262 -29.90164,41.44262 0,0 -4.19672,48.78688 16.2623,73.44262 1.57377,2.62295 -1.57377,16.2623 -31.47541,-2.62295 1.04918,-1.57377 -1.04918,9.96721 -1.04918,9.96721 0,0 -46.68853,2.09836 -45.63935,-60.85245 -2.62295,-2.62296 -13.639341,34.09836 -13.639341,34.09836 0,0 -24.131151,-32 -0.52459,-92.85246 11.803281,-30.42623 23.344261,-52.85246 31.934421,-67.67213 8.59017,-14.81968 14.22951,-22.03279 14.22951,-22.03279"
|
||||
id="path8"
|
||||
sodipodi:nodetypes="csccccccccccccccccccccccccccssc"
|
||||
inkscape:label="头发部件15 头发底色" /><path
|
||||
id="path36"
|
||||
style="fill:#fffdfc;stroke-width:7.4278;stroke-linecap:round;stroke-miterlimit:1.3;paint-order:fill markers stroke"
|
||||
inkscape:label="头发打光"
|
||||
d="m 355.67214,146.88524 a 91.803276,41.967213 0 0 1 -91.80327,41.96721 91.803276,41.967213 0 0 1 -91.80328,-41.96721 91.803276,41.967213 0 0 1 91.80328,-41.96721 91.803276,41.967213 0 0 1 91.80327,41.96721 z" /><path
|
||||
style="fill:#fde9e7;fill-opacity:1;stroke:none;stroke-width:6.5;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
||||
d="m 349.37705,329.44262 c 0,0 71.34426,-41.96721 78.68852,-91.27868 0,1.04918 26.22951,28.32786 26.22951,28.32786 l 11.54099,24.13115 -15.73771,7.34426 2.09836,13.63935 -22.03279,-1.04918 -12.59016,30.42623 -12.59016,-17.83607 -23.08197,26.22951 -45.11475,14.68852 z"
|
||||
id="path37"
|
||||
inkscape:label="头发阴影"
|
||||
sodipodi:nodetypes="cccccccccccc" /><path
|
||||
style="fill:#fde9e7;fill-opacity:1;stroke:none;stroke-width:6.5;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
||||
d="m 325.2459,144.78689 c 0,0 10.49181,44.06557 2.09836,78.68852 0,10.4918 33.57377,60.85246 33.57377,60.85246 0,0 14.68853,-71.34426 -25.18032,-132.19672 -8.39345,-5.2459 -10.49181,-7.34426 -10.49181,-7.34426 z"
|
||||
id="path38"
|
||||
inkscape:label="头发阴影2" /><path
|
||||
style="fill:#fde9e7;fill-opacity:1;stroke:none;stroke-width:6.5;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
||||
d="m 186.7541,166.81967 23.08197,1.04918 c 0,0 -14.68853,26.22951 -13.63935,46.16394 -2.09836,-3.14754 -22.03279,31.47541 -22.03279,31.47541 0,0 -9.44262,-14.68853 -3.14754,-50.36066 9.44263,-16.78688 15.73771,-28.32787 15.73771,-28.32787 z"
|
||||
id="path39"
|
||||
inkscape:label="头发阴影" /><path
|
||||
id="path36-5"
|
||||
style="fill:#fffdfc;stroke-width:6.94619;stroke-linecap:round;stroke-miterlimit:1.3;paint-order:fill markers stroke"
|
||||
inkscape:label="头发打光"
|
||||
transform="matrix(0.99965843,-0.02613486,0,1,0,0)"
|
||||
d="m 340.28017,112.23744 a 80.289719,41.967213 0 0 1 -80.28972,41.96721 80.289719,41.967213 0 0 1 -80.28972,-41.96721 80.289719,41.967213 0 0 1 80.28972,-41.967211 80.289719,41.967213 0 0 1 80.28972,41.967211 z" /><path
|
||||
style="opacity:1;fill:none;fill-opacity:1;stroke:#a18f90;stroke-width:7.7;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
||||
d="m 138.62295,122.4918 c 0,0 53.63935,-52.983604 92.98361,-58.229505"
|
||||
id="path65"
|
||||
sodipodi:nodetypes="cc" /><path
|
||||
style="opacity:1;fill:#1a1a1a;fill-opacity:0;stroke:#c3b4b0;stroke-width:6.5;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
||||
d="m 436.45902,268.06557 c 0,0 3.67213,18.36066 -2.62295,42.49181"
|
||||
id="path17"
|
||||
inkscape:label="头发部件7" /><path
|
||||
style="opacity:1;fill:#1a1a1a;fill-opacity:0;stroke:#c3b4b0;stroke-width:6.5;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
||||
d="m 418.09836,305.83607 -8.91803,17.83606"
|
||||
id="path16"
|
||||
inkscape:label="头发部件8" /><path
|
||||
style="opacity:1;fill:#1a1a1a;fill-opacity:0;stroke:#c3b4b0;stroke-width:6.5;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
||||
d="m 377.70492,300.59016 c 0,0 2.62295,12.06558 -22.03279,32"
|
||||
id="path15"
|
||||
inkscape:label="头发部件9" /><path
|
||||
style="opacity:1;fill:#1a1a1a;fill-opacity:0;stroke:#c3b4b0;stroke-width:4.2;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
||||
d="m 284.32787,97.573771 c 0,0 12.06557,39.344259 -27.80328,119.081969 0.52459,0 53.5082,-38.81967 56.13115,-89.18033"
|
||||
id="path10"
|
||||
inkscape:label="头发部件14" /><path
|
||||
style="opacity:1;fill:#1a1a1a;fill-opacity:0;stroke:#c3b4b0;stroke-width:5.1;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
||||
d="m 272.78689,87.081967 c 0,0 13.63934,-8.393442 18.88524,-0.52459 17.17267,10.839383 13.03281,30.236103 24.19672,44.393443 1.79263,1.85342 7.1718,-0.78131 7.80328,1.77049 4.88126,19.72513 14.81582,50.80617 4.32401,100.11764"
|
||||
id="path11"
|
||||
sodipodi:nodetypes="csssc"
|
||||
inkscape:label="头发部件13" /><path
|
||||
style="opacity:1;fill:#1a1a1a;fill-opacity:0;stroke:#c3b4b0;stroke-width:4.5;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
||||
d="m 171.00392,241.8537 c 0,0 -28.56248,-60.09248 92.36438,-147.634619 2.22564,0.741882 -59.35061,54.157419 -66.02755,111.282379"
|
||||
id="path12"
|
||||
inkscape:label="头发部件12" /><path
|
||||
style="opacity:1;fill:#1a1a1a;fill-opacity:0;stroke:#c3b4b0;stroke-width:3;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
||||
d="m 327.34426,143.7377 c 0,0 25.18033,20.45902 32,65.57378"
|
||||
id="path13"
|
||||
inkscape:label="头发部件11" /><path
|
||||
style="opacity:1;fill:#1a1a1a;fill-opacity:0;stroke:#4c4f59;stroke-width:6.5;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
||||
d="m 362.4918,268.59016 -3.14754,19.93443"
|
||||
id="path14"
|
||||
inkscape:label="头发部件10" /><path
|
||||
style="opacity:1;fill:#1a1a1a;fill-opacity:0;stroke:#4c4f59;stroke-width:6.5;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
||||
d="m 421.77049,344.13115 c 0,0 -2.62295,58.22951 -33.57377,111.7377 -30.95082,53.5082 38.29508,15.73771 38.29508,15.73771"
|
||||
id="path18"
|
||||
inkscape:label="头发部件6" /><path
|
||||
style="opacity:1;fill:#1a1a1a;fill-opacity:0;stroke:#4c4f59;stroke-width:6.5;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
||||
d="m 395.01639,348.32787 12.06558,60.32787"
|
||||
id="path19"
|
||||
inkscape:label="头发部件5" /><path
|
||||
style="opacity:1;fill:#1a1a1a;fill-opacity:0;stroke:#4c4f59;stroke-width:6.5;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
||||
d="m 371.93443,423.86885 c 0,0 30.95082,-25.70492 24.65573,-73.96721"
|
||||
id="path20"
|
||||
inkscape:label="头发部件4" /><path
|
||||
style="opacity:1;fill:#1a1a1a;fill-opacity:0;stroke:#4c4f59;stroke-width:6.5;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
||||
d="m 332.06557,427.54098 c 0,0 36.72132,1.57377 38.81968,-29.37705"
|
||||
id="path21"
|
||||
inkscape:label="头发部件3" /><path
|
||||
style="opacity:1;fill:#1a1a1a;fill-opacity:0;stroke:#4c4f59;stroke-width:6.5;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
||||
d="m 372.45902,427.01639 -2.09836,-28.85246"
|
||||
id="path22"
|
||||
inkscape:label="头发部件2" /><path
|
||||
style="fill:#93cefc;fill-opacity:1;stroke:#4d4e59;stroke-width:6.5;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
||||
d="m 377.50929,228.61046 c 0,0 24.13115,1.57377 24.13115,10.49181 -0.52459,3.67213 -11.54099,17.83606 -23.60656,18.88524 -2.09836,-1.57377 -0.52459,-29.37705 -0.52459,-29.37705 z"
|
||||
id="path34"
|
||||
sodipodi:nodetypes="cccc"
|
||||
inkscape:label="兔子发卡后" /><path
|
||||
style="opacity:1;fill:#fffdfe;fill-opacity:1;stroke:#4c4f59;stroke-width:6.5;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
||||
d="m 350.95082,235.54098 c 0,0 -6.55738,-22.03278 1.04918,-22.03278 7.60656,0 13.37705,13.90164 13.37705,13.90164 0,0 5.05941,-17.72738 9.96722,-15.21311 11.79758,6.04389 3.40983,21.77048 3.40983,21.77048 0,0 18.62295,25.96722 -12.06558,27.27869 -30.16717,1.28919 -15.7377,-25.70492 -15.7377,-25.70492 z"
|
||||
id="path24"
|
||||
sodipodi:nodetypes="cscscsc"
|
||||
inkscape:label="兔子发卡" /><path
|
||||
style="display:inline;opacity:1;fill:#1a1a1a;fill-opacity:0;stroke:#4c4f59;stroke-width:6.5;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
||||
d="m 332.06557,425.96721 c 0,0 10.49181,-25.18032 6.29509,-49.83606"
|
||||
id="path23"
|
||||
inkscape:label="头发部件" /><path
|
||||
style="display:inline;opacity:1;fill:#e85240;fill-opacity:1;stroke:#4c4f59;stroke-width:6.5;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
||||
d="m 192.2623,268.85246 c 0,0 -15.47541,22.03279 -2.62296,35.40984 42.7541,0.26229 43.27869,0.52459 43.27869,0.52459 0,0 11.27869,-8.39345 5.5082,-32 -25.44262,-9.96722 -46.16393,-3.93443 -46.16393,-3.93443 z"
|
||||
id="path25"
|
||||
inkscape:label="左眼白" /><path
|
||||
style="opacity:1;fill:#fbb579;fill-opacity:1;stroke:none;stroke-width:6.5;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
||||
d="m 188.06557,301.11475 c -0.44151,-8.78192 13.4528,-14.01636 22.29509,-14.42623 8.67244,-0.402 23.2398,3.38526 23.08196,12.06558 -0.0775,4.26198 -11.54098,5.5082 -11.54098,5.5082 0,0 -33.2673,8.16555 -33.83607,-3.14755 z"
|
||||
id="path27"
|
||||
sodipodi:nodetypes="saacs"
|
||||
inkscape:label="左眼瞳" /><path
|
||||
id="path26"
|
||||
style="display:inline;opacity:1;fill:#ffffff;stroke-width:6.5;stroke-linecap:round;stroke-miterlimit:1.3;paint-order:fill markers stroke"
|
||||
inkscape:label="眼光"
|
||||
d="m 217.96721,273.04919 a 7.8688526,7.0819674 0 0 1 -7.86885,7.08197 7.8688526,7.0819674 0 0 1 -7.86885,-7.08197 7.8688526,7.0819674 0 0 1 7.86885,-7.08196 7.8688526,7.0819674 0 0 1 7.86885,7.08196 z" /><path
|
||||
style="fill:none;fill-opacity:1;stroke:#ebc2bf;stroke-width:2.5;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
||||
d="m 174.16393,245.5082 c 15.08644,-4.76649 31.53934,-7.45293 47.09358,-3.51527 3.29779,0.83486 6.51821,1.99257 9.56216,3.51527"
|
||||
id="path42"
|
||||
inkscape:path-effect="#path-effect42"
|
||||
inkscape:original-d="m 174.16393,245.5082 c 11.60252,-3.79212 36.99244,-9.83166 56.65574,0"
|
||||
transform="translate(0,4)" /><path
|
||||
style="opacity:1;fill:#fde9e7;fill-opacity:1;stroke:none;stroke-width:6.945;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
||||
d="m 477.77235,132.79697 c 0,0 13.35388,10.01542 14.83765,17.43424 1.48376,7.41883 1.48376,57.4959 1.48376,57.4959 l -4.45129,1.11282 -30.78813,-37.46507 c 0,0 8.90259,-14.46671 11.12824,-22.62741 2.22565,-8.16071 7.78977,-15.95048 7.78977,-15.95048 z"
|
||||
id="path67" /><path
|
||||
style="fill:#e85240;fill-opacity:0;stroke:#4c4f59;stroke-width:6.5;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
||||
d="m 192.2623,268.85246 c 0,0 -15.47541,22.03279 -2.62296,35.40984 42.7541,0.26229 43.27869,0.52459 43.27869,0.52459 0,0 11.27869,-8.39345 5.5082,-32 -25.44262,-9.96722 -46.16393,-3.93443 -46.16393,-3.93443 z"
|
||||
id="path25-9"
|
||||
inkscape:label="左眼遮挡上" /><path
|
||||
style="display:inline;fill:#e85240;fill-opacity:1;stroke:#4c4f59;stroke-width:6.5;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
||||
d="m 325.52138,272.81871 c 17.02505,1.06627 38.81967,23.08196 17.83607,48.26229 -1.57377,0.52459 -48.98481,-5.30599 -49.57378,-9.18033 -0.80561,-5.29953 -7.95666,-41.56799 31.73771,-39.08196 z"
|
||||
id="path40"
|
||||
sodipodi:nodetypes="scss"
|
||||
inkscape:label="右眼白" /><path
|
||||
style="fill:#fbb579;fill-opacity:1;stroke:none;stroke-width:7.14054;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
||||
d="m 293.04592,306.32136 c 2.55323,-8.53469 20.43105,-9.52962 30.78942,-7.36618 10.1594,2.12188 25.66784,9.9581 22.45763,18.22228 -1.5762,4.05766 -15.25453,1.93723 -15.25453,1.93723 0,0 -41.28165,-1.79873 -37.99252,-12.79333 z"
|
||||
id="path27-0"
|
||||
sodipodi:nodetypes="saacs"
|
||||
inkscape:label="左眼瞳" /><path
|
||||
style="display:inline;fill:none;fill-opacity:1;stroke:#4c4f59;stroke-width:6.5;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
||||
d="m 325.52138,272.81871 c 17.02505,1.06627 38.81967,23.08196 17.83607,48.26229 -1.57377,0.52459 -48.98481,-5.30599 -49.57378,-9.18033 -0.80561,-5.29953 -7.95666,-41.56799 31.73771,-39.08196 z"
|
||||
id="path40-2"
|
||||
sodipodi:nodetypes="scss"
|
||||
inkscape:label="右眼遮挡上" /><path
|
||||
style="fill:none;fill-opacity:1;stroke:#ebc2bf;stroke-width:2.5;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
||||
d="m 309.32819,258.43459 c 0,0 26.2295,-5.2459 36.72131,4.19672"
|
||||
id="path43" /><path
|
||||
id="path26-9"
|
||||
style="display:inline;fill:#ffffff;stroke-width:6.5;stroke-linecap:round;stroke-miterlimit:1.3;paint-order:fill markers stroke"
|
||||
inkscape:label="眼光"
|
||||
d="m 329.6342,284.99301 a 7.8688526,7.0819674 0 0 1 -7.86885,7.08197 7.8688526,7.0819674 0 0 1 -7.86885,-7.08197 7.8688526,7.0819674 0 0 1 7.86885,-7.08197 7.8688526,7.0819674 0 0 1 7.86885,7.08197 z" /><path
|
||||
style="opacity:1;fill:none;fill-opacity:1;stroke:#4c4f59;stroke-width:14.5;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
||||
d="m 170.4918,266.4918 c 0,0 32,-19.93442 70.29509,-2.09836"
|
||||
id="path28"
|
||||
inkscape:label="眉毛" /><path
|
||||
style="fill:none;fill-opacity:1;stroke:#4c4f59;stroke-width:14.5;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
||||
d="m 292.9971,274.17683 c 0,0 35.55098,-12.55026 69.08832,13.13797"
|
||||
id="path28-2"
|
||||
inkscape:label="眉毛" /><path
|
||||
style="display:inline;opacity:1;fill:none;fill-opacity:0;stroke:#4c4f59;stroke-width:6.5;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
||||
d="m 431.03375,176.1971 18.17612,-17.43424 41.54542,54.15742"
|
||||
id="path30"
|
||||
inkscape:label="蓝色发卡遮挡上" /><path
|
||||
style="opacity:1;fill:none;fill-opacity:0;stroke:#4c4f59;stroke-width:6.5;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
||||
d="m 454.0321,267.44865 43.21466,-23.92571"
|
||||
id="path31"
|
||||
sodipodi:nodetypes="cc"
|
||||
inkscape:label="蓝色发卡遮挡下" /><path
|
||||
style="display:inline;fill:#fff9f6;fill-opacity:0;stroke:#4c4f59;stroke-width:6.5;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
||||
d="m 164.23888,424.64081 -31.07097,26.05282 c 0,0 -5.19317,18.17613 25.96589,28.93342 31.15907,10.7573 213.29123,1.48377 213.29123,1.48377 0,0 52.30272,11.87012 80.86519,-37.09413 -0.37094,0.74188 -8.90259,19.28895 -8.90259,19.28895 l 1.48377,19.28894 c 0,0 57.73516,-9.41425 30.45647,-148.95524 4.19672,-9.44262 8.39344,-15.21311 8.39344,-15.21311 l -3.14754,-21.77049 c 0,0 5.65758,-40.33533 7.88323,-45.52851 2.22564,-5.19317 8.51558,-6.35116 9.64447,-11.12823 2.06907,-8.75561 -2.78206,-20.40177 -6.67694,-26.15136 -0.74189,-3.33847 6.86241,-35.05395 2.04017,-58.05231 -7.04788,-11.87012 -4.82223,-11.87012 -18.918,-25.59495 0,0.37094 8.90259,-44.88389 -0.74188,-73.446367 -2.96753,0 -32.27189,-10.757296 -96.81567,23.740241 -0.37094,1.112824 -26.33683,-30.046242 -153.56969,-15.950474 -0.74188,0 -43.40012,-37.836009 -66.76942,-40.432598 -1.11283,-0.370941 -22.62742,-4.080354 -25.22401,48.964247 0,1.112824 -44.14201,15.950474 -56.01213,64.914721 -11.87012,48.96425 -11.128238,80.86519 -11.128238,80.86519 0,0 -18.904912,27.6632 -9.798121,25.03308 5.346826,-1.54421 4.975885,23.56023 4.975885,23.56023 l -2.225648,39.31977 c 0,0 -14.466709,35.23942 -14.83765,59.72155 -0.370942,24.48212 -0.741883,34.12659 4.822236,48.5933 5.564119,14.46671 20.772711,46.7386 42.658245,63.06002 8.902591,0.37094 18.918001,1.8547 18.918001,1.8547 l 3.70942,-20.77271 c 0,0 18.918,15.95048 32.27189,14.83765 13.35388,-1.11282 -0.74189,-0.74188 -0.74189,-0.74188 l -13.72482,-17.80518 z"
|
||||
id="path2-4"
|
||||
sodipodi:nodetypes="ccscccccccsaccccccccscsccsscccsccc"
|
||||
inkscape:label="上发层" /><path
|
||||
style="fill:none;fill-opacity:1;stroke:#c3b4b0;stroke-width:6.5;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
||||
d="m 197.5082,220.06557 18.36065,-12.32786"
|
||||
id="path46" /><path
|
||||
style="fill:none;fill-opacity:1;stroke:#c3b4b0;stroke-width:6.5;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
||||
d="m 327.54114,234.24941 c 0,0 -6.27192,4.90452 -15.85821,-0.991"
|
||||
id="path47"
|
||||
sodipodi:nodetypes="cc" /><path
|
||||
style="display:inline;fill:none;fill-opacity:1;stroke:#4c4f59;stroke-width:6.5;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
||||
d="m 322.62295,86.03279 c 0,0 75.43341,26.12055 110.16394,89.18032 33.44261,60.72132 33.04918,62.42623 33.04918,62.42623 0,0 0.52459,8.39345 -19.93443,11.54099 -2.62295,0 14.68852,30.95082 19.93443,37.2459 -14.68853,7.34426 -16.78689,8.91803 -16.78689,8.91803 0,0 6.29508,7.86885 4.72131,15.73771 -17.31147,-0.52459 -22.55738,-1.04918 -22.55738,-1.04918 0,0 -4.19672,17.31147 -15.7377,33.04918 C 410.22951,333.11475 410.22951,320 410.22951,320 c 0,0 -24.13115,36.72131 -72.39344,41.44262 -1.57377,-2.62295 12.59016,-14.16393 17.83606,-28.32787 -11.54098,1.57377 -14.68852,2.09836 -14.68852,2.09836 l 10.4918,-16.78688 c 0,0 11.01639,-24.13115 9.44262,-28.32787 -1.04918,-2.62295 -32,-57.70492 -32,-57.70492 0,0 -24.65573,33.04918 -43.54098,49.83607 -6.81967,-4.19672 -13.63934,-14.68853 -13.63934,-14.68853 l -12.59017,11.54099 c 0,0 -48.43778,-50.76436 -42.0945,-90.18318 -3.14754,12.06557 0.12729,56.60941 -5.6432,62.3799 -5.24591,-2.09836 -16.2623,-14.68853 -14.16394,-40.91803 -0.52459,-0.52459 -29.90164,41.44262 -29.90164,41.44262 0,0 -4.19672,48.78688 16.2623,73.44262 1.57377,2.62295 -1.57377,16.2623 -31.47541,-2.62295 1.04918,-1.57377 -1.04918,9.96721 -1.04918,9.96721 0,0 -46.68853,2.09836 -45.63935,-60.85245 -2.62295,-2.62296 -13.63934,34.09836 -13.63934,34.09836 0,0 -24.13115,-32 -0.52459,-92.85246 11.80328,-30.42623 23.34426,-52.85246 31.93442,-67.67213 8.59017,-14.81968 14.22951,-22.03279 14.22951,-22.03279"
|
||||
id="path8-0"
|
||||
sodipodi:nodetypes="csccccccccccccccccccccccccccssc"
|
||||
inkscape:label="头发部件15 头发底色" /><path
|
||||
id="path44-3"
|
||||
style="fill:#a88a8f;stroke:#4c4f59;stroke-width:2.8623;stroke-linecap:round;stroke-miterlimit:1.3;paint-order:fill markers stroke"
|
||||
d="m 216.29183,255.53888 a 8.8904896,7.4928694 0 0 1 -8.89049,7.49287 8.8904896,7.4928694 0 0 1 -8.89049,-7.49287 8.8904896,7.4928694 0 0 1 8.89049,-7.49287 8.8904896,7.4928694 0 0 1 8.89049,7.49287 z" /><path
|
||||
id="path44"
|
||||
style="fill:#a88a8f;stroke:#4c4f59;stroke-width:2.8623;stroke-linecap:round;stroke-miterlimit:1.3;paint-order:fill markers stroke"
|
||||
d="m 336.16933,270.5473 a 8.8904896,7.4928694 0 0 1 -8.89049,7.49287 8.8904896,7.4928694 0 0 1 -8.89049,-7.49287 8.8904896,7.4928694 0 0 1 8.89049,-7.49287 8.8904896,7.4928694 0 0 1 8.89049,7.49287 z" /><path
|
||||
style="opacity:1;fill:none;fill-opacity:0;stroke:#a18f90;stroke-width:10.4;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
||||
d="m 318.7189,85.461784 c 0,0 50.44801,17.063296 76.78484,43.400126 26.33683,26.33683 32.64283,39.31978 32.64283,39.31978"
|
||||
id="path32"
|
||||
inkscape:label="上方头发边缘加厚" /><path
|
||||
style="fill:none;fill-opacity:1;stroke:#4c4f59;stroke-width:6.5;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
||||
d="m 186.7541,224.52459 10.4918,1.04918"
|
||||
id="path45" /><path
|
||||
style="opacity:1;fill:#fcbeb5;fill-opacity:1;stroke:#e99c9b;stroke-width:3.4;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
||||
d="m 250.94176,349.98308 c 0,0 -13.91029,-8.90259 -10.0154,-16.69235 3.89488,-7.78977 17.80517,-4.4513 17.80517,-4.4513 0,0 12.98295,-1.29829 20.03083,-3.89488 7.04788,-2.59659 9.64447,5.0077 9.64447,5.0077 0,0 4.76461,6.06544 -4.26583,16.50689 -5.93505,6.86241 -21.14364,7.04788 -21.14364,7.04788"
|
||||
id="path48"
|
||||
sodipodi:nodetypes="cscscsc" /><path
|
||||
style="opacity:1;fill:#ffffff;fill-opacity:1;stroke:#e99c9b;stroke-width:1.8;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
||||
d="m 276.20884,325.76493 c 0,0 4.35031,3.01904 5.37456,7.95235 0.57119,0.10629 3.33355,-3.08729 2.97647,-6.87263 -0.67968,-1.48531 -8.35103,-1.07972 -8.35103,-1.07972 z"
|
||||
id="path49" /><path
|
||||
style="opacity:1;fill:#ffffff;fill-opacity:1;stroke:#e99c9b;stroke-width:1.8;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
||||
d="m 245.0067,329.39584 -4.17308,2.78206 -0.0927,6.0278 -0.0956,-0.0355 7.07328,-8.8174 z"
|
||||
id="path50"
|
||||
sodipodi:nodetypes="cccccc" /><path
|
||||
style="fill:none;fill-opacity:1;stroke:#e99c9b;stroke-width:3.4;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
||||
d="m 250.94176,349.98308 c 0,0 -13.91029,-8.90259 -10.0154,-16.69235 3.89488,-7.78977 17.80517,-4.4513 17.80517,-4.4513 0,0 12.98295,-1.29829 20.03083,-3.89488 7.04788,-2.59659 9.64447,5.0077 9.64447,5.0077 0,0 4.76461,6.06544 -4.26583,16.50689 -5.93505,6.86241 -21.14364,7.04788 -21.14364,7.04788"
|
||||
id="path48-2"
|
||||
sodipodi:nodetypes="cscscsc" /><path
|
||||
style="opacity:1;fill:#ffffff;fill-opacity:1;stroke:#e89493;stroke-width:2.8;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
||||
d="m 190.84928,314.92913 6.12053,15.02312"
|
||||
id="path51" /><path
|
||||
style="opacity:1;fill:#ffffff;fill-opacity:1;stroke:#e89493;stroke-width:2.8;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
||||
d="m 204.94505,316.96931 5.56412,14.28124"
|
||||
id="path52" /><path
|
||||
style="opacity:1;fill:#ffffff;fill-opacity:1;stroke:#e89493;stroke-width:2.8;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
||||
d="m 217.92799,317.34025 7.41883,16.50689"
|
||||
id="path53" /><path
|
||||
style="opacity:1;fill:#ffffff;fill-opacity:1;stroke:#e89493;stroke-width:2.8;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
||||
d="m 300.64789,328.46849 6.86242,18.17612"
|
||||
id="path54" /><path
|
||||
style="opacity:1;fill:#ffffff;fill-opacity:1;stroke:#e89493;stroke-width:2.8;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
||||
d="m 312.70348,329.21037 6.67695,17.0633"
|
||||
id="path55" /><path
|
||||
style="opacity:1;fill:#ffffff;fill-opacity:1;stroke:#e89493;stroke-width:2.8;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
||||
d="m 326.24284,328.65396 7.04788,17.99065"
|
||||
id="path56" /><path
|
||||
style="opacity:1;fill:none;fill-opacity:1;stroke:#e49589;stroke-width:4.6;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
||||
d="m 218.4918,348.98361 c -1.87307,1.30472 -3.33608,3.11401 -4.70333,4.91603 -1.02497,1.35089 -2.00187,2.75003 -2.77208,4.2643"
|
||||
id="path61"
|
||||
inkscape:path-effect="#path-effect61"
|
||||
inkscape:original-d="m 218.4918,348.98361 c -1.70592,0.99645 -5.82749,5.88449 -7.47541,9.18033"
|
||||
transform="rotate(-3.3120949,219.55256,344.63259)" /><path
|
||||
style="opacity:1;fill:none;fill-opacity:1;stroke:#4c4f59;stroke-width:3.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
||||
d="m 127.47541,375.08197 c 0,0 -14.42623,-19.14754 -12.06557,-59.54099"
|
||||
id="path92" /><path
|
||||
style="opacity:1;fill:#ffffff;fill-opacity:0.734285;stroke:#c1545a;stroke-width:7.8;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
||||
d="m 136.39344,322.09836 c 0,0 9.44263,-18.88525 -4.19672,-39.86885 -3.14754,-0.52459 27.80328,-27.80328 5.2459,-77.11476 -1.57377,0.52459 23.60656,-55.60655 -24.13115,-82.88524 -4.19672,-8.39344 -45.639336,-69.245904 -89.180316,13.11475 0,11.54099 -1.04918,36.19672 -1.04918,36.19672 0,0 -14.6885299,38.81968 -6.29509,71.86886 2.09837,3.14754 -20.9835999,49.83606 11.0164,106.4918 0.52459,0.52459 -16.78689,57.70492 12.59016,82.88525 1.04918,4.72131 13.63935,33.57377 42.49181,39.34426 12.59016,0.52459 19.409826,-0.52459 19.409826,-0.52459 0,0 31.47541,-4.72131 47.73771,-67.14754 -2.09836,-27.80328 -4.19672,-50.36066 -11.0164,-70.81968"
|
||||
id="path93" /><path
|
||||
id="path101"
|
||||
style="opacity:1;fill:#ffffff;fill-opacity:0.734285;stroke:#000000;stroke-width:9;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
||||
d="m 109.69769,219.48859 c 0,0 -15.208592,-11.87012 -21.143652,-19.65989 m -37.09413,20.03083 22.25648,-21.88553 m 0.37094,-14.09577 5.93506,41.91636 m -35.23942,-24.11118 56.012132,-4.82224 m 11.12824,-17.43424 -19.288952,0.37094 m -0.37094,-0.74188 -1.48376,-29.6753 m -38.94883,3.33847 37.836,-2.96753 m -24.85306,-11.49918 0.74188,39.69072"
|
||||
inkscape:label="杂" /><path
|
||||
id="path111"
|
||||
style="opacity:1;fill:#ffffff;fill-opacity:0.734285;stroke:#000000;stroke-width:9;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
||||
d="m 58.507798,352.28556 54.157422,-3.33847 m -46.367662,-13.72483 35.610372,-2.96753 m -21.514602,-32.27189 2.59659,33.38472 m -18.918,-16.32142 37.094122,-3.33847 m 1.48377,18.54706 0.74188,-33.75565 m -0.37094,0.37094 -40.803542,2.22565 m 0,-0.37094 4.4513,33.38471 m 27.44965,-48.22237 3.33847,-14.4667 m 0,-1.48377 -27.44965,1.48377 m -1.48377,14.83765 0.74188,-20.03083"
|
||||
inkscape:label="鱼" /><path
|
||||
d="m 69.453989,376.46984 c -4.220665,0.22086 -8.079406,1.8518 -10.783191,4.54882 -2.980547,2.98155 -4.241957,7.10139 -3.534076,11.54824 0.904812,5.67433 4.763552,11.7394 11.384623,17.90216 1.405115,1.30393 2.39508,2.14912 4.390984,3.74184 3.507462,2.79468 6.679618,5.01174 11.04931,7.70876 1.559468,0.96838 4.635815,2.76496 5.796096,3.38931 l 0.335314,0.18263 0.48966,-0.26758 c 1.783005,-0.98111 4.960482,-2.86264 6.844609,-4.05189 7.126702,-4.49782 12.832312,-9.01266 17.063632,-13.50625 6.86057,-7.28827 9.77725,-14.38543 8.43068,-20.49298 -0.93142,-4.20475 -4.04502,-7.68327 -8.46261,-9.45012 -1.90011,-0.76026 -3.74699,-1.15525 -5.83869,-1.24869 -2.7091,-0.12316 -5.37562,0.33977 -7.91973,1.37185 -4.332435,1.75413 -7.951675,5.17315 -10.229661,9.66674 -0.159681,0.3143 -0.308701,0.59038 -0.329985,0.60737 -0.0692,0.0553 -0.143707,-0.051 -0.431122,-0.62859 -0.681263,-1.35913 -2.01719,-3.3341 -3.065699,-4.52331 -1.07513,-1.22747 -2.804909,-2.72251 -4.092931,-3.54224 -3.326503,-2.11937 -7.222503,-3.15569 -11.097213,-2.95607 z"
|
||||
id="path1-4"
|
||||
style="fill:#d96477;fill-opacity:1;stroke:none;stroke-width:0.00475452" /></g></svg>
|
||||
|
After Width: | Height: | Size: 60 KiB |
BIN
Website/docs/public/web-app-manifest-192x192.png
Normal file
|
After Width: | Height: | Size: 22 KiB |
BIN
Website/docs/public/web-app-manifest-512x512.png
Normal file
|
After Width: | Height: | Size: 66 KiB |
101
Website/docs/zh/config.ts
Normal file
@@ -0,0 +1,101 @@
|
||||
import { defineConfig, type DefaultTheme } from 'vitepress'
|
||||
|
||||
export default defineConfig({
|
||||
lang: 'zh-Hans',
|
||||
description: '下一代 Android Root 解决方案 - Android 上的内核级的高级 root 方案',
|
||||
|
||||
themeConfig: {
|
||||
nav: nav(),
|
||||
|
||||
sidebar: {
|
||||
'/zh/': { base: '/zh/', items: sidebar() },
|
||||
},
|
||||
|
||||
search: { options: searchOptions() },
|
||||
editLink: {
|
||||
pattern: 'https://github.com/sukisu-ultra/sukisu-ultra/edit/main/docs/:path',
|
||||
text: '在 GitHub 上编辑此页面',
|
||||
},
|
||||
|
||||
docFooter: {
|
||||
prev: '上一页',
|
||||
next: '下一页',
|
||||
},
|
||||
|
||||
outline: {
|
||||
label: '页面导航',
|
||||
},
|
||||
|
||||
lastUpdated: {
|
||||
text: '最后更新于',
|
||||
},
|
||||
|
||||
notFound: {
|
||||
title: '页面未找到',
|
||||
quote: '抱歉,我们无法找到您要查找的页面。',
|
||||
linkLabel: '前往首页',
|
||||
linkText: '带我回首页',
|
||||
},
|
||||
|
||||
langMenuLabel: '多语言',
|
||||
returnToTopLabel: '回到顶部',
|
||||
sidebarMenuLabel: '菜单',
|
||||
darkModeSwitchLabel: '主题',
|
||||
lightModeSwitchTitle: '切换到浅色模式',
|
||||
darkModeSwitchTitle: '切换到深色模式',
|
||||
skipToContentLabel: '跳转到内容',
|
||||
},
|
||||
})
|
||||
|
||||
function nav(): DefaultTheme.NavItem[] {
|
||||
return [
|
||||
{ text: '首页', link: '/zh/' },
|
||||
{
|
||||
text: '开始使用',
|
||||
items: [
|
||||
{ text: '介绍', link: '/zh/guide/' },
|
||||
{ text: '安装', link: '/zh/guide/installation' },
|
||||
{ text: '集成', link: '/zh/guide/how-to-integrate' },
|
||||
{ text: '兼容性', link: '/zh/guide/compatibility' },
|
||||
{ text: '链接', link: '/zh/guide/links' },
|
||||
{ text: '许可', link: '/zh/guide/license' },
|
||||
],
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
function sidebar(): DefaultTheme.SidebarItem[] {
|
||||
return [
|
||||
{
|
||||
text: '开始使用',
|
||||
items: [
|
||||
{ text: '介绍', link: '/guide/' },
|
||||
{ text: '安装', link: '/guide/installation' },
|
||||
{ text: '集成', link: '/guide/how-to-integrate' },
|
||||
{ text: '兼容性', link: '/guide/compatibility' },
|
||||
{ text: '链接', link: '/guide/links' },
|
||||
{ text: '许可', link: '/guide/license' },
|
||||
],
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
function searchOptions(): Partial<DefaultTheme.LocalSearchOptions> {
|
||||
return {
|
||||
translations: {
|
||||
button: {
|
||||
buttonText: '搜索文档',
|
||||
buttonAriaLabel: '搜索文档',
|
||||
},
|
||||
modal: {
|
||||
noResultsText: '无法找到相关结果',
|
||||
resetButtonTitle: '清除查询条件',
|
||||
footer: {
|
||||
selectText: '选择',
|
||||
navigateText: '切换',
|
||||
closeText: '关闭',
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
23
Website/docs/zh/guide/compatibility.md
Normal file
@@ -0,0 +1,23 @@
|
||||
# 兼容性状态
|
||||
|
||||
::: info KernelSU
|
||||
KernelSU(v0.9.5 之前的版本)官方支持 Android GKI 2.0 设备(内核 5.10+)
|
||||
:::
|
||||
|
||||
::: warning 传统内核支持
|
||||
较旧的内核(4.4+)也兼容,但内核必须手动构建
|
||||
:::
|
||||
|
||||
::: tip 扩展兼容性
|
||||
SukiSU-Ultra 可以通过额外的反向移植支持 3.x 内核(3.4-3.18)
|
||||
:::
|
||||
|
||||
## 架构支持
|
||||
|
||||
目前支持以下处理器架构:
|
||||
|
||||
| 架构 | 支持级别 | 备注 |
|
||||
| --------------- | :---------: | -----------: |
|
||||
| **arm64-v8a** | ✅ 完全支持 | 主要目标架构 |
|
||||
| **armeabi-v7a** | ✅ 基础支持 | 最低功能要求 |
|
||||
| **X86_64** | 🟡 部分支持 | 支持部分设备 |
|
||||
94
Website/docs/zh/guide/how-to-integrate.md
Normal file
@@ -0,0 +1,94 @@
|
||||
# 集成指导
|
||||
|
||||
SukiSU 可以集成到 GKI 和 non-GKI 内核中,并且已反向移植到 4.14 版本。
|
||||
|
||||
<!-- 应该是 3.4 版本,但 backslashxx 的 syscall manual hook 无法在 SukiSU 中使用-->
|
||||
|
||||
有些 OEM 定制可能导致多达 50% 的内核代码超出内核树代码,而非来自上游 Linux 内核或 ACK。因此,non-GKI 内核的定制特性导致了严重的内核碎片化,而且我们缺乏构建它们的通用方法。因此,我们无法提供 non-GKI 内核的启动映像。
|
||||
|
||||
前提条件:开源的、可启动的内核。
|
||||
|
||||
## Hook 方法
|
||||
|
||||
1. **KPROBES hook:**
|
||||
- GKI kernels 的默认 hook 方法。
|
||||
- 需要 `# CONFIG_KSU_MANUAL_HOOK is not set`(未设定) & `CONFIG_KPROBES=y`
|
||||
- 用作可加载的内核模块 (LKM).
|
||||
|
||||
2. **Manual hook:**
|
||||
|
||||
<!-- - backslashxx's syscall manual hook: https://github.com/backslashxx/KernelSU/issues/5 (v1.5 version is not available at the moment, if you want to use it, please use v1.4 version, or standard KernelSU hooks)-->
|
||||
- 需要 `CONFIG_KSU_MANUAL_HOOK=y`
|
||||
- 需要 [`guide/how-to-integrate.md`](how-to-integrate.md)
|
||||
- 需要 [https://github.com/~](https://github.com/tiann/KernelSU/blob/main/website/docs/guide/how-to-integrate-for-non-gki.md#manually-modify-the-kernel-source)
|
||||
|
||||
3. **Tracepoint Hook:**
|
||||
- 自 SukiSU commit [49b01aad](https://github.com/SukiSU-Ultra/SukiSU-Ultra/commit/49b01aad74bcca6dba5a8a2e053bb54b648eb124) 引入的 hook 方法
|
||||
- 需要 `CONFIG_KSU_TRACEPOINT_HOOK=y`
|
||||
- 需要 [`guide/tracepoint-hook.md`](tracepoint-hook.md)
|
||||
|
||||
<!-- This part refer to [rsuntk/KernelSU](https://github.com/rsuntk/KernelSU). -->
|
||||
|
||||
如果您能够构建可启动内核,有两种方法可以将 KernelSU 集成到内核源代码中:
|
||||
|
||||
1. 使用 `kprobe` 自动集成
|
||||
2. 手动集成
|
||||
|
||||
## 与 kprobe 集成
|
||||
|
||||
适用:
|
||||
|
||||
- GKI 内核
|
||||
|
||||
不适用:
|
||||
|
||||
- non-GKI 内核
|
||||
|
||||
KernelSU 使用 kprobe 机制来做内核的相关 hook,如果 _kprobe_ 可以在你编译的内核中正常运行,那么推荐用这个方法来集成。
|
||||
|
||||
请参阅此文档 [https://github.com/~](https://github.com/tiann/KernelSU/blob/main/website/docs/guide/how-to-integrate-for-non-gki.md#integrate-with-kprobe)。虽然标题为“适用于 non-GKI”,但仅适用于 GKI。
|
||||
|
||||
替换 KernelSU 添加到内核源代码树的步骤的执行命令为:
|
||||
|
||||
```sh [bash]
|
||||
curl -LSs "https://raw.githubusercontent.com/SukiSU-Ultra/SukiSU-Ultra/main/kernel/setup.sh" | bash -s main
|
||||
```
|
||||
|
||||
## 手动修改内核源代码
|
||||
|
||||
适用:
|
||||
|
||||
- GKI 内核
|
||||
- non-GKI 内核
|
||||
|
||||
请参考此文档 [https://github.com/~ (non-GKI 内核集成)](https://github.com/tiann/KernelSU/blob/main/website/docs/guide/how-to-integrate-for-non-gki.md#manually-modify-the-kernel-source) 和 [https://github.com/~ (GKI 内核构建)](https://kernelsu.org/zh_CN/guide/how-to-build.html) 进行手动集成。虽然第一个链接的标题是“适用于 non-GKI”,但它也适用于 GKI。两者都可以正常工作。
|
||||
|
||||
还有另一种集成方法,但是仍在开发中。
|
||||
|
||||
<!-- 这是 backslashxx 的syscall manual hook,但目前无法使用。 -->
|
||||
|
||||
将 KernelSU(SukiSU)添加到内核源代码树的步骤的运行命令将被替换为:
|
||||
|
||||
### GKI 内核
|
||||
|
||||
```sh [bash]
|
||||
curl -LSs "https://raw.githubusercontent.com/SukiSU-Ultra/SukiSU-Ultra/main/kernel/setup.sh" | bash -s main
|
||||
```
|
||||
|
||||
### non-GKI 内核
|
||||
|
||||
```sh [bash]
|
||||
curl -LSs "https://raw.githubusercontent.com/SukiSU-Ultra/SukiSU-Ultra/main/kernel/setup.sh" | bash -s nongki
|
||||
```
|
||||
|
||||
### 带有 susfs 的 GKI / non-GKI 内核(实验)
|
||||
|
||||
```sh [bash]
|
||||
curl -LSs "https://raw.githubusercontent.com/SukiSU-Ultra/SukiSU-Ultra/main/kernel/setup.sh" | bash -s susfs-{{branch}}
|
||||
```
|
||||
|
||||
分支:
|
||||
|
||||
- `main` (susfs-main)
|
||||
- `test` (susfs-test)
|
||||
- 版本号 (例如: susfs-1.5.7, 你需要在 [分支](https://github.com/SukiSU-Ultra/SukiSU-Ultra/branches) 里找到它)
|
||||
35
Website/docs/zh/guide/index.md
Normal file
@@ -0,0 +1,35 @@
|
||||
# 什么是 SukiSU-Ultra?
|
||||
|
||||
一个 Android 上基于内核的 root 方案,由 [`tiann/KernelSU`](https://github.com/tiann/KernelSU) 分叉而来,添加了一些有趣的变更。
|
||||
|
||||
## 特性
|
||||
|
||||
1. 基于内核的 su 和权限管理。
|
||||
2. 基于 Magic Mount 的模块系统。
|
||||
3. App Profile: 把 Root 权限关进笼子里。
|
||||
4. 支持 non-GKI 与 GKI 1.0。
|
||||
5. KPM 支持
|
||||
6. 可调整管理器外观,可自定义 susfs 配置。
|
||||
|
||||
## 如何安装
|
||||
|
||||
了解如何在您的设备上 **[安装](./installation)** SukiSU-Ultra
|
||||
|
||||
## 如何集成
|
||||
|
||||
阅读 **[集成](./how-to-integrate.md)** 将 SukiSU 集成到你的内核中
|
||||
|
||||
## 了解兼容性
|
||||
|
||||
检查设备 **[兼容性](./compatibility)** 要求
|
||||
|
||||
## 资源获取
|
||||
|
||||
查找其他资源和 **[下载](./links)**
|
||||
|
||||
## 获取支持
|
||||
|
||||
需要帮助?我们在这里为您提供帮助:
|
||||
|
||||
- **错误报告**: [GitHub Issues](https://github.com/SukiSU-Ultra/SukiSU-Ultra/issues)
|
||||
- **直接支持**: 联系开发者处理关键问题
|
||||
149
Website/docs/zh/guide/installation.md
Normal file
@@ -0,0 +1,149 @@
|
||||
# 安装参考
|
||||
|
||||
::: details 将会了解 SukiSU-Ultra 安装过程
|
||||
[[toc]]
|
||||
:::
|
||||
|
||||
::: danger 警告
|
||||
**Root 您的设备可能会使保修失效,如果操作不当可能会造成永久性损坏。**
|
||||
请务必在继续之前创建完整备份,阅读文档确保与您的设备兼容,遵循文档参考,准备好恢复计划
|
||||
:::
|
||||
|
||||
## 通用 GKI
|
||||
|
||||
请**全部**参考 [KernelSU 安装指南](https://kernelsu.org/zh_CN/guide/installation.html)
|
||||
|
||||
1. 适用于 GKI 2.0 设备,如小米、红米、三星等(不包括内核修改的制造商,如魅族、一加、真我和 OPPO)
|
||||
2. 在[更多链接](./links)中查找 GKI 构建。找到设备内核版本,然后下载并使用 TWRP 或内核刷写工具刷入带有 `AnyKernel3` 后缀的
|
||||
zip 文件。
|
||||
3. 无后缀的 `.zip` 档案是未压缩的,`gz` 后缀是特定型号使用的压缩格式。
|
||||
|
||||
## 一加设备
|
||||
|
||||
使用[更多链接](./links)部分提到的链接,用您的设备信息创建自定义构建,然后刷入带有 AnyKernel3 后缀的 zip 文件。
|
||||
|
||||
::: details 展开
|
||||
|
||||
- 只需要填写内核版本的前两部分,如 `5.10`、`5.15`、`6.1` 或 `6.6`。
|
||||
- 请自行搜索处理器代号,通常是不含数字的英文字母。
|
||||
- 可以从一加开源内核仓库中找到分支和配置文件。
|
||||
- 第三方 Recovery(推荐 TWRP)
|
||||
|
||||
:::
|
||||
|
||||
## 开始安装
|
||||
|
||||
### 通用 GKI 安装
|
||||
|
||||
::: tip
|
||||
适用于 GKI 2.0 设备,如小米、红米、三星等(不包括内核修改的制造商,如魅族、一加、真我和 OPPO)
|
||||
:::
|
||||
|
||||
#### 步骤:
|
||||
|
||||
1. 下载 GKI 构建文件
|
||||
|
||||
从我们的[资源部分](./links)查找 GKI 构建。查找您设备的内核版本并下载带有 `AnyKernel3` 后缀的 `zip` 文件。
|
||||
|
||||
2. 通过 Recovery 刷入
|
||||
|
||||
启动到 `TWRP recovery`,选择 **Install**,导航到下载的 `AnyKernel3 zip` 文件,滑动刷入后重启系统
|
||||
|
||||
3. [验证安装](#验证)
|
||||
|
||||
::: details 文件格式说明
|
||||
无后缀的 `.zip` 档案是未压缩的,`gz` 后缀是特定型号使用的压缩格式。
|
||||
:::
|
||||
|
||||
### 一加设备安装
|
||||
|
||||
1. 获取设备信息
|
||||
|
||||
- 内核版本(前两部分,例如 `5.10`、`5.15`、`6.1`、`6.6`)
|
||||
- 处理器代号(通常是不含数字的英文)
|
||||
- 来自一加开源内核仓库的分支和配置文件
|
||||
|
||||
2. 创建自定义构建
|
||||
|
||||
使用我们[资源部分](./links)中提到的链接,用您的设备信息创建自定义构建。
|
||||
|
||||
3. 刷入构建
|
||||
|
||||
下载生成的带有 AnyKernel3 后缀的 zip 文件,启动到 `TWRP recovery`,选择 **Install**,导航到下载的 `AnyKernel3 zip` 文件,滑动刷入后重启系统
|
||||
|
||||
::: tip
|
||||
您只需要填写内核版本的前两部分。自行搜索处理器代号
|
||||
:::
|
||||
|
||||
### 手动内核集成
|
||||
|
||||
面向希望将 SukiSU Ultra 集成到自己内核构建中的高级用户
|
||||
|
||||
#### 主分支(GKI)
|
||||
|
||||
```sh [bash]
|
||||
curl -LSs "https://raw.githubusercontent.com/SukiSU-Ultra/SukiSU-Ultra/main/kernel/setup.sh" | bash -s main
|
||||
```
|
||||
|
||||
#### 非 GKI 分支
|
||||
|
||||
```sh [bash]
|
||||
curl -LSs "https://raw.githubusercontent.com/SukiSU-Ultra/SukiSU-Ultra/main/kernel/setup.sh" | bash -s nongki
|
||||
```
|
||||
|
||||
#### SUSFS-Dev 分支(推荐)
|
||||
|
||||
```sh [bash]
|
||||
curl -LSs "https://raw.githubusercontent.com/SukiSU-Ultra/SukiSU-Ultra/main/kernel/setup.sh" | bash -s susfs-dev
|
||||
```
|
||||
|
||||
```bash
|
||||
curl -LSs "https://raw.githubusercontent.com/SukiSU-Ultra/SukiSU-Ultra/main/kernel/setup.sh" | bash -s susfs-main
|
||||
```
|
||||
|
||||
::: warning 必需的内核配置
|
||||
为了支持 KPM,添加 `CONFIG_KPM=y`
|
||||
|
||||
对于非 GKI 设备,还要添加 `CONFIG_KALLSYMS=y` 和 `CONFIG_KALLSYMS_ALL=y`
|
||||
:::
|
||||
|
||||
## 安装后设置
|
||||
|
||||
### 系统更新时保持 Root
|
||||
|
||||
> [!IMPORTANT]
|
||||
> 如何在 OTA 更新后保持 root 访问权限:
|
||||
|
||||
#### 步骤:
|
||||
|
||||
1. 系统更新后重启前
|
||||
- OTA 安装后不要立即重启,将 SukiSU Ultra 安装到第二插槽
|
||||
- 打开 SukiSU Ultra 管理器,在**刷入/修补内核**界面选择 **GKI/non_GKI install**,选择您的 `AnyKernel3` 内核 `zip` 文件,选择与当前运行插槽相对的插槽然后刷入重启。
|
||||
|
||||
2. 替代方案:LKM 模式
|
||||
|
||||
使用 [LKM 模式](#通用-gki) 在 OTA 后安装到未使用的插槽。
|
||||
|
||||
::: warning 警告
|
||||
**非 GKI 设备注意事项:** 此方法不支持所有非 GKI 设备。对于非 GKI 设备,使用 TWRP 是最安全的方法。
|
||||
:::
|
||||
|
||||
## 验证
|
||||
|
||||
安装后,验证一切是否正常工作,
|
||||
|
||||
- 打开 SukiSU Ultra 管理器检查 root 工作状态
|
||||
- 使用 root 权限的应用程序来验证 root 访问是否工作正常
|
||||
- 在设置 -> 关于手机中检查内核版本
|
||||
|
||||
## 需要帮助?
|
||||
|
||||
如果在安装过程中遇到问题:
|
||||
|
||||
1. 查看我们的[兼容性指南](./compatibility)了解设备要求
|
||||
2. 访问我们的 [GitHub 仓库](https://github.com/sukisu-ultra/sukisu-ultra)获取支持
|
||||
3. 加入我们的 [Telegram 社区](https://t.me/sukiksu)获取帮助
|
||||
|
||||
::: danger 安全提醒
|
||||
**始终有备用计划!** 保留您的原始 `boot.img/init_boot.img` 并知道如何在出现问题时恢复设备。
|
||||
:::
|
||||
77
Website/docs/zh/guide/license.md
Normal file
@@ -0,0 +1,77 @@
|
||||
# 许可证
|
||||
|
||||
## 软件许可
|
||||
|
||||
### 内核组件
|
||||
|
||||
::: info GPL-2.0 许可证
|
||||
"kernel" 目录中的文件采用 GPL-2.0-only 许可证
|
||||
:::
|
||||
|
||||
**许可证:** [GPL-2.0-only](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html)
|
||||
|
||||
### 应用程序核心
|
||||
|
||||
::: tip GPL-3.0 许可证
|
||||
所有其他部分(除下述特别说明外)采用 GPL-3.0 或更高版本许可证
|
||||
:::
|
||||
|
||||
**许可证:** [GPL-3.0 或更高版本](https://www.gnu.org/licenses/gpl-3.0.html)
|
||||
|
||||
## 艺术作品与品牌资产
|
||||
|
||||
### 启动器图标与角色艺术
|
||||
|
||||
::: warning 版权声明
|
||||
动画角色艺术作品有特殊许可要求
|
||||
:::
|
||||
|
||||
包含动画角色表情的文件 `ic_launcher(?!.*alt.*).*` 图像有特定的版权条款:
|
||||
|
||||
**版权持有者:**
|
||||
|
||||
- **动画角色艺术:** [怡子曰曰](https://space.bilibili.com/10545509)
|
||||
- **品牌知识产权:** [明风OuO](https://space.bilibili.com/274939213)
|
||||
- **矢量化制作:** @MiRinChan
|
||||
|
||||
**许可要求:**
|
||||
|
||||
1. **知识共享许可证:** [CC BY-NC-SA 4.0](https://creativecommons.org/licenses/by-nc-sa/4.0/legalcode.txt)
|
||||
2. **作者授权:** 需要获得两位版权持有者的授权
|
||||
3. **署名要求:** 必须署名上述所有贡献者
|
||||
|
||||
::: details 使用要求
|
||||
在使用这些艺术资产之前,您必须:
|
||||
|
||||
- 遵守知识共享署名-非商业性使用-相同方式共享 4.0 国际许可证
|
||||
- 获得两位原作者对艺术内容使用的授权
|
||||
- 为所有贡献者提供适当的署名
|
||||
:::
|
||||
|
||||
## 摘要
|
||||
|
||||
| 组件 | 许可证 | 备注 |
|
||||
| ---------------- | ------------------------------------------------------------------------- | ----------------------- |
|
||||
| **内核文件** | [GPL-2.0-only](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html) | `/kernel/` 目录中的文件 |
|
||||
| **应用程序代码** | [GPL-3.0+](https://www.gnu.org/licenses/gpl-3.0.html) | 主要应用程序组件 |
|
||||
| **角色艺术** | [CC BY-NC-SA 4.0](https://creativecommons.org/licenses/by-nc-sa/4.0/) | + 需要作者授权 |
|
||||
| **品牌资产** | 混合许可 | 查看具体署名要求 |
|
||||
|
||||
## 链接
|
||||
|
||||
- **GPL-2.0:** [完整许可证文本](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html)
|
||||
- **GPL-3.0:** [完整许可证文本](https://www.gnu.org/licenses/gpl-3.0.html)
|
||||
- **CC BY-NC-SA 4.0:** [完整许可证文本](https://creativecommons.org/licenses/by-nc-sa/4.0/legalcode.txt)
|
||||
|
||||
## 问题
|
||||
|
||||
关于许可证或使用权限的问题:
|
||||
|
||||
1. **代码许可:** 参考相应的 GPL 许可证条款
|
||||
2. **艺术作品使用:** 联系原作者获取授权
|
||||
3. **商业使用:** 查看 CC BY-NC-SA 4.0 限制条款
|
||||
4. **分发:** 确保遵守所有适用的许可证
|
||||
|
||||
::: tip 合规提示
|
||||
在重新分发或修改 SukiSU-Ultra 时,请确保遵守各个组件的所有适用许可证和署名要求。
|
||||
:::
|
||||
71
Website/docs/zh/guide/links.md
Normal file
@@ -0,0 +1,71 @@
|
||||
# 更多链接
|
||||
|
||||
## 参与翻译
|
||||
|
||||
::: info 贡献翻译
|
||||
如果您需要为管理器提交翻译,请访问我们的 Crowdin 项目
|
||||
:::
|
||||
|
||||
**翻译平台:** [Crowdin - SukiSU-Ultra](https://crowdin.com/project/SukiSU-Ultra)
|
||||
|
||||
## 项目与构建
|
||||
|
||||
基于 Sukisu 和 susfs 编译的项目:
|
||||
|
||||
### GKI 构建
|
||||
|
||||
::: tip 通用 GKI 支持
|
||||
集成 KernelSU 和 SUSFS 的通用内核映像构建
|
||||
:::
|
||||
|
||||
**仓库:** [GKI_KernelSU_SUSFS](https://github.com/ShirkNeko/GKI_KernelSU_SUSFS)
|
||||
|
||||
### OnePlus 构建
|
||||
|
||||
::: tip 设备特定构建
|
||||
带有 MKSU 和 SUSFS 的自动化 OnePlus 内核构建
|
||||
:::
|
||||
|
||||
**仓库:** [Action_OnePlus_MKSU_SUSFS](https://github.com/ShirkNeko/Action_OnePlus_MKSU_SUSFS)
|
||||
|
||||
## 社区与支持
|
||||
|
||||
### Telegram 社区
|
||||
|
||||
> 与其他用户联系,获取支持并保持更新
|
||||
|
||||
**主群组:** [Tg Group](https://t.me/sukiksu)
|
||||
|
||||
### 测试构建
|
||||
|
||||
::: warning 实验性构建
|
||||
测试构建是实验性的,可能不稳定
|
||||
:::
|
||||
|
||||
**测试构建频道:** [最新测试构建](https://t.me/Sukiksu/7114)
|
||||
|
||||
## 下载与发布
|
||||
|
||||
### 官方发布
|
||||
|
||||
> 从我们的 GitHub 发布页面下载最新稳定版本
|
||||
|
||||
**GitHub 发布:** [SukiSU-Ultra 发布](https://github.com/sukisu-ultra/sukisu-ultra/releases)
|
||||
|
||||
### 问题报告
|
||||
|
||||
> 在我们的 GitHub 仓库上报告错误或请求新功能
|
||||
|
||||
**GitHub 问题:** [报告问题](https://github.com/sukisu-ultra/sukisu-ultra/issues)
|
||||
|
||||
## 快速链接汇总
|
||||
|
||||
| 资源 | 链接 | 描述 |
|
||||
| ----------------- | :--------------------------------------------------------------------: | -----------: |
|
||||
| **翻译** | [Crowdin](https://crowdin.com/project/SukiSU-Ultra) | 提交翻译 |
|
||||
| **Telegram 群组** | [t.me/sukiksu](https://t.me/sukiksu) | 社区支持 |
|
||||
| **测试构建** | [测试频道](https://t.me/Sukiksu/7114) | 实验性构建 |
|
||||
| **发布** | [GitHub 发布](https://github.com/sukisu-ultra/sukisu-ultra/releases) | 稳定下载 |
|
||||
| **问题** | [GitHub 问题](https://github.com/sukisu-ultra/sukisu-ultra/issues) | 错误报告 |
|
||||
| **GKI 构建** | [GKI 仓库](https://github.com/ShirkNeko/GKI_KernelSU_SUSFS) | 通用构建 |
|
||||
| **OnePlus 构建** | [OnePlus 仓库](https://github.com/ShirkNeko/Action_OnePlus_MKSU_SUSFS) | 设备特定构建 |
|
||||
266
Website/docs/zh/guide/tracepoint-hook.md
Normal file
@@ -0,0 +1,266 @@
|
||||
# Tracepoint Hook 集成
|
||||
|
||||
## 介绍
|
||||
|
||||
自 commit [49b01aad](https://github.com/SukiSU-Ultra/SukiSU-Ultra/commit/49b01aad74bcca6dba5a8a2e053bb54b648eb124) 起,SukiSU 引入了 Tracepoint Hook
|
||||
|
||||
**该 Hook 理论上相比于 Kprobes Hook,性能开销更小,但次于 Manual Hook / Syscall Hook**
|
||||
|
||||
::: warning
|
||||
目前 Tracepoint Hook 在 6.x 设备上并不稳定,请勿使用,否则可能出现无法开机或无法获取 `ROOT` 权限等问题。
|
||||
:::
|
||||
|
||||
> [!NOTE]
|
||||
> 本教程参考了 [backslashxx/KernelSU#5](https://github.com/backslashxx/KernelSU/issues/5) 的 syscall hook v1.4 版本钩子,以及原版 KernelSU 的 [Manual Hook](https://kernelsu.org/guide/how-to-integrate-for-non-gki.html#manually-modify-the-kernel-source)
|
||||
|
||||
## 在内核中放置 TP 钩子
|
||||
|
||||
::: code-group
|
||||
|
||||
```diff[exec.c]
|
||||
--- a/fs/exec.c
|
||||
+++ b/fs/exec.c
|
||||
@@ -78,6 +78,10 @@
|
||||
#include <trace/hooks/sched.h>
|
||||
#endif
|
||||
|
||||
+#if defined(CONFIG_KSU) && defined(CONFIG_KSU_TRACEPOINT_HOOK)
|
||||
+#include <../drivers/kernelsu/ksu_trace.h>
|
||||
+#endif
|
||||
+
|
||||
EXPORT_TRACEPOINT_SYMBOL_GPL(task_rename);
|
||||
|
||||
static int bprm_creds_from_file(struct linux_binprm *bprm);
|
||||
@@ -2037,6 +2041,9 @@ static int do_execve(struct filename *filename,
|
||||
{
|
||||
struct user_arg_ptr argv = { .ptr.native = __argv };
|
||||
struct user_arg_ptr envp = { .ptr.native = __envp };
|
||||
+#if defined(CONFIG_KSU) && defined(CONFIG_KSU_TRACEPOINT_HOOK)
|
||||
+ trace_ksu_trace_execveat_hook((int *)AT_FDCWD, &filename, &argv, &envp, 0);
|
||||
+#endif
|
||||
return do_execveat_common(AT_FDCWD, filename, argv, envp, 0);
|
||||
}
|
||||
|
||||
@@ -2064,6 +2071,9 @@ static int compat_do_execve(struct filename *filename,
|
||||
.is_compat = true,
|
||||
.ptr.compat = __envp,
|
||||
};
|
||||
+#if defined(CONFIG_KSU) && defined(CONFIG_KSU_TRACEPOINT_HOOK)
|
||||
+ trace_ksu_trace_execveat_sucompat_hook((int *)AT_FDCWD, &filename, NULL, NULL, NULL); /* 32-bit su */
|
||||
+#endif
|
||||
return do_execveat_common(AT_FDCWD, filename, argv, envp, 0);
|
||||
}
|
||||
```
|
||||
|
||||
```diff[open.c]
|
||||
--- a/fs/open.c
|
||||
+++ b/fs/open.c
|
||||
@@ -37,6 +37,10 @@
|
||||
#include "internal.h"
|
||||
#include <trace/hooks/syscall_check.h>
|
||||
|
||||
+#if defined(CONFIG_KSU) && defined(CONFIG_KSU_TRACEPOINT_HOOK)
|
||||
+#include <../drivers/kernelsu/ksu_trace.h>
|
||||
+#endif
|
||||
+
|
||||
int do_truncate(struct user_namespace *mnt_userns, struct dentry *dentry,
|
||||
loff_t length, unsigned int time_attrs, struct file *filp)
|
||||
{
|
||||
@@ -468,6 +472,9 @@ static long do_faccessat(int dfd, const char __user *filename, int mode, int fla
|
||||
|
||||
SYSCALL_DEFINE3(faccessat, int, dfd, const char __user *, filename, int, mode)
|
||||
{
|
||||
+#if defined(CONFIG_KSU) && defined(CONFIG_KSU_TRACEPOINT_HOOK)
|
||||
+ trace_ksu_trace_faccessat_hook(&dfd, &filename, &mode, NULL);
|
||||
+#endif
|
||||
return do_faccessat(dfd, filename, mode, 0);
|
||||
}
|
||||
```
|
||||
|
||||
```diff[read_write.c]
|
||||
--- a/fs/read_write.c
|
||||
+++ b/fs/read_write.c
|
||||
@@ -25,6 +25,10 @@
|
||||
#include <linux/uaccess.h>
|
||||
#include <asm/unistd.h>
|
||||
|
||||
+#if defined(CONFIG_KSU) && defined(CONFIG_KSU_TRACEPOINT_HOOK)
|
||||
+#include <../drivers/kernelsu/ksu_trace.h>
|
||||
+#endif
|
||||
+
|
||||
const struct file_operations generic_ro_fops = {
|
||||
.llseek = generic_file_llseek,
|
||||
.read_iter = generic_file_read_iter,
|
||||
@@ -630,6 +634,9 @@ ssize_t ksys_read(unsigned int fd, char __user *buf, size_t count)
|
||||
|
||||
SYSCALL_DEFINE3(read, unsigned int, fd, char __user *, buf, size_t, count)
|
||||
{
|
||||
+#if defined(CONFIG_KSU) && defined(CONFIG_KSU_TRACEPOINT_HOOK)
|
||||
+ trace_ksu_trace_sys_read_hook(fd, &buf, &count);
|
||||
+#endif
|
||||
return ksys_read(fd, buf, count);
|
||||
}
|
||||
```
|
||||
|
||||
```diff[stat.c]
|
||||
--- a/fs/stat.c
|
||||
+++ b/fs/stat.c
|
||||
@@ -24,6 +24,10 @@
|
||||
#include "internal.h"
|
||||
#include "mount.h"
|
||||
|
||||
+#if defined(CONFIG_KSU) && defined(CONFIG_KSU_TRACEPOINT_HOOK)
|
||||
+#include <../drivers/kernelsu/ksu_trace.h>
|
||||
+#endif
|
||||
+
|
||||
/**
|
||||
* generic_fillattr - Fill in the basic attributes from the inode struct
|
||||
* @mnt_userns: user namespace of the mount the inode was found from
|
||||
@@ -408,6 +412,10 @@ SYSCALL_DEFINE4(newfstatat, int, dfd, const char __user *, filename,
|
||||
struct kstat stat;
|
||||
int error;
|
||||
|
||||
+#if defined(CONFIG_KSU) && defined(CONFIG_KSU_TRACEPOINT_HOOK)
|
||||
+ trace_ksu_trace_stat_hook(&dfd, &filename, &flag);
|
||||
+#endif
|
||||
+
|
||||
error = vfs_fstatat(dfd, filename, &stat, flag);
|
||||
if (error)
|
||||
return error;
|
||||
@@ -559,6 +567,10 @@ SYSCALL_DEFINE4(fstatat64, int, dfd, const char __user *, filename,
|
||||
struct kstat stat;
|
||||
int error;
|
||||
|
||||
+#if defined(CONFIG_KSU) && defined(CONFIG_KSU_TRACEPOINT_HOOK)
|
||||
+ trace_ksu_trace_stat_hook(&dfd, &filename, &flag); /* 32-bit su support */
|
||||
+#endif
|
||||
+
|
||||
error = vfs_fstatat(dfd, filename, &stat, flag);
|
||||
if (error)
|
||||
return error;
|
||||
```
|
||||
|
||||
:::
|
||||
通常是要改四个地方:
|
||||
|
||||
1. compat_do_execve,通常位于 `fs/exec.c`
|
||||
2. do_faccessat,通常位于 `/fs/open.c`
|
||||
3. sys_read,通常位于 `fs/read_write.c`
|
||||
4. newfstatat SYSCALL,通常位于 `fs/stat.c`
|
||||
|
||||
如果没有 do_faccessat 方法,可以找 faccessat 的 SYSCALL 定义(对于早于 4.17 的内核)
|
||||
|
||||
```diff
|
||||
--- a/fs/open.c
|
||||
+++ b/fs/open.c
|
||||
@@ -31,6 +31,9 @@
|
||||
#include <linux/ima.h>
|
||||
#include <linux/dnotify.h>
|
||||
#include <linux/compat.h>
|
||||
+#if defined(CONFIG_KSU) && defined(CONFIG_KSU_TRACEPOINT_HOOK)
|
||||
+#include <../drivers/kernelsu/ksu_trace.h>
|
||||
+#endif
|
||||
|
||||
#include "internal.h"
|
||||
|
||||
@@ -369,6 +372,9 @@ SYSCALL_DEFINE3(faccessat, int, dfd, const char __user *, filename, int, mode)
|
||||
int res;
|
||||
unsigned int lookup_flags = LOOKUP_FOLLOW;
|
||||
|
||||
+#if defined(CONFIG_KSU) && defined(CONFIG_KSU_TRACEPOINT_HOOK)
|
||||
+ trace_ksu_trace_faccessat_hook(&dfd, &filename, &mode, NULL);
|
||||
+#endif
|
||||
if (mode & ~S_IRWXO) /* where's F_OK, X_OK, W_OK, R_OK? */
|
||||
return -EINVAL;
|
||||
```
|
||||
|
||||
如果没有 sys_read 方法,并且 4.14 及以下需要修改 read 的 SYSCALL 定义
|
||||
|
||||
```diff
|
||||
--- a/fs/read_write.c
|
||||
+++ b/fs/read_write.c
|
||||
@@ -25,6 +25,11 @@
|
||||
#include <linux/uaccess.h>
|
||||
#include <asm/unistd.h>
|
||||
|
||||
+#if defined(CONFIG_KSU) && defined(CONFIG_KSU_TRACEPOINT_HOOK)
|
||||
+#include <../drivers/kernelsu/ksu_trace.h>
|
||||
+#endif
|
||||
+
|
||||
+
|
||||
const struct file_operations generic_ro_fops = {
|
||||
.llseek = generic_file_llseek,
|
||||
.read_iter = generic_file_read_iter,
|
||||
@@ -575,6 +580,9 @@ SYSCALL_DEFINE3(read, unsigned int, fd, char __user *, buf, size_t, count)
|
||||
|
||||
if (f.file) {
|
||||
loff_t pos = file_pos_read(f.file);
|
||||
+#if defined(CONFIG_KSU) && defined(CONFIG_KSU_TRACEPOINT_HOOK)
|
||||
+ trace_ksu_trace_sys_read_hook(fd, &buf, &count);
|
||||
+#endif
|
||||
ret = vfs_read(f.file, buf, count, &pos);
|
||||
if (ret >= 0)
|
||||
file_pos_write(f.file, pos);
|
||||
```
|
||||
|
||||
## 安全模式
|
||||
|
||||
要使用 KernelSU 内置的安全模式,你还需要修改 `drivers/input/input.c` 中的 input_handle_event 方法:
|
||||
|
||||
```diff
|
||||
--- a/drivers/input/input.c
|
||||
+++ b/drivers/input/input.c
|
||||
@@ -26,6 +26,10 @@
|
||||
#include "input-compat.h"
|
||||
#include "input-poller.h"
|
||||
|
||||
+#if defined(CONFIG_KSU) && defined(CONFIG_KSU_TRACEPOINT_HOOK)
|
||||
+#include <../../drivers/kernelsu/ksu_trace.h>
|
||||
+#endif
|
||||
+
|
||||
MODULE_AUTHOR("Vojtech Pavlik <vojtech@suse.cz>");
|
||||
MODULE_DESCRIPTION("Input core");
|
||||
MODULE_LICENSE("GPL");
|
||||
@@ -451,6 +455,10 @@ void input_event(struct input_dev *dev,
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
+#if defined(CONFIG_KSU) && defined(CONFIG_KSU_TRACEPOINT_HOOK)
|
||||
+ trace_ksu_trace_input_hook(&type, &code, &value);
|
||||
+#endif
|
||||
+
|
||||
if (is_event_supported(type, dev->evbit, EV_MAX)) {
|
||||
|
||||
spin_lock_irqsave(&dev->event_lock, flags);
|
||||
```
|
||||
|
||||
## pm 命令执行失败?
|
||||
|
||||
你需要修改 `drivers/tty/pty.c`
|
||||
|
||||
```diff
|
||||
--- a/drivers/tty/pty.c
|
||||
+++ b/drivers/tty/pty.c
|
||||
@@ -31,6 +31,10 @@
|
||||
#include <linux/compat.h>
|
||||
#include "tty.h"
|
||||
|
||||
+#if defined(CONFIG_KSU) && defined(CONFIG_KSU_TRACEPOINT_HOOK)
|
||||
+#include <../../drivers/kernelsu/ksu_trace.h>
|
||||
+#endif
|
||||
+
|
||||
#undef TTY_DEBUG_HANGUP
|
||||
#ifdef TTY_DEBUG_HANGUP
|
||||
# define tty_debug_hangup(tty, f, args...) tty_debug(tty, f, ##args)
|
||||
@@ -707,6 +711,10 @@ static struct tty_struct *pts_unix98_lookup(struct tty_driver *driver,
|
||||
{
|
||||
struct tty_struct *tty;
|
||||
|
||||
+#if defined(CONFIG_KSU) && defined(CONFIG_KSU_TRACEPOINT_HOOK)
|
||||
+ trace_ksu_trace_devpts_hook((struct inode *)file->f_path.dentry->d_inode);
|
||||
+#endif
|
||||
+
|
||||
mutex_lock(&devpts_mutex);
|
||||
tty = devpts_get_priv(file->f_path.dentry);
|
||||
mutex_unlock(&devpts_mutex);
|
||||
```
|
||||
37
Website/docs/zh/index.md
Normal file
@@ -0,0 +1,37 @@
|
||||
---
|
||||
layout: home
|
||||
|
||||
hero:
|
||||
name: 'SukiSU-Ultra'
|
||||
text: '下一代 Android Root 解决方案'
|
||||
tagline: Android 上的内核级的高级 root 方案
|
||||
image:
|
||||
src: /logo.svg
|
||||
alt: SukiSU-Ultra
|
||||
actions:
|
||||
- theme: brand
|
||||
text: 开始使用
|
||||
link: /zh/guide/
|
||||
- theme: alt
|
||||
text: 在 GitHub 上查看
|
||||
link: https://github.com/sukisu-ultra/sukisu-ultra
|
||||
|
||||
features:
|
||||
- title: 内核级的 su 和 root 权限管理
|
||||
details: 在内核进行安全的 root 权限管理。
|
||||
|
||||
- title: 不基于 OverlayFS 的模块系统
|
||||
details: 模块系统基于来自 5ec1cff 的 Magic Mount。
|
||||
|
||||
- title: App Profile
|
||||
details: 把 root 权限关进笼子里。
|
||||
|
||||
- title: 重新支持非 GKI 与 GKI 1.0 内核
|
||||
details: 增强对旧设备的兼容性。
|
||||
|
||||
- title: 更多自定义选项
|
||||
details: 提供广泛的自定义选项。
|
||||
|
||||
- title: 支持 KPM
|
||||
details: 完整的基于 KernelPatch 的 KPM 功能。
|
||||
---
|
||||
BIN
Website/favicon/apple-touch-icon.png
Normal file
|
After Width: | Height: | Size: 21 KiB |
BIN
Website/favicon/favicon-96x96.png
Normal file
|
After Width: | Height: | Size: 10 KiB |
BIN
Website/favicon/favicon.ico
Normal file
|
After Width: | Height: | Size: 144 KiB |
3
Website/favicon/favicon.svg
Normal file
|
After Width: | Height: | Size: 58 KiB |
26
Website/favicon/site.webmanifest
Normal file
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"name": "SukiSU",
|
||||
"short_name": "SukiSU",
|
||||
"icons": [
|
||||
{
|
||||
"src": "/favicon.svg",
|
||||
"type": "image/svg+xml",
|
||||
"purpose": "any"
|
||||
},
|
||||
{
|
||||
"src": "/web-app-manifest-192x192.png",
|
||||
"sizes": "192x192",
|
||||
"type": "image/png",
|
||||
"purpose": "any maskable"
|
||||
},
|
||||
{
|
||||
"src": "/web-app-manifest-512x512.png",
|
||||
"sizes": "512x512",
|
||||
"type": "image/png",
|
||||
"purpose": "any maskable"
|
||||
}
|
||||
],
|
||||
"theme_color": "#000000",
|
||||
"background_color": "#000000",
|
||||
"display": "standalone"
|
||||
}
|
||||
BIN
Website/favicon/web-app-manifest-192x192.png
Normal file
|
After Width: | Height: | Size: 22 KiB |
BIN
Website/favicon/web-app-manifest-512x512.png
Normal file
|
After Width: | Height: | Size: 66 KiB |
57
Website/package.json
Normal file
@@ -0,0 +1,57 @@
|
||||
{
|
||||
"name": "sukisu-ultra-docs",
|
||||
"version": "2.0.0",
|
||||
"description": "SukiSU-Ultra Documentation - Next-Generation Android Root Solution",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "tsx scripts/rebuild-ico.ts && tsx scripts/sync-favicons.ts && vitepress dev docs --host",
|
||||
"build": "tsx scripts/sync-favicons.ts && vitepress build docs && pnpm build:sw:dist",
|
||||
"preview": "vitepress preview docs",
|
||||
"build:sw": "tsc scripts/sw.ts --target ES2022 --lib ES2022,WebWorker --outDir docs/public --skipLibCheck",
|
||||
"build:sw:dist": "tsc scripts/sw.ts --target ES2022 --lib ES2022,WebWorker --outDir docs/.vitepress/dist --skipLibCheck",
|
||||
"build:scripts": "pnpm build:sw",
|
||||
"type-check": "tsc --noEmit",
|
||||
"format": "prettier --write .",
|
||||
"format:check": "prettier --check ."
|
||||
},
|
||||
"keywords": [
|
||||
"android",
|
||||
"root",
|
||||
"kernelsu",
|
||||
"sukisu",
|
||||
"documentation",
|
||||
"vitepress"
|
||||
],
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/sukisu-ultra/sukisu-ultra.git"
|
||||
},
|
||||
"homepage": "https://sukisu.org",
|
||||
"bugs": {
|
||||
"url": "https://github.com/sukisu-ultra/sukisu-ultra/issues"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^24.3.0",
|
||||
"@types/serviceworker": "^0.0.152",
|
||||
"markdown-it-footnote": "^4.0.0",
|
||||
"markdown-it-mark": "^4.0.0",
|
||||
"markdown-it-mathjax3": "^4.3.2",
|
||||
"markdown-it-sub": "^2.0.0",
|
||||
"markdown-it-task-lists": "^2.1.1",
|
||||
"prettier": "^3.6.2",
|
||||
"terser": "^5.44.0",
|
||||
"ts-node": "^10.9.2",
|
||||
"tsx": "^4.20.5",
|
||||
"typescript": "^5.9.2",
|
||||
"vitepress": "1.6.4",
|
||||
"vue": "^3.5.21"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=22"
|
||||
},
|
||||
"packageManager": "pnpm@10.17.1",
|
||||
"dependencies": {
|
||||
"@nolebase/vitepress-plugin-git-changelog": "^2.18.2",
|
||||
"vitepress-plugin-group-icons": "^1.6.3"
|
||||
}
|
||||
}
|
||||
6
Website/pnpm-workspace.yaml
Normal file
@@ -0,0 +1,6 @@
|
||||
packages:
|
||||
- docs
|
||||
- .
|
||||
|
||||
onlyBuiltDependencies:
|
||||
- esbuild
|
||||
207
Website/scripts/sw.ts
Normal file
@@ -0,0 +1,207 @@
|
||||
/// <reference lib="webworker" />
|
||||
|
||||
const CACHE_NAME: string = 'sukisu-ultra-v2.0'
|
||||
const STATIC_CACHE: string = 'sukisu-static-v2.0'
|
||||
const RUNTIME_CACHE: string = 'sukisu-runtime-v2.0'
|
||||
const IMAGE_CACHE: string = 'sukisu-images-v2.0'
|
||||
|
||||
const CRITICAL_ASSETS: string[] = [
|
||||
'/',
|
||||
'/guide/',
|
||||
'/guide/installation',
|
||||
'/guide/compatibility',
|
||||
'/zh/',
|
||||
'/zh/guide/',
|
||||
'/logo.svg',
|
||||
'/favicon.ico',
|
||||
'/offline.html',
|
||||
]
|
||||
|
||||
const CACHE_STRATEGIES: {
|
||||
static: string[]
|
||||
images: string[]
|
||||
documents: string[]
|
||||
} = {
|
||||
static: ['.css', '.js', '.woff2', '.woff', '.ttf', '.otf'],
|
||||
images: ['.png', '.jpg', '.jpeg', '.svg', '.webp', '.avif', '.gif', '.ico'],
|
||||
documents: ['.html', '.json', '.xml'],
|
||||
}
|
||||
|
||||
self.addEventListener('install', (event: ExtendableEvent) => {
|
||||
event.waitUntil(
|
||||
(async (): Promise<void> => {
|
||||
const cache: Cache = await caches.open(STATIC_CACHE)
|
||||
try {
|
||||
await cache.addAll(CRITICAL_ASSETS)
|
||||
} catch (error) {
|
||||
console.warn('Failed to cache some assets:', error)
|
||||
}
|
||||
;(self as any).skipWaiting()
|
||||
})()
|
||||
)
|
||||
})
|
||||
|
||||
self.addEventListener('activate', (event: ExtendableEvent) => {
|
||||
event.waitUntil(
|
||||
(async (): Promise<void> => {
|
||||
const cacheNames: string[] = await caches.keys()
|
||||
const validCaches: string[] = [STATIC_CACHE, RUNTIME_CACHE, IMAGE_CACHE, CACHE_NAME]
|
||||
|
||||
await Promise.all(
|
||||
cacheNames
|
||||
.filter((name: string) => !validCaches.includes(name))
|
||||
.map((name: string) => caches.delete(name))
|
||||
)
|
||||
;(self as any).clients.claim()
|
||||
})()
|
||||
)
|
||||
})
|
||||
|
||||
self.addEventListener('fetch', (event: FetchEvent) => {
|
||||
const { request } = event
|
||||
const url: URL = new URL(request.url)
|
||||
|
||||
if (request.method !== 'GET' || url.origin !== location.origin) return
|
||||
if (url.pathname.startsWith('/api/')) return
|
||||
|
||||
event.respondWith(handleRequest(request, url))
|
||||
})
|
||||
|
||||
async function handleRequest(request: Request, url: URL): Promise<Response> {
|
||||
const isStatic: boolean = CACHE_STRATEGIES.static.some((ext: string) =>
|
||||
url.pathname.endsWith(ext)
|
||||
)
|
||||
const isImage: boolean = CACHE_STRATEGIES.images.some((ext: string) => url.pathname.endsWith(ext))
|
||||
const isDocument: boolean =
|
||||
CACHE_STRATEGIES.documents.some((ext: string) => url.pathname.endsWith(ext)) ||
|
||||
url.pathname.endsWith('/')
|
||||
|
||||
try {
|
||||
if (isStatic) {
|
||||
return await handleStatic(request)
|
||||
} else if (isImage) {
|
||||
return await handleImage(request)
|
||||
} else if (isDocument) {
|
||||
return await handleDocument(request)
|
||||
} else {
|
||||
return await handleDefault(request)
|
||||
}
|
||||
} catch (error) {
|
||||
return await handleOffline(request)
|
||||
}
|
||||
}
|
||||
|
||||
async function handleStatic(request: Request): Promise<Response> {
|
||||
const cache: Cache = await caches.open(STATIC_CACHE)
|
||||
const cached: Response | undefined = await cache.match(request)
|
||||
|
||||
if (cached) {
|
||||
fetch(request)
|
||||
.then((response: Response) => {
|
||||
if (response.ok) cache.put(request, response.clone())
|
||||
})
|
||||
.catch(() => {})
|
||||
|
||||
return cached
|
||||
}
|
||||
|
||||
const response: Response = await fetch(request)
|
||||
if (response.ok) {
|
||||
cache.put(request, response.clone())
|
||||
}
|
||||
return response
|
||||
}
|
||||
|
||||
async function handleImage(request: Request): Promise<Response> {
|
||||
const cache: Cache = await caches.open(IMAGE_CACHE)
|
||||
const cached: Response | undefined = await cache.match(request)
|
||||
|
||||
if (cached) return cached
|
||||
|
||||
const response: Response = await fetch(request)
|
||||
if (response.ok) {
|
||||
cache.put(request, response.clone())
|
||||
}
|
||||
return response
|
||||
}
|
||||
|
||||
async function handleDocument(request: Request): Promise<Response> {
|
||||
const cache: Cache = await caches.open(RUNTIME_CACHE)
|
||||
|
||||
try {
|
||||
const response: Response = await fetch(request)
|
||||
if (response.ok) {
|
||||
cache.put(request, response.clone())
|
||||
}
|
||||
return response
|
||||
} catch (error) {
|
||||
const cached: Response | undefined = await cache.match(request)
|
||||
if (cached) return cached
|
||||
|
||||
if (request.mode === 'navigate') {
|
||||
const offlinePage: Response | undefined = await caches.match('/offline.html')
|
||||
if (offlinePage) return offlinePage
|
||||
}
|
||||
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
async function handleDefault(request: Request): Promise<Response> {
|
||||
const cache: Cache = await caches.open(RUNTIME_CACHE)
|
||||
|
||||
try {
|
||||
const response: Response = await fetch(request)
|
||||
if (response.ok) {
|
||||
cache.put(request, response.clone())
|
||||
}
|
||||
return response
|
||||
} catch (error) {
|
||||
const cached: Response | undefined = await cache.match(request)
|
||||
if (cached) return cached
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
async function handleOffline(request: Request): Promise<Response> {
|
||||
const cache: Cache = await caches.open(STATIC_CACHE)
|
||||
|
||||
if (request.mode === 'navigate') {
|
||||
const offlinePage: Response | undefined = await caches.match('/offline.html')
|
||||
if (offlinePage) return offlinePage
|
||||
}
|
||||
|
||||
return new Response('Offline', {
|
||||
status: 503,
|
||||
statusText: 'Service Unavailable',
|
||||
headers: new Headers({
|
||||
'Content-Type': 'text/plain',
|
||||
}),
|
||||
})
|
||||
}
|
||||
|
||||
self.addEventListener('sync', (event: any) => {
|
||||
if (event.tag === 'data-sync') {
|
||||
event.waitUntil(syncData())
|
||||
}
|
||||
})
|
||||
|
||||
async function syncData(): Promise<void> {
|
||||
try {
|
||||
console.log('Syncing application data...')
|
||||
} catch (error) {
|
||||
console.error('Data sync failed:', error)
|
||||
}
|
||||
}
|
||||
|
||||
interface PerformanceMessage {
|
||||
type: string
|
||||
name: string
|
||||
}
|
||||
|
||||
self.addEventListener('message', (event: MessageEvent) => {
|
||||
const data = event.data as PerformanceMessage
|
||||
if (data && data.type === 'PERFORMANCE_MARK') {
|
||||
performance.mark(data.name)
|
||||
}
|
||||
})
|
||||
38
Website/scripts/sync-favicons.ts
Normal file
@@ -0,0 +1,38 @@
|
||||
import fs from 'fs'
|
||||
import path from 'path'
|
||||
import { fileURLToPath } from 'url'
|
||||
|
||||
const __filename = fileURLToPath(import.meta.url)
|
||||
const __dirname = path.dirname(__filename)
|
||||
|
||||
const srcDir = path.join(__dirname, '../favicon')
|
||||
const dstDir = path.join(__dirname, '../docs/public')
|
||||
|
||||
function ensureDir(dir: string) {
|
||||
if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true })
|
||||
}
|
||||
|
||||
function copyAll(src: string, dst: string) {
|
||||
ensureDir(dst)
|
||||
const entries = fs.readdirSync(src, { withFileTypes: true })
|
||||
let count = 0
|
||||
for (const entry of entries) {
|
||||
const s = path.join(src, entry.name)
|
||||
const d = path.join(dst, entry.name)
|
||||
if (entry.isDirectory()) {
|
||||
copyAll(s, d)
|
||||
} else if (entry.isFile()) {
|
||||
fs.copyFileSync(s, d)
|
||||
count++
|
||||
}
|
||||
}
|
||||
return count
|
||||
}
|
||||
|
||||
if (!fs.existsSync(srcDir)) {
|
||||
console.warn('Favicons source folder not found:', srcDir)
|
||||
process.exit(0)
|
||||
}
|
||||
|
||||
const copied = copyAll(srcDir, dstDir)
|
||||
console.log(`✅ Synced ${copied} favicon file(s) from \'favicon\' to docs/public`)
|
||||
25
Website/tsconfig.json
Normal file
@@ -0,0 +1,25 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "ES2022",
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "node",
|
||||
"esModuleInterop": true,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"strict": true,
|
||||
"skipLibCheck": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"resolveJsonModule": true,
|
||||
"declaration": true,
|
||||
"outDir": "./dist",
|
||||
"rootDir": "./",
|
||||
"types": ["node", "serviceworker"],
|
||||
"lib": ["ES2022", "WebWorker"],
|
||||
"ignoreDeprecations": "5.0"
|
||||
},
|
||||
"ts-node": {
|
||||
"esm": true,
|
||||
"experimentalSpecifierResolution": "node"
|
||||
},
|
||||
"include": ["scripts/**/*"],
|
||||
"exclude": ["node_modules", "dist", "docs/.vitepress/dist"]
|
||||
}
|
||||
11
Website/wrangler.toml
Normal file
@@ -0,0 +1,11 @@
|
||||
# Cloudflare Pages Configuration for SukiSU-Ultra
|
||||
name = "sukisu-ultra-docs"
|
||||
compatibility_date = "2025-08-04"
|
||||
compatibility_flags = ["nodejs_compat"]
|
||||
pages_build_output_dir = "docs/.vitepress/dist"
|
||||
|
||||
[env.production]
|
||||
name = "sukisu-ultra-docs"
|
||||
|
||||
[env.preview]
|
||||
name = "sukisu-ultra-docs-preview"
|
||||
@@ -1,6 +1,6 @@
|
||||
<img align='right' src='zakomonochrome-128.svg' width='100px' alt="logo">
|
||||
|
||||
# SukiSU Ultra
|
||||
<img align='right' src='SukiSU-mini.svg' width='220px' alt="sukisu logo">
|
||||
|
||||
|
||||
**English** | [简体中文](./zh/README.md) | [日本語](./ja/README.md) | [Türkçe](./tr/README.md)
|
||||
|
||||
|
||||
183
docs/SukiSU-mini.svg
Normal file
|
After Width: | Height: | Size: 185 KiB |
188
docs/SukiSU.svg
Normal file
|
After Width: | Height: | Size: 200 KiB |
@@ -1,4 +1,6 @@
|
||||
# SukiSU Ultra
|
||||
<img align='right' src='SukiSU-mini.svg' width='220px' alt="sukisu logo">
|
||||
|
||||
|
||||
[English](../README.md) | [简体中文](../zh/README.md) | **日本語** | [Türkçe](../tr/README.md)
|
||||
|
||||
|
||||
183
docs/ja/SukiSU-mini.svg
Normal file
|
After Width: | Height: | Size: 185 KiB |
@@ -1,4 +1,6 @@
|
||||
# SukiSU Ultra
|
||||
<img align='right' src='SukiSU-mini.svg' width='250px' alt="sukisu logo">
|
||||
|
||||
|
||||
[English](../README.md) | [简体中文](../zh/README.md) | [日本語](../ja/README.md) | **Türkçe**
|
||||
|
||||
|
||||
183
docs/tr/SukiSU-mini.svg
Normal file
|
After Width: | Height: | Size: 185 KiB |
@@ -1,6 +1,6 @@
|
||||
<img align='right' src='zakomonochrome-128.svg' width='100px' alt="logo">
|
||||
|
||||
# SukiSU Ultra
|
||||
<img align='right' src='SukiSU-mini.svg' width='220px' alt="sukisu logo">
|
||||
|
||||
|
||||
[English](../README.md) | **简体中文** | [日本語](../ja/README.md) | [Türkçe](../tr/README.md)
|
||||
|
||||
|
||||
183
docs/zh/SukiSU-mini.svg
Normal file
|
After Width: | Height: | Size: 185 KiB |
188
docs/zh/SukiSU.svg
Normal file
|
After Width: | Height: | Size: 200 KiB |
@@ -1,65 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
width="128"
|
||||
height="128"
|
||||
viewBox="0 0 128 128"
|
||||
version="1.1"
|
||||
id="svg1"
|
||||
inkscape:version="1.4.2 (ebf0e940d0, 2025-05-08)"
|
||||
sodipodi:docname="zakomonochrome-128.svg"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<sodipodi:namedview
|
||||
id="namedview1"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#999999"
|
||||
borderopacity="1"
|
||||
inkscape:showpageshadow="2"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#d1d1d1"
|
||||
inkscape:document-units="px"
|
||||
inkscape:zoom="2.6185048"
|
||||
inkscape:cx="59.957881"
|
||||
inkscape:cy="71.032903"
|
||||
inkscape:window-width="1280"
|
||||
inkscape:window-height="696"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="layer1" />
|
||||
<defs
|
||||
id="defs1" />
|
||||
<g
|
||||
inkscape:label="图层 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1">
|
||||
<rect
|
||||
style="fill:#ffffff;stroke:#000000;stroke-width:0;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.3;stroke-dasharray:none;paint-order:fill markers stroke;fill-opacity:1"
|
||||
id="rect1"
|
||||
width="128"
|
||||
height="128"
|
||||
x="0"
|
||||
y="0"
|
||||
rx="7.772471"
|
||||
ry="7.772471" />
|
||||
<path
|
||||
id="path101"
|
||||
style="fill:#ffffff;fill-opacity:0.734285;stroke:#000000;stroke-width:4.27504;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
||||
d="m 42.510282,81.796052 c 0,0 -7.224141,-5.638356 -10.043315,-9.338525 M 14.847106,81.97224 25.41902,71.576535 m 0.17619,-6.695549 2.819179,19.910444 M 11.675534,73.338532 38.281518,71.047931 M 43.567475,62.7666 34.40515,62.942814 M 34.22896,62.590425 33.524162,48.494537 m -18.500855,1.58577 17.972249,-1.409582 m -11.8053,-5.462154 0.352397,18.853251"
|
||||
inkscape:label="杂" />
|
||||
<path
|
||||
id="path111"
|
||||
style="fill:#ffffff;fill-opacity:0.734285;stroke:#000000;stroke-width:3.94824;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
||||
d="M 55.912937,82.876745 79.671596,81.412163 M 59.330273,75.391135 74.952411,74.089291 m -9.43837,-14.157553 1.139102,14.645756 m -8.299247,-7.160159 16.273048,-1.464569 m 0.650926,8.136525 0.325472,-14.808482 m -0.162747,0.162739 -17.900363,0.976379 m 0,-0.162738 1.952774,14.645756 m 12.042061,-21.154974 1.464576,-6.346492 m 0,-0.650928 -12.042063,0.650928 m -0.650918,6.509218 0.325459,-8.787441"
|
||||
inkscape:label="鱼" />
|
||||
<path
|
||||
d="m 95.08569,51.121163 c -1.90515,0.116064 -3.64694,0.97349 -4.86738,2.391307 -1.34538,1.56738 -1.91476,3.733159 -1.59523,6.070852 0.40842,2.982962 2.1502,6.17135 5.13887,9.411078 0.63424,0.68546 1.08109,1.129773 1.98202,1.967071 1.58321,1.469144 3.01507,2.634638 4.9875,4.052454 0.70392,0.50905 2.09253,1.453525 2.61627,1.781734 l 0.15133,0.09594 0.22103,-0.140663 c 0.80481,-0.515755 2.23909,-1.504852 3.08956,-2.130057 3.21689,-2.364488 5.79232,-4.737902 7.70228,-7.100167 3.09676,-3.831409 4.4133,-7.562359 3.80549,-10.773058 -0.42043,-2.210414 -1.82588,-4.039057 -3.81992,-4.967887 -0.85767,-0.399664 -1.69132,-0.607312 -2.6355,-0.656431 -1.22285,-0.0647 -2.42648,0.178619 -3.57485,0.721182 -1.95561,0.922124 -3.58927,2.719503 -4.61752,5.081755 -0.072,0.165235 -0.1394,0.310355 -0.14895,0.319295 -0.0312,0.02902 -0.0648,-0.02679 -0.19458,-0.330457 -0.30752,-0.714476 -0.91055,-1.752718 -1.38382,-2.377871 -0.4853,-0.645282 -1.2661,-1.431214 -1.84749,-1.862143 -1.50155,-1.114153 -3.26013,-1.658924 -5.00914,-1.553996 z"
|
||||
id="path1-4"
|
||||
style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.00231605" />
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 3.9 KiB |
@@ -8,6 +8,7 @@ kernelsu-objs += core_hook.o
|
||||
kernelsu-objs += ksud.o
|
||||
kernelsu-objs += embed_ksud.o
|
||||
kernelsu-objs += kernel_compat.o
|
||||
kernelsu-objs += throne_comm.o
|
||||
|
||||
ifeq ($(CONFIG_KSU_TRACEPOINT_HOOK), y)
|
||||
kernelsu-objs += ksu_trace.o
|
||||
@@ -28,7 +29,7 @@ obj-$(CONFIG_KPM) += kpm/
|
||||
REPO_OWNER := SukiSU-Ultra
|
||||
REPO_NAME := SukiSU-Ultra
|
||||
REPO_BRANCH := main
|
||||
KSU_VERSION_API := 3.1.9
|
||||
KSU_VERSION_API := 3.2.0
|
||||
|
||||
GIT_BIN := /usr/bin/env PATH="$$PATH":/usr/bin:/usr/local/bin git
|
||||
CURL_BIN := /usr/bin/env PATH="$$PATH":/usr/bin:/usr/local/bin curl
|
||||
@@ -94,11 +95,11 @@ endif
|
||||
|
||||
$(info -- Supported Unofficial Manager: 5ec1cff (GKI) ShirkNeko udochina (GKI and KPM))
|
||||
|
||||
ifeq ($(strip $(CONFIG_KSU_KPROBES_HOOK)),y)
|
||||
ifeq ($(CONFIG_KSU_KPROBES_HOOK), y)
|
||||
$(info -- SukiSU: CONFIG_KSU_KPROBES_HOOK)
|
||||
else ifeq ($(strip $(CONFIG_KSU_TRACEPOINT_HOOK)),y)
|
||||
else ifeq ($(CONFIG_KSU_TRACEPOINT_HOOK), y)
|
||||
$(info -- SukiSU: CONFIG_KSU_TRACEPOINT_HOOK)
|
||||
else ifeq ($(strip $(CONFIG_KSU_MANUAL_HOOK)),y)
|
||||
else ifeq ($(CONFIG_KSU_MANUAL_HOOK), y)
|
||||
$(info -- SukiSU: CONFIG_KSU_MANUAL_HOOK)
|
||||
endif
|
||||
|
||||
@@ -117,7 +118,7 @@ $(info -- KERNEL_VERSION: $(KERNEL_VERSION))
|
||||
$(info -- KERNEL_TYPE: $(KERNEL_TYPE))
|
||||
|
||||
$(info -- KERNEL_VERSION: $(KERNEL_VERSION))
|
||||
ifeq ($(CONFIG_KPM),y)
|
||||
ifeq ($(CONFIG_KPM), y)
|
||||
$(info -- KPM is enabled)
|
||||
else
|
||||
$(info -- KPM is disabled)
|
||||
|
||||
@@ -30,7 +30,6 @@ static struct apk_sign_key {
|
||||
const char *sha256;
|
||||
} apk_sign_keys[] = {
|
||||
{EXPECTED_SIZE_SHIRKNEKO, EXPECTED_HASH_SHIRKNEKO}, // ShirkNeko/SukiSU
|
||||
{EXPECTED_SIZE_OTHER, EXPECTED_HASH_OTHER}, // Dynamic Sign
|
||||
#ifdef EXPECTED_SIZE
|
||||
{EXPECTED_SIZE, EXPECTED_HASH}, // Custom
|
||||
#endif
|
||||
@@ -82,6 +81,54 @@ static int ksu_sha256(const unsigned char *data, unsigned int datalen,
|
||||
crypto_free_shash(alg);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
static struct dynamic_sign_key dynamic_sign = DYNAMIC_SIGN_DEFAULT_CONFIG;
|
||||
|
||||
static bool check_dynamic_sign(struct file *fp, u32 size4, loff_t *pos, int *matched_index)
|
||||
{
|
||||
struct dynamic_sign_key current_dynamic_key = dynamic_sign;
|
||||
|
||||
if (ksu_get_dynamic_manager_config(¤t_dynamic_key.size, ¤t_dynamic_key.hash)) {
|
||||
pr_debug("Using dynamic manager config: size=0x%x, hash=%.16s...\n",
|
||||
current_dynamic_key.size, current_dynamic_key.hash);
|
||||
}
|
||||
|
||||
if (size4 != current_dynamic_key.size) {
|
||||
return false;
|
||||
}
|
||||
|
||||
#define CERT_MAX_LENGTH 1024
|
||||
char cert[CERT_MAX_LENGTH];
|
||||
if (size4 > CERT_MAX_LENGTH) {
|
||||
pr_info("cert length overlimit\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
ksu_kernel_read_compat(fp, cert, size4, pos);
|
||||
|
||||
unsigned char digest[SHA256_DIGEST_SIZE];
|
||||
if (ksu_sha256(cert, size4, digest) < 0) {
|
||||
pr_info("sha256 error\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
char hash_str[SHA256_DIGEST_SIZE * 2 + 1];
|
||||
hash_str[SHA256_DIGEST_SIZE * 2] = '\0';
|
||||
bin2hex(hash_str, digest, SHA256_DIGEST_SIZE);
|
||||
|
||||
pr_info("sha256: %s, expected: %s, index: dynamic\n", hash_str, current_dynamic_key.hash);
|
||||
|
||||
if (strcmp(current_dynamic_key.hash, hash_str) == 0) {
|
||||
if (matched_index) {
|
||||
*matched_index = DYNAMIC_SIGN_INDEX;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool check_block(struct file *fp, u32 *size4, loff_t *pos, u32 *offset, int *matched_index)
|
||||
{
|
||||
int i;
|
||||
@@ -103,18 +150,18 @@ static bool check_block(struct file *fp, u32 *size4, loff_t *pos, u32 *offset, i
|
||||
ksu_kernel_read_compat(fp, size4, 0x4, pos); // certificate length
|
||||
*offset += 0x4 * 2;
|
||||
|
||||
if (ksu_is_dynamic_manager_enabled()) {
|
||||
loff_t temp_pos = *pos;
|
||||
if (check_dynamic_sign(fp, *size4, &temp_pos, matched_index)) {
|
||||
*pos = temp_pos;
|
||||
*offset += *size4;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(apk_sign_keys); i++) {
|
||||
sign_key = apk_sign_keys[i];
|
||||
|
||||
if (i == 1) { // Dynamic Sign indexing
|
||||
unsigned int size;
|
||||
const char *hash;
|
||||
if (ksu_get_dynamic_manager_config(&size, &hash)) {
|
||||
sign_key.size = size;
|
||||
sign_key.sha256 = hash;
|
||||
}
|
||||
}
|
||||
|
||||
if (*size4 != sign_key.size)
|
||||
continue;
|
||||
*offset += *size4;
|
||||
@@ -326,8 +373,8 @@ clean:
|
||||
}
|
||||
|
||||
if (check_multi_manager) {
|
||||
// 0: ShirkNeko/SukiSU, 1: Dynamic Sign
|
||||
if (matched_index == 0 || matched_index == 1) {
|
||||
// 0: ShirkNeko/SukiSU, DYNAMIC_SIGN_INDEX : Dynamic Sign
|
||||
if (matched_index == 0 || matched_index == DYNAMIC_SIGN_INDEX) {
|
||||
pr_info("Multi-manager APK detected (dynamic_manager enabled): signature_index=%d\n", matched_index);
|
||||
return true;
|
||||
}
|
||||
@@ -369,7 +416,7 @@ bool is_manager_apk(char *path)
|
||||
return check_v2_signature(path, false, NULL);
|
||||
}
|
||||
|
||||
bool ksu_is_dynamic_manager_apk(char *path, int *signature_index)
|
||||
bool is_dynamic_manager_apk(char *path, int *signature_index)
|
||||
{
|
||||
return check_v2_signature(path, true, signature_index);
|
||||
}
|
||||
@@ -6,4 +6,6 @@
|
||||
|
||||
bool is_manager_apk(char *path);
|
||||
|
||||
bool is_dynamic_manager_apk(char *path, int *signature_index);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
#include <linux/uidgid.h>
|
||||
#include <linux/version.h>
|
||||
#include <linux/mount.h>
|
||||
#include <linux/binfmts.h>
|
||||
|
||||
#include <linux/fs.h>
|
||||
#include <linux/namei.h>
|
||||
@@ -42,6 +43,7 @@
|
||||
#include "manager.h"
|
||||
#include "selinux/selinux.h"
|
||||
#include "throne_tracker.h"
|
||||
#include "throne_comm.h"
|
||||
#include "kernel_compat.h"
|
||||
|
||||
#include "kpm/kpm.h"
|
||||
@@ -64,7 +66,7 @@ static inline bool is_allow_su()
|
||||
return ksu_is_allow_uid(current_uid().val);
|
||||
}
|
||||
|
||||
static inline bool is_unsupported_uid(uid_t uid)
|
||||
static inline bool is_unsupported_app_uid(uid_t uid)
|
||||
{
|
||||
#define LAST_APPLICATION_UID 19999
|
||||
uid_t appid = uid % 100000;
|
||||
@@ -224,6 +226,9 @@ int ksu_handle_rename(struct dentry *old_dentry, struct dentry *new_dentry)
|
||||
|
||||
track_throne();
|
||||
|
||||
// Also request userspace scan for next time
|
||||
ksu_request_userspace_scan();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -407,6 +412,8 @@ int ksu_handle_prctl(int option, unsigned long arg2, unsigned long arg3,
|
||||
post_fs_data_lock = true;
|
||||
pr_info("post-fs-data triggered\n");
|
||||
on_post_fs_data();
|
||||
// Initialize throne communication
|
||||
ksu_throne_comm_init();
|
||||
// Initializing Dynamic Signatures
|
||||
ksu_dynamic_manager_init();
|
||||
pr_info("Dynamic sign config loaded during post-fs-data\n");
|
||||
@@ -621,14 +628,13 @@ int ksu_handle_prctl(int option, unsigned long arg2, unsigned long arg3,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool is_appuid(kuid_t uid)
|
||||
static bool is_non_appuid(kuid_t uid)
|
||||
{
|
||||
#define PER_USER_RANGE 100000
|
||||
#define FIRST_APPLICATION_UID 10000
|
||||
#define LAST_APPLICATION_UID 19999
|
||||
|
||||
uid_t appid = uid.val % PER_USER_RANGE;
|
||||
return appid >= FIRST_APPLICATION_UID && appid <= LAST_APPLICATION_UID;
|
||||
return appid < FIRST_APPLICATION_UID;
|
||||
}
|
||||
|
||||
static bool should_umount(struct path *path)
|
||||
@@ -700,13 +706,25 @@ int ksu_handle_setuid(struct cred *new, const struct cred *old)
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!is_appuid(new_uid) || is_unsupported_uid(new_uid.val)) {
|
||||
// pr_info("handle setuid ignore non application or isolated uid: %d\n", new_uid.val);
|
||||
if (is_non_appuid(new_uid)) {
|
||||
#ifdef CONFIG_KSU_DEBUG
|
||||
pr_info("handle setuid ignore non application uid: %d\n", new_uid.val);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
// isolated process may be directly forked from zygote, always unmount
|
||||
if (is_unsupported_app_uid(new_uid.val)) {
|
||||
#ifdef CONFIG_KSU_DEBUG
|
||||
pr_info("handle umount for unsupported application uid: %d\n", new_uid.val);
|
||||
#endif
|
||||
goto do_umount;
|
||||
}
|
||||
|
||||
if (ksu_is_allow_uid(new_uid.val)) {
|
||||
// pr_info("handle setuid ignore allowed application: %d\n", new_uid.val);
|
||||
#ifdef CONFIG_KSU_DEBUG
|
||||
pr_info("handle setuid ignore allowed application: %d\n", new_uid.val);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -718,11 +736,11 @@ int ksu_handle_setuid(struct cred *new, const struct cred *old)
|
||||
#endif
|
||||
}
|
||||
|
||||
do_umount:
|
||||
// check old process's selinux context, if it is not zygote, ignore it!
|
||||
// because some su apps may setuid to untrusted_app but they are in global mount namespace
|
||||
// when we umount for such process, that is a disaster!
|
||||
bool is_zygote_child = is_zygote(old->security);
|
||||
if (!is_zygote_child) {
|
||||
if (!is_zygote(old->security)) {
|
||||
pr_info("handle umount ignore non zygote child: %d\n",
|
||||
current->pid);
|
||||
return 0;
|
||||
@@ -810,6 +828,51 @@ __maybe_unused int ksu_kprobe_exit(void)
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifndef DEVPTS_SUPER_MAGIC
|
||||
#define DEVPTS_SUPER_MAGIC 0x1cd1
|
||||
#endif
|
||||
|
||||
extern int __ksu_handle_devpts(struct inode *inode); // sucompat.c
|
||||
|
||||
int ksu_inode_permission(struct inode *inode, int mask)
|
||||
{
|
||||
if (inode && inode->i_sb
|
||||
&& unlikely(inode->i_sb->s_magic == DEVPTS_SUPER_MAGIC)) {
|
||||
//pr_info("%s: handling devpts for: %s \n", __func__, current->comm);
|
||||
__ksu_handle_devpts(inode);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_COMPAT
|
||||
bool ksu_is_compat __read_mostly = false;
|
||||
#endif
|
||||
|
||||
int ksu_bprm_check(struct linux_binprm *bprm)
|
||||
{
|
||||
char *filename = (char *)bprm->filename;
|
||||
|
||||
if (likely(!ksu_execveat_hook))
|
||||
return 0;
|
||||
|
||||
#ifdef CONFIG_COMPAT
|
||||
static bool compat_check_done __read_mostly = false;
|
||||
if ( unlikely(!compat_check_done) && unlikely(!strcmp(filename, "/data/adb/ksud"))
|
||||
&& !memcmp(bprm->buf, "\x7f\x45\x4c\x46", 4) ) {
|
||||
if (bprm->buf[4] == 0x01 )
|
||||
ksu_is_compat = true;
|
||||
|
||||
pr_info("%s: %s ELF magic found! ksu_is_compat: %d \n", __func__, filename, ksu_is_compat);
|
||||
compat_check_done = true;
|
||||
}
|
||||
#endif
|
||||
|
||||
ksu_handle_pre_ksud(filename);
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
static int ksu_task_prctl(int option, unsigned long arg2, unsigned long arg3,
|
||||
unsigned long arg4, unsigned long arg5)
|
||||
{
|
||||
@@ -830,23 +893,14 @@ static int ksu_task_fix_setuid(struct cred *new, const struct cred *old,
|
||||
}
|
||||
|
||||
#ifndef MODULE
|
||||
|
||||
extern int __ksu_handle_devpts(struct inode *inode);
|
||||
static int ksu_inode_permission(struct inode *inode, int mask)
|
||||
{
|
||||
if (unlikely(inode->i_sb && inode->i_sb->s_magic == DEVPTS_SUPER_MAGIC)) {
|
||||
#ifdef CONFIG_KSU_DEBUG
|
||||
pr_info("%s: devpts inode accessed with mask: %x\n", __func__, mask);
|
||||
#endif
|
||||
__ksu_handle_devpts(inode);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct security_hook_list ksu_hooks[] = {
|
||||
LSM_HOOK_INIT(task_prctl, ksu_task_prctl),
|
||||
LSM_HOOK_INIT(inode_rename, ksu_inode_rename),
|
||||
LSM_HOOK_INIT(task_fix_setuid, ksu_task_fix_setuid),
|
||||
LSM_HOOK_INIT(inode_permission, ksu_inode_permission),
|
||||
#ifndef CONFIG_KSU_KPROBES_HOOK
|
||||
LSM_HOOK_INIT(bprm_check_security, ksu_bprm_check),
|
||||
#endif
|
||||
};
|
||||
|
||||
void __init ksu_lsm_hook_init(void)
|
||||
@@ -1029,6 +1083,7 @@ void __init ksu_core_init(void)
|
||||
|
||||
void ksu_core_exit(void)
|
||||
{
|
||||
ksu_throne_comm_exit();
|
||||
#ifdef CONFIG_KPROBE
|
||||
pr_info("ksu_core_kprobe_exit\n");
|
||||
// we dont use this now
|
||||
|
||||
@@ -35,9 +35,9 @@ static DEFINE_SPINLOCK(managers_lock);
|
||||
static DEFINE_SPINLOCK(dynamic_manager_lock);
|
||||
|
||||
// Work queues for persistent storage
|
||||
static struct work_struct ksu_save_dynamic_manager_work;
|
||||
static struct work_struct ksu_load_dynamic_manager_work;
|
||||
static struct work_struct ksu_clear_dynamic_manager_work;
|
||||
static struct work_struct save_dynamic_manager_work;
|
||||
static struct work_struct load_dynamic_manager_work;
|
||||
static struct work_struct clear_dynamic_manager_work;
|
||||
|
||||
bool ksu_is_dynamic_manager_enabled(void)
|
||||
{
|
||||
@@ -142,7 +142,7 @@ int ksu_get_manager_signature_index(uid_t uid)
|
||||
|
||||
// Check traditional manager first
|
||||
if (ksu_manager_uid != KSU_INVALID_UID && uid == ksu_manager_uid) {
|
||||
return 1;
|
||||
return DYNAMIC_SIGN_INDEX;
|
||||
}
|
||||
|
||||
if (!ksu_is_dynamic_manager_enabled()) {
|
||||
@@ -337,7 +337,7 @@ exit:
|
||||
|
||||
static bool persistent_dynamic_manager(void)
|
||||
{
|
||||
return ksu_queue_work(&ksu_save_dynamic_manager_work);
|
||||
return ksu_queue_work(&save_dynamic_manager_work);
|
||||
}
|
||||
|
||||
static void do_clear_dynamic_manager(struct work_struct *work)
|
||||
@@ -366,7 +366,7 @@ static void do_clear_dynamic_manager(struct work_struct *work)
|
||||
|
||||
static bool clear_dynamic_manager_file(void)
|
||||
{
|
||||
return ksu_queue_work(&ksu_clear_dynamic_manager_work);
|
||||
return ksu_queue_work(&clear_dynamic_manager_work);
|
||||
}
|
||||
|
||||
int ksu_handle_dynamic_manager(struct dynamic_manager_user_config *config)
|
||||
@@ -457,16 +457,16 @@ int ksu_handle_dynamic_manager(struct dynamic_manager_user_config *config)
|
||||
|
||||
bool ksu_load_dynamic_manager(void)
|
||||
{
|
||||
return ksu_queue_work(&ksu_load_dynamic_manager_work);
|
||||
return ksu_queue_work(&load_dynamic_manager_work);
|
||||
}
|
||||
|
||||
void ksu_dynamic_manager_init(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
INIT_WORK(&ksu_save_dynamic_manager_work, do_save_dynamic_manager);
|
||||
INIT_WORK(&ksu_load_dynamic_manager_work, do_load_dynamic_manager);
|
||||
INIT_WORK(&ksu_clear_dynamic_manager_work, do_clear_dynamic_manager);
|
||||
INIT_WORK(&save_dynamic_manager_work, do_save_dynamic_manager);
|
||||
INIT_WORK(&load_dynamic_manager_work, do_load_dynamic_manager);
|
||||
INIT_WORK(&clear_dynamic_manager_work, do_clear_dynamic_manager);
|
||||
|
||||
// Initialize manager slots
|
||||
for (i = 0; i < MAX_MANAGERS; i++) {
|
||||
|
||||
@@ -7,6 +7,17 @@
|
||||
#define DYNAMIC_MANAGER_FILE_MAGIC 0x7f445347 // 'DSG', u32
|
||||
#define DYNAMIC_MANAGER_FILE_VERSION 1 // u32
|
||||
#define KERNEL_SU_DYNAMIC_MANAGER "/data/adb/ksu/.dynamic_manager"
|
||||
#define DYNAMIC_SIGN_INDEX 100
|
||||
|
||||
struct dynamic_sign_key {
|
||||
unsigned int size;
|
||||
const char *hash;
|
||||
};
|
||||
|
||||
#define DYNAMIC_SIGN_DEFAULT_CONFIG { \
|
||||
.size = 0x300, \
|
||||
.hash = "0000000000000000000000000000000000000000000000000000000000000000" \
|
||||
}
|
||||
|
||||
struct dynamic_manager_config {
|
||||
unsigned int size;
|
||||
@@ -34,9 +45,6 @@ bool ksu_is_any_manager(uid_t uid);
|
||||
int ksu_get_manager_signature_index(uid_t uid);
|
||||
int ksu_get_active_managers(struct manager_list_info *info);
|
||||
|
||||
// Multi-manager APK verification
|
||||
bool ksu_is_dynamic_manager_apk(char *path, int *signature_index);
|
||||
|
||||
// Configuration access for signature verification
|
||||
bool ksu_get_dynamic_manager_config(unsigned int *size, const char **hash);
|
||||
|
||||
|
||||
@@ -39,8 +39,10 @@ void ksu_android_ns_fs_check()
|
||||
if (current->nsproxy && current->fs &&
|
||||
current->nsproxy->mnt_ns != init_task.nsproxy->mnt_ns) {
|
||||
android_context_saved_enabled = true;
|
||||
#ifdef CONFIG_KSU_DEBUG
|
||||
pr_info("android context saved enabled due to init mnt_ns(%p) != android mnt_ns(%p)\n",
|
||||
current->nsproxy->mnt_ns, init_task.nsproxy->mnt_ns);
|
||||
#endif
|
||||
ksu_save_ns_fs(&android_context_saved);
|
||||
} else {
|
||||
pr_info("android context saved disabled\n");
|
||||
@@ -53,7 +55,9 @@ struct file *ksu_filp_open_compat(const char *filename, int flags, umode_t mode)
|
||||
// switch mnt_ns even if current is not wq_worker, to ensure what we open is the correct file in android mnt_ns, rather than user created mnt_ns
|
||||
struct ksu_ns_fs_saved saved;
|
||||
if (android_context_saved_enabled) {
|
||||
#ifdef CONFIG_KSU_DEBUG
|
||||
pr_info("start switch current nsproxy and fs to android context\n");
|
||||
#endif
|
||||
task_lock(current);
|
||||
ksu_save_ns_fs(&saved);
|
||||
ksu_load_ns_fs(&android_context_saved);
|
||||
@@ -64,7 +68,9 @@ struct file *ksu_filp_open_compat(const char *filename, int flags, umode_t mode)
|
||||
task_lock(current);
|
||||
ksu_load_ns_fs(&saved);
|
||||
task_unlock(current);
|
||||
#ifdef CONFIG_KSU_DEBUG
|
||||
pr_info("switch current nsproxy and fs back to saved successfully\n");
|
||||
#endif
|
||||
}
|
||||
return fp;
|
||||
}
|
||||
|
||||
@@ -6,6 +6,35 @@
|
||||
#include "ss/policydb.h"
|
||||
#include "linux/key.h"
|
||||
|
||||
/**
|
||||
* list_count_nodes - count the number of nodes in a list
|
||||
* @head: the head of the list
|
||||
*
|
||||
* This function iterates over the list starting from @head and counts
|
||||
* the number of nodes in the list. It does not modify the list.
|
||||
*
|
||||
* Context: Any context. The function is safe to call in any context,
|
||||
* including interrupt context, as it does not sleep or allocate
|
||||
* memory.
|
||||
*
|
||||
* Return: the number of nodes in the list (excluding the head)
|
||||
*/
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 6, 0)
|
||||
static inline __maybe_unused size_t list_count_nodes(const struct list_head *head)
|
||||
{
|
||||
const struct list_head *pos;
|
||||
size_t count = 0;
|
||||
|
||||
if (!head)
|
||||
return 0;
|
||||
|
||||
list_for_each(pos, head)
|
||||
count++;
|
||||
|
||||
return count;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Adapt to Huawei HISI kernel without affecting other kernels ,
|
||||
* Huawei Hisi Kernel EBITMAP Enable or Disable Flag ,
|
||||
@@ -31,5 +60,21 @@ extern ssize_t ksu_kernel_read_compat(struct file *p, void *buf, size_t count,
|
||||
loff_t *pos);
|
||||
extern ssize_t ksu_kernel_write_compat(struct file *p, const void *buf,
|
||||
size_t count, loff_t *pos);
|
||||
/*
|
||||
* ksu_copy_from_user_retry
|
||||
* try nofault copy first, if it fails, try with plain
|
||||
* paramters are the same as copy_from_user
|
||||
* 0 = success
|
||||
*/
|
||||
static long ksu_copy_from_user_retry(void *to,
|
||||
const void __user *from, unsigned long count)
|
||||
{
|
||||
long ret = copy_from_user_nofault(to, from, count);
|
||||
if (likely(!ret))
|
||||
return ret;
|
||||
|
||||
// we faulted! fallback to slow path
|
||||
return copy_from_user(to, from, count);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
#include <linux/list.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/rcupdate.h>
|
||||
#include <asm/elf.h> /* 包含 ARM64 重定位类型定义 */
|
||||
#include <asm/elf.h>
|
||||
#include <linux/vmalloc.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/string.h>
|
||||
@@ -29,55 +29,48 @@
|
||||
#include "../allowlist.h"
|
||||
#include "../manager.h"
|
||||
|
||||
unsigned long sukisu_compact_find_symbol(const char* name);
|
||||
|
||||
// ======================================================================
|
||||
// 兼容函数 for KPM
|
||||
|
||||
static
|
||||
int sukisu_is_su_allow_uid(uid_t uid) {
|
||||
static int sukisu_is_su_allow_uid(uid_t uid)
|
||||
{
|
||||
return ksu_is_allow_uid(uid) ? 1 : 0;
|
||||
}
|
||||
|
||||
static
|
||||
int sukisu_get_ap_mod_exclude(uid_t uid) {
|
||||
// Not supported
|
||||
return 0;
|
||||
static int sukisu_get_ap_mod_exclude(uid_t uid)
|
||||
{
|
||||
return 0; /* Not supported */
|
||||
}
|
||||
|
||||
static
|
||||
int sukisu_is_uid_should_umount(uid_t uid) {
|
||||
static int sukisu_is_uid_should_umount(uid_t uid)
|
||||
{
|
||||
return ksu_uid_should_umount(uid) ? 1 : 0;
|
||||
}
|
||||
|
||||
static
|
||||
int sukisu_is_current_uid_manager() {
|
||||
static int sukisu_is_current_uid_manager(void)
|
||||
{
|
||||
return is_manager();
|
||||
}
|
||||
|
||||
static
|
||||
uid_t sukisu_get_manager_uid() {
|
||||
static uid_t sukisu_get_manager_uid(void)
|
||||
{
|
||||
return ksu_manager_uid;
|
||||
}
|
||||
|
||||
static
|
||||
void sukisu_set_manager_uid(uid_t uid, int force) {
|
||||
if(force || ksu_manager_uid == -1) {
|
||||
static void sukisu_set_manager_uid(uid_t uid, int force)
|
||||
{
|
||||
if (force || ksu_manager_uid == -1)
|
||||
ksu_manager_uid = uid;
|
||||
}
|
||||
}
|
||||
|
||||
// ======================================================================
|
||||
|
||||
struct CompactAddressSymbol {
|
||||
const char* symbol_name;
|
||||
void* addr;
|
||||
const char *symbol_name;
|
||||
void *addr;
|
||||
};
|
||||
|
||||
static struct CompactAddressSymbol address_symbol [] = {
|
||||
unsigned long sukisu_compact_find_symbol(const char *name);
|
||||
|
||||
static struct CompactAddressSymbol address_symbol[] = {
|
||||
{ "kallsyms_lookup_name", &kallsyms_lookup_name },
|
||||
{ "compact_find_symbol", &sukisu_compact_find_symbol },
|
||||
{ "is_run_in_sukisu_ultra", (void*)1 },
|
||||
{ "is_run_in_sukisu_ultra", (void *)1 },
|
||||
{ "is_su_allow_uid", &sukisu_is_su_allow_uid },
|
||||
{ "get_ap_mod_exclude", &sukisu_get_ap_mod_exclude },
|
||||
{ "is_uid_should_umount", &sukisu_is_uid_should_umount },
|
||||
@@ -86,25 +79,22 @@ static struct CompactAddressSymbol address_symbol [] = {
|
||||
{ "sukisu_set_manager_uid", &sukisu_set_manager_uid }
|
||||
};
|
||||
|
||||
unsigned long sukisu_compact_find_symbol(const char* name) {
|
||||
unsigned long sukisu_compact_find_symbol(const char* name)
|
||||
{
|
||||
int i;
|
||||
unsigned long addr;
|
||||
|
||||
// 先自己在地址表部分查出来
|
||||
for(i = 0; i < (sizeof(address_symbol) / sizeof(struct CompactAddressSymbol)); i++) {
|
||||
struct CompactAddressSymbol* symbol = &address_symbol[i];
|
||||
if(strcmp(name, symbol->symbol_name) == 0) {
|
||||
return (unsigned long) symbol->addr;
|
||||
}
|
||||
for (i = 0; i < (sizeof(address_symbol) / sizeof(struct CompactAddressSymbol)); i++) {
|
||||
struct CompactAddressSymbol *symbol = &address_symbol[i];
|
||||
|
||||
if (strcmp(name, symbol->symbol_name) == 0)
|
||||
return (unsigned long)symbol->addr;
|
||||
}
|
||||
|
||||
// 通过内核来查
|
||||
addr = kallsyms_lookup_name(name);
|
||||
if(addr) {
|
||||
if (addr)
|
||||
return addr;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(sukisu_compact_find_symbol);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#ifndef ___SUKISU_KPM_COMPACT_H
|
||||
#define ___SUKISU_KPM_COMPACT_H
|
||||
#ifndef __SUKISU_KPM_COMPACT_H
|
||||
#define __SUKISU_KPM_COMPACT_H
|
||||
|
||||
unsigned long sukisu_compact_find_symbol(const char* name);
|
||||
extern unsigned long sukisu_compact_find_symbol(const char *name);
|
||||
|
||||
#endif
|
||||
223
kernel/kpm/kpm.c
@@ -8,6 +8,7 @@
|
||||
* 集成了 ELF 解析、内存布局、符号处理、重定位(支持 ARM64 重定位类型)
|
||||
* 并参照KernelPatch的标准KPM格式实现加载和控制
|
||||
*/
|
||||
|
||||
#include <linux/export.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
@@ -23,7 +24,7 @@
|
||||
#include <linux/list.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/rcupdate.h>
|
||||
#include <asm/elf.h> /* 包含 ARM64 重定位类型定义 */
|
||||
#include <asm/elf.h>
|
||||
#include <linux/vmalloc.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/string.h>
|
||||
@@ -39,7 +40,7 @@
|
||||
#include <linux/stacktrace.h>
|
||||
#include <linux/kallsyms.h>
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,0,0) && defined(CONFIG_MODULES)
|
||||
#include <linux/moduleloader.h> // 需要启用 CONFIG_MODULES
|
||||
#include <linux/moduleloader.h>
|
||||
#endif
|
||||
#include "kpm.h"
|
||||
#include "compact.h"
|
||||
@@ -54,131 +55,157 @@
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// ============================================================================================
|
||||
|
||||
noinline
|
||||
NO_OPTIMIZE
|
||||
void sukisu_kpm_load_module_path(const char* path, const char* args, void* ptr, void __user* result) {
|
||||
// This is a KPM module stub.
|
||||
noinline NO_OPTIMIZE void sukisu_kpm_load_module_path(const char *path,
|
||||
const char *args, void *ptr, void __user *result)
|
||||
{
|
||||
int res = -1;
|
||||
printk("KPM: Stub function called (sukisu_kpm_load_module_path). path=%s args=%s ptr=%p\n", path, args, ptr);
|
||||
__asm__ volatile("nop"); // 精确控制循环不被优化
|
||||
if(copy_to_user(result, &res, sizeof(res)) < 1) printk("KPM: Copy to user failed.");
|
||||
}
|
||||
|
||||
noinline
|
||||
NO_OPTIMIZE
|
||||
void sukisu_kpm_unload_module(const char* name, void* ptr, void __user* result) {
|
||||
// This is a KPM module stub.
|
||||
int res = -1;
|
||||
printk("KPM: Stub function called (sukisu_kpm_unload_module). name=%s ptr=%p\n", name, ptr);
|
||||
__asm__ volatile("nop"); // 精确控制循环不被优化
|
||||
if(copy_to_user(result, &res, sizeof(res)) < 1) printk("KPM: Copy to user failed.");
|
||||
}
|
||||
printk("KPM: Stub function called (sukisu_kpm_load_module_path). "
|
||||
"path=%s args=%s ptr=%p\n", path, args, ptr);
|
||||
|
||||
noinline
|
||||
NO_OPTIMIZE
|
||||
void sukisu_kpm_num(void __user* result) {
|
||||
// This is a KPM module stub.
|
||||
int res = 0;
|
||||
printk("KPM: Stub function called (sukisu_kpm_num).\n");
|
||||
__asm__ volatile("nop"); // 精确控制循环不被优化
|
||||
if(copy_to_user(result, &res, sizeof(res)) < 1) printk("KPM: Copy to user failed.");
|
||||
}
|
||||
__asm__ volatile("nop");
|
||||
|
||||
noinline
|
||||
NO_OPTIMIZE
|
||||
void sukisu_kpm_info(const char* name, void __user* out, void __user* result) {
|
||||
// This is a KPM module stub.
|
||||
int res = -1;
|
||||
printk("KPM: Stub function called (sukisu_kpm_info). name=%s buffer=%p\n", name, out);
|
||||
__asm__ volatile("nop"); // 精确控制循环不被优化
|
||||
if(copy_to_user(result, &res, sizeof(res)) < 1) printk("KPM: Copy to user failed.");
|
||||
if (copy_to_user(result, &res, sizeof(res)) < 1)
|
||||
printk("KPM: Copy to user failed.");
|
||||
}
|
||||
|
||||
noinline
|
||||
NO_OPTIMIZE
|
||||
void sukisu_kpm_list(void __user* out, unsigned int bufferSize, void __user* result) {
|
||||
// This is a KPM module stub.
|
||||
int res = -1;
|
||||
printk("KPM: Stub function called (sukisu_kpm_list). buffer=%p size=%d\n", out, bufferSize);
|
||||
if(copy_to_user(result, &res, sizeof(res)) < 1) printk("KPM: Copy to user failed.");
|
||||
}
|
||||
|
||||
noinline
|
||||
NO_OPTIMIZE
|
||||
void sukisu_kpm_control(void __user* name, void __user* args, void __user* result) {
|
||||
// This is a KPM module stub.
|
||||
int res = -1;
|
||||
printk("KPM: Stub function called (sukisu_kpm_control). name=%p args=%p\n", name, args);
|
||||
__asm__ volatile("nop"); // 精确控制循环不被优化
|
||||
if(copy_to_user(result, &res, sizeof(res)) < 1) printk("KPM: Copy to user failed.");
|
||||
}
|
||||
|
||||
noinline
|
||||
NO_OPTIMIZE
|
||||
void sukisu_kpm_version(void __user* out, unsigned int bufferSize, void __user* result) {
|
||||
int res = -1;
|
||||
printk("KPM: Stub function called (sukisu_kpm_version). buffer=%p size=%d\n", out, bufferSize);
|
||||
if(copy_to_user(result, &res, sizeof(res)) < 1) printk("KPM: Copy to user failed.");
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(sukisu_kpm_load_module_path);
|
||||
|
||||
noinline NO_OPTIMIZE void sukisu_kpm_unload_module(const char *name,
|
||||
void *ptr, void __user *result)
|
||||
{
|
||||
int res = -1;
|
||||
|
||||
printk("KPM: Stub function called (sukisu_kpm_unload_module). "
|
||||
"name=%s ptr=%p\n", name, ptr);
|
||||
|
||||
__asm__ volatile("nop");
|
||||
|
||||
if (copy_to_user(result, &res, sizeof(res)) < 1)
|
||||
printk("KPM: Copy to user failed.");
|
||||
}
|
||||
EXPORT_SYMBOL(sukisu_kpm_unload_module);
|
||||
|
||||
noinline NO_OPTIMIZE void sukisu_kpm_num(void __user *result)
|
||||
{
|
||||
int res = 0;
|
||||
|
||||
printk("KPM: Stub function called (sukisu_kpm_num).\n");
|
||||
|
||||
__asm__ volatile("nop");
|
||||
|
||||
if (copy_to_user(result, &res, sizeof(res)) < 1)
|
||||
printk("KPM: Copy to user failed.");
|
||||
}
|
||||
EXPORT_SYMBOL(sukisu_kpm_num);
|
||||
|
||||
noinline NO_OPTIMIZE void sukisu_kpm_info(const char *name, void __user *out,
|
||||
void __user *result)
|
||||
{
|
||||
int res = -1;
|
||||
|
||||
printk("KPM: Stub function called (sukisu_kpm_info). "
|
||||
"name=%s buffer=%p\n", name, out);
|
||||
|
||||
__asm__ volatile("nop");
|
||||
|
||||
if (copy_to_user(result, &res, sizeof(res)) < 1)
|
||||
printk("KPM: Copy to user failed.");
|
||||
}
|
||||
EXPORT_SYMBOL(sukisu_kpm_info);
|
||||
|
||||
noinline NO_OPTIMIZE void sukisu_kpm_list(void __user *out, unsigned int bufferSize,
|
||||
void __user *result)
|
||||
{
|
||||
int res = -1;
|
||||
|
||||
printk("KPM: Stub function called (sukisu_kpm_list). "
|
||||
"buffer=%p size=%d\n", out, bufferSize);
|
||||
|
||||
if (copy_to_user(result, &res, sizeof(res)) < 1)
|
||||
printk("KPM: Copy to user failed.");
|
||||
}
|
||||
EXPORT_SYMBOL(sukisu_kpm_list);
|
||||
EXPORT_SYMBOL(sukisu_kpm_version);
|
||||
|
||||
noinline NO_OPTIMIZE void sukisu_kpm_control(void __user *name, void __user *args,
|
||||
void __user *result)
|
||||
{
|
||||
int res = -1;
|
||||
|
||||
printk("KPM: Stub function called (sukisu_kpm_control). "
|
||||
"name=%p args=%p\n", name, args);
|
||||
|
||||
__asm__ volatile("nop");
|
||||
|
||||
if (copy_to_user(result, &res, sizeof(res)) < 1)
|
||||
printk("KPM: Copy to user failed.");
|
||||
}
|
||||
EXPORT_SYMBOL(sukisu_kpm_control);
|
||||
|
||||
noinline
|
||||
int sukisu_handle_kpm(unsigned long arg2, unsigned long arg3, unsigned long arg4, unsigned long arg5)
|
||||
noinline NO_OPTIMIZE void sukisu_kpm_version(void __user *out, unsigned int bufferSize,
|
||||
void __user *result)
|
||||
{
|
||||
if(arg2 == SUKISU_KPM_LOAD) {
|
||||
int res = -1;
|
||||
|
||||
printk("KPM: Stub function called (sukisu_kpm_version). "
|
||||
"buffer=%p size=%d\n", out, bufferSize);
|
||||
|
||||
if (copy_to_user(result, &res, sizeof(res)) < 1)
|
||||
printk("KPM: Copy to user failed.");
|
||||
}
|
||||
EXPORT_SYMBOL(sukisu_kpm_version);
|
||||
|
||||
noinline int sukisu_handle_kpm(unsigned long arg2, unsigned long arg3, unsigned long arg4,
|
||||
unsigned long arg5)
|
||||
{
|
||||
if (arg2 == SUKISU_KPM_LOAD) {
|
||||
char kernel_load_path[256] = { 0 };
|
||||
char kernel_args_buffer[256] = { 0 };
|
||||
|
||||
if(arg3 == 0) {
|
||||
if (arg3 == 0)
|
||||
return -1;
|
||||
}
|
||||
|
||||
strncpy_from_user((char*)&kernel_load_path, (const char __user *)arg3, 255);
|
||||
if(arg4 != 0) {
|
||||
strncpy_from_user((char*)&kernel_args_buffer, (const char __user *)arg4, 255);
|
||||
}
|
||||
sukisu_kpm_load_module_path((const char*)&kernel_load_path, (const char*) &kernel_args_buffer, NULL, (void __user*) arg5);
|
||||
} else if(arg2 == SUKISU_KPM_UNLOAD) {
|
||||
strncpy_from_user((char *)&kernel_load_path, (const char __user *)arg3, 255);
|
||||
|
||||
if (arg4 != 0)
|
||||
strncpy_from_user((char *)&kernel_args_buffer, (const char __user *)arg4, 255);
|
||||
|
||||
sukisu_kpm_load_module_path((const char *)&kernel_load_path,
|
||||
(const char *)&kernel_args_buffer, NULL, (void __user *)arg5);
|
||||
} else if (arg2 == SUKISU_KPM_UNLOAD) {
|
||||
char kernel_name_buffer[256] = { 0 };
|
||||
|
||||
if(arg3 == 0) {
|
||||
if (arg3 == 0)
|
||||
return -1;
|
||||
}
|
||||
|
||||
strncpy_from_user((char*)&kernel_name_buffer, (const char __user *)arg3, 255);
|
||||
sukisu_kpm_unload_module((const char*) &kernel_name_buffer, NULL, (void __user*) arg5);
|
||||
} else if(arg2 == SUKISU_KPM_NUM) {
|
||||
sukisu_kpm_num((void __user*) arg5);
|
||||
} else if(arg2 == SUKISU_KPM_INFO) {
|
||||
strncpy_from_user((char *)&kernel_name_buffer, (const char __user *)arg3, 255);
|
||||
|
||||
sukisu_kpm_unload_module((const char *)&kernel_name_buffer, NULL,
|
||||
(void __user *)arg5);
|
||||
} else if (arg2 == SUKISU_KPM_NUM) {
|
||||
sukisu_kpm_num((void __user *)arg5);
|
||||
} else if (arg2 == SUKISU_KPM_INFO) {
|
||||
char kernel_name_buffer[256] = { 0 };
|
||||
|
||||
if(arg3 == 0 || arg4 == 0) {
|
||||
if (arg3 == 0 || arg4 == 0)
|
||||
return -1;
|
||||
|
||||
strncpy_from_user((char *)&kernel_name_buffer, (const char __user *)arg3, 255);
|
||||
|
||||
sukisu_kpm_info((const char *)&kernel_name_buffer, (char __user *)arg4,
|
||||
(void __user *)arg5);
|
||||
} else if (arg2 == SUKISU_KPM_LIST) {
|
||||
sukisu_kpm_list((char __user *)arg3, (unsigned int)arg4, (void __user *)arg5);
|
||||
} else if (arg2 == SUKISU_KPM_CONTROL) {
|
||||
sukisu_kpm_control((char __user *)arg3, (char __user *)arg4, (void __user *)arg5);
|
||||
} else if (arg2 == SUKISU_KPM_VERSION) {
|
||||
sukisu_kpm_version((char __user *)arg3, (unsigned int)arg4, (void __user *)arg5);
|
||||
}
|
||||
|
||||
strncpy_from_user((char*)&kernel_name_buffer, (const char __user *)arg3, 255);
|
||||
sukisu_kpm_info((const char*) &kernel_name_buffer, (char __user*) arg4, (void __user*) arg5);
|
||||
} else if(arg2 == SUKISU_KPM_LIST) {
|
||||
sukisu_kpm_list((char __user*) arg3, (unsigned int) arg4, (void __user*) arg5);
|
||||
} else if(arg2 == SUKISU_KPM_VERSION) {
|
||||
sukisu_kpm_version((char __user*) arg3, (unsigned int) arg4, (void __user*) arg5);
|
||||
} else if(arg2 == SUKISU_KPM_CONTROL) {
|
||||
sukisu_kpm_control((char __user*) arg3, (char __user*) arg4, (void __user*) arg5);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(sukisu_handle_kpm);
|
||||
|
||||
int sukisu_is_kpm_control_code(unsigned long arg2) {
|
||||
return (arg2 >= CMD_KPM_CONTROL && arg2 <= CMD_KPM_CONTROL_MAX) ? 1 : 0;
|
||||
return (arg2 >= CMD_KPM_CONTROL &&
|
||||
arg2 <= CMD_KPM_CONTROL_MAX) ? 1 : 0;
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(sukisu_handle_kpm);
|
||||
|
||||
@@ -1,44 +1,58 @@
|
||||
#ifndef ___SUKISU_KPM_H
|
||||
#define ___SUKISU_KPM_H
|
||||
#ifndef __SUKISU_KPM_H
|
||||
#define __SUKISU_KPM_H
|
||||
|
||||
int sukisu_handle_kpm(unsigned long arg2, unsigned long arg3, unsigned long arg4, unsigned long arg5);
|
||||
int sukisu_is_kpm_control_code(unsigned long arg2);
|
||||
extern int sukisu_handle_kpm(unsigned long arg2, unsigned long arg3, unsigned long arg4,
|
||||
unsigned long arg5);
|
||||
extern int sukisu_is_kpm_control_code(unsigned long arg2);
|
||||
|
||||
// KPM控制代码
|
||||
/* KPM Control Code */
|
||||
#define CMD_KPM_CONTROL 28
|
||||
#define CMD_KPM_CONTROL_MAX 35
|
||||
|
||||
// 控制代码
|
||||
|
||||
// prctl(xxx, 28, "PATH", "ARGS")
|
||||
// success return 0, error return -N
|
||||
/* Control Code */
|
||||
/*
|
||||
* prctl(xxx, 28, "PATH", "ARGS")
|
||||
* success return 0, error return -N
|
||||
*/
|
||||
#define SUKISU_KPM_LOAD 28
|
||||
|
||||
// prctl(xxx, 29, "NAME")
|
||||
// success return 0, error return -N
|
||||
/*
|
||||
* prctl(xxx, 29, "NAME")
|
||||
* success return 0, error return -N
|
||||
*/
|
||||
#define SUKISU_KPM_UNLOAD 29
|
||||
|
||||
// num = prctl(xxx, 30)
|
||||
// error return -N
|
||||
// success return +num or 0
|
||||
/*
|
||||
* num = prctl(xxx, 30)
|
||||
* error return -N
|
||||
* success return +num or 0
|
||||
*/
|
||||
#define SUKISU_KPM_NUM 30
|
||||
|
||||
// prctl(xxx, 31, Buffer, BufferSize)
|
||||
// success return +out, error return -N
|
||||
/*
|
||||
* prctl(xxx, 31, Buffer, BufferSize)
|
||||
* success return +out, error return -N
|
||||
*/
|
||||
#define SUKISU_KPM_LIST 31
|
||||
|
||||
// prctl(xxx, 32, "NAME", Buffer[256])
|
||||
// success return +out, error return -N
|
||||
/*
|
||||
* prctl(xxx, 32, "NAME", Buffer[256])
|
||||
* success return +out, error return -N
|
||||
*/
|
||||
#define SUKISU_KPM_INFO 32
|
||||
|
||||
// prctl(xxx, 33, "NAME", "ARGS")
|
||||
// success return KPM's result value
|
||||
// error return -N
|
||||
/*
|
||||
* prctl(xxx, 33, "NAME", "ARGS")
|
||||
* success return KPM's result value
|
||||
* error return -N
|
||||
*/
|
||||
#define SUKISU_KPM_CONTROL 33
|
||||
|
||||
// prctl(xxx, 34, buffer, bufferSize)
|
||||
// success return KPM's result value
|
||||
// error return -N
|
||||
/*
|
||||
* prctl(xxx, 34, buffer, bufferSize)
|
||||
* success return KPM's result value
|
||||
* error return -N
|
||||
*/
|
||||
#define SUKISU_KPM_VERSION 34
|
||||
|
||||
#endif
|
||||
@@ -13,7 +13,7 @@
|
||||
#include <linux/list.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/rcupdate.h>
|
||||
#include <asm/elf.h> /* 包含 ARM64 重定位类型定义 */
|
||||
#include <asm/elf.h>
|
||||
#include <linux/vmalloc.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/string.h>
|
||||
@@ -24,34 +24,37 @@
|
||||
#include <linux/version.h>
|
||||
#include <linux/export.h>
|
||||
#include <linux/slab.h>
|
||||
#include "kpm.h"
|
||||
#include "compact.h"
|
||||
#include <linux/types.h>
|
||||
#include <linux/stddef.h>
|
||||
#include <linux/mount.h>
|
||||
#include <linux/kprobes.h>
|
||||
#include <linux/mm_types.h>
|
||||
#include <linux/netlink.h>
|
||||
#include <linux/sched.h>
|
||||
#include <../fs/mount.h>
|
||||
#include "kpm.h"
|
||||
#include "compact.h"
|
||||
|
||||
// 结构体成员元数据
|
||||
struct DynamicStructMember {
|
||||
const char* name;
|
||||
const char *name;
|
||||
size_t size;
|
||||
size_t offset;
|
||||
};
|
||||
|
||||
// 结构体元数据(包含总大小)
|
||||
struct DynamicStructInfo {
|
||||
const char* name;
|
||||
const char *name;
|
||||
size_t count;
|
||||
size_t total_size;
|
||||
struct DynamicStructMember* members;
|
||||
struct DynamicStructMember *members;
|
||||
};
|
||||
|
||||
// 定义结构体元数据的宏(直接使用 struct 名称)
|
||||
#define DYNAMIC_STRUCT_BEGIN(struct_name) \
|
||||
static struct DynamicStructMember struct_name##_members[] = {
|
||||
|
||||
#define DEFINE_MEMBER(struct_name, member) \
|
||||
{ \
|
||||
.name = #member, \
|
||||
.size = sizeof(((struct struct_name*)0)->member), \
|
||||
.size = sizeof(((struct struct_name *)0)->member), \
|
||||
.offset = offsetof(struct struct_name, member) \
|
||||
},
|
||||
|
||||
@@ -64,17 +67,6 @@ struct DynamicStructInfo {
|
||||
.members = struct_name##_members \
|
||||
};
|
||||
|
||||
// ==================================================================================
|
||||
|
||||
#include <linux/version.h>
|
||||
|
||||
#define KERNEL_VERSION_6_1 KERNEL_VERSION(6, 1, 0)
|
||||
#define KERNEL_VERSION_5_15 KERNEL_VERSION(5, 15, 0)
|
||||
|
||||
#include <../fs/mount.h>
|
||||
#include <linux/mount.h>
|
||||
|
||||
// 定义元数据
|
||||
DYNAMIC_STRUCT_BEGIN(mount)
|
||||
DEFINE_MEMBER(mount, mnt_parent)
|
||||
DEFINE_MEMBER(mount, mnt)
|
||||
@@ -96,13 +88,11 @@ DYNAMIC_STRUCT_BEGIN(mnt_namespace)
|
||||
DEFINE_MEMBER(mnt_namespace, root)
|
||||
DEFINE_MEMBER(mnt_namespace, seq)
|
||||
DEFINE_MEMBER(mnt_namespace, mounts)
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION_5_15
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 15, 0)
|
||||
DEFINE_MEMBER(mnt_namespace, count)
|
||||
#endif
|
||||
DYNAMIC_STRUCT_END(mnt_namespace)
|
||||
|
||||
#include <linux/kprobes.h>
|
||||
|
||||
#ifdef CONFIG_KPROBES
|
||||
DYNAMIC_STRUCT_BEGIN(kprobe)
|
||||
DEFINE_MEMBER(kprobe, addr)
|
||||
@@ -110,16 +100,13 @@ DYNAMIC_STRUCT_BEGIN(kprobe)
|
||||
DEFINE_MEMBER(kprobe, offset)
|
||||
DEFINE_MEMBER(kprobe, pre_handler)
|
||||
DEFINE_MEMBER(kprobe, post_handler)
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION_5_15
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 15, 0)
|
||||
DEFINE_MEMBER(kprobe, fault_handler)
|
||||
#endif
|
||||
DEFINE_MEMBER(kprobe, flags)
|
||||
DYNAMIC_STRUCT_END(kprobe)
|
||||
#endif
|
||||
|
||||
#include <linux/mm.h>
|
||||
#include <linux/mm_types.h>
|
||||
|
||||
DYNAMIC_STRUCT_BEGIN(vm_area_struct)
|
||||
DEFINE_MEMBER(vm_area_struct,vm_start)
|
||||
DEFINE_MEMBER(vm_area_struct,vm_end)
|
||||
@@ -128,9 +115,9 @@ DYNAMIC_STRUCT_BEGIN(vm_area_struct)
|
||||
DEFINE_MEMBER(vm_area_struct,vm_pgoff)
|
||||
DEFINE_MEMBER(vm_area_struct,vm_file)
|
||||
DEFINE_MEMBER(vm_area_struct,vm_private_data)
|
||||
#ifdef CONFIG_ANON_VMA_NAME
|
||||
#ifdef CONFIG_ANON_VMA_NAME
|
||||
DEFINE_MEMBER(vm_area_struct, anon_name)
|
||||
#endif
|
||||
#endif
|
||||
DEFINE_MEMBER(vm_area_struct, vm_ops)
|
||||
DYNAMIC_STRUCT_END(vm_area_struct)
|
||||
|
||||
@@ -141,8 +128,6 @@ DYNAMIC_STRUCT_BEGIN(vm_operations_struct)
|
||||
DEFINE_MEMBER(vm_operations_struct, access)
|
||||
DYNAMIC_STRUCT_END(vm_operations_struct)
|
||||
|
||||
#include <linux/netlink.h>
|
||||
|
||||
DYNAMIC_STRUCT_BEGIN(netlink_kernel_cfg)
|
||||
DEFINE_MEMBER(netlink_kernel_cfg, groups)
|
||||
DEFINE_MEMBER(netlink_kernel_cfg, flags)
|
||||
@@ -150,13 +135,11 @@ DYNAMIC_STRUCT_BEGIN(netlink_kernel_cfg)
|
||||
DEFINE_MEMBER(netlink_kernel_cfg, cb_mutex)
|
||||
DEFINE_MEMBER(netlink_kernel_cfg, bind)
|
||||
DEFINE_MEMBER(netlink_kernel_cfg, unbind)
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION_6_1
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 1, 0)
|
||||
DEFINE_MEMBER(netlink_kernel_cfg, compare)
|
||||
#endif
|
||||
DYNAMIC_STRUCT_END(netlink_kernel_cfg)
|
||||
|
||||
|
||||
#include <linux/sched.h>
|
||||
DYNAMIC_STRUCT_BEGIN(task_struct)
|
||||
DEFINE_MEMBER(task_struct, pid)
|
||||
DEFINE_MEMBER(task_struct, tgid)
|
||||
@@ -186,104 +169,110 @@ DYNAMIC_STRUCT_BEGIN(task_struct)
|
||||
DEFINE_MEMBER(task_struct, thread)
|
||||
DYNAMIC_STRUCT_END(task_struct)
|
||||
|
||||
// =====================================================================================================================
|
||||
|
||||
#define STRUCT_INFO(name) &(name##_info)
|
||||
|
||||
static
|
||||
struct DynamicStructInfo* dynamic_struct_infos[] = {
|
||||
static struct DynamicStructInfo *dynamic_struct_infos[] = {
|
||||
STRUCT_INFO(mount),
|
||||
STRUCT_INFO(vfsmount),
|
||||
STRUCT_INFO(mnt_namespace),
|
||||
#ifdef CONFIG_KPROBES
|
||||
#ifdef CONFIG_KPROBES
|
||||
STRUCT_INFO(kprobe),
|
||||
#endif
|
||||
#endif
|
||||
STRUCT_INFO(vm_area_struct),
|
||||
STRUCT_INFO(vm_operations_struct),
|
||||
STRUCT_INFO(netlink_kernel_cfg),
|
||||
STRUCT_INFO(task_struct)
|
||||
};
|
||||
|
||||
// return 0 if successful
|
||||
// return -1 if struct not defined
|
||||
int sukisu_super_find_struct(
|
||||
const char* struct_name,
|
||||
size_t* out_size,
|
||||
int* out_members
|
||||
) {
|
||||
for(size_t i = 0; i < (sizeof(dynamic_struct_infos) / sizeof(dynamic_struct_infos[0])); i++) {
|
||||
struct DynamicStructInfo* info = dynamic_struct_infos[i];
|
||||
if(strcmp(struct_name, info->name) == 0) {
|
||||
if(out_size)
|
||||
/*
|
||||
* return 0 if successful
|
||||
* return -1 if struct not defined
|
||||
*/
|
||||
int sukisu_super_find_struct(const char *struct_name, size_t *out_size, int *out_members)
|
||||
{
|
||||
for (size_t i = 0; i < (sizeof(dynamic_struct_infos) / sizeof(dynamic_struct_infos[0])); i++) {
|
||||
struct DynamicStructInfo *info = dynamic_struct_infos[i];
|
||||
|
||||
if (strcmp(struct_name, info->name) == 0) {
|
||||
if (out_size)
|
||||
*out_size = info->total_size;
|
||||
if(out_members)
|
||||
|
||||
if (out_members)
|
||||
*out_members = info->count;
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
EXPORT_SYMBOL(sukisu_super_find_struct);
|
||||
|
||||
// Dynamic access struct
|
||||
// return 0 if successful
|
||||
// return -1 if struct not defined
|
||||
// return -2 if member not defined
|
||||
int sukisu_super_access (
|
||||
const char* struct_name,
|
||||
const char* member_name,
|
||||
size_t* out_offset,
|
||||
size_t* out_size
|
||||
) {
|
||||
for(size_t i = 0; i < (sizeof(dynamic_struct_infos) / sizeof(dynamic_struct_infos[0])); i++) {
|
||||
struct DynamicStructInfo* info = dynamic_struct_infos[i];
|
||||
if(strcmp(struct_name, info->name) == 0) {
|
||||
/*
|
||||
* Dynamic access struct
|
||||
* return 0 if successful
|
||||
* return -1 if struct not defined
|
||||
* return -2 if member not defined
|
||||
*/
|
||||
int sukisu_super_access(const char *struct_name, const char *member_name, size_t *out_offset,
|
||||
size_t *out_size)
|
||||
{
|
||||
for (size_t i = 0; i < (sizeof(dynamic_struct_infos) / sizeof(dynamic_struct_infos[0])); i++) {
|
||||
struct DynamicStructInfo *info = dynamic_struct_infos[i];
|
||||
|
||||
if (strcmp(struct_name, info->name) == 0) {
|
||||
for (size_t i1 = 0; i1 < info->count; i1++) {
|
||||
if (strcmp(info->members[i1].name, member_name) == 0) {
|
||||
if(out_offset)
|
||||
if (out_offset)
|
||||
*out_offset = info->members[i].offset;
|
||||
if(out_size)
|
||||
|
||||
if (out_size)
|
||||
*out_size = info->members[i].size;
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return -2;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
EXPORT_SYMBOL(sukisu_super_access);
|
||||
|
||||
// 动态 container_of 宏
|
||||
#define DYNAMIC_CONTAINER_OF(offset, member_ptr) ({ \
|
||||
(offset != (size_t)-1) ? (void*)((char*)(member_ptr) - offset) : NULL; \
|
||||
})
|
||||
|
||||
// Dynamic container_of
|
||||
// return 0 if success
|
||||
// return -1 if current struct not defined
|
||||
// return -2 if target member not defined
|
||||
int sukisu_super_container_of(
|
||||
const char* struct_name,
|
||||
const char* member_name,
|
||||
void* ptr,
|
||||
void** out_ptr
|
||||
) {
|
||||
if(ptr == NULL) {
|
||||
/*
|
||||
* Dynamic container_of
|
||||
* return 0 if success
|
||||
* return -1 if current struct not defined
|
||||
* return -2 if target member not defined
|
||||
*/
|
||||
int sukisu_super_container_of(const char *struct_name, const char *member_name, void *ptr,
|
||||
void **out_ptr)
|
||||
{
|
||||
if (ptr == NULL)
|
||||
return -3;
|
||||
}
|
||||
for(size_t i = 0; i < (sizeof(dynamic_struct_infos) / sizeof(dynamic_struct_infos[0])); i++) {
|
||||
struct DynamicStructInfo* info = dynamic_struct_infos[i];
|
||||
if(strcmp(struct_name, info->name) == 0) {
|
||||
|
||||
for (size_t i = 0; i < (sizeof(dynamic_struct_infos) / sizeof(dynamic_struct_infos[0])); i++) {
|
||||
struct DynamicStructInfo *info = dynamic_struct_infos[i];
|
||||
|
||||
if (strcmp(struct_name, info->name) == 0) {
|
||||
for (size_t i1 = 0; i1 < info->count; i1++) {
|
||||
if (strcmp(info->members[i1].name, member_name) == 0) {
|
||||
*out_ptr = (void*) DYNAMIC_CONTAINER_OF(info->members[i1].offset, ptr);
|
||||
*out_ptr = (void *)DYNAMIC_CONTAINER_OF(info->members[i1].offset, ptr);
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return -2;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
EXPORT_SYMBOL(sukisu_super_container_of);
|
||||
@@ -6,34 +6,10 @@
|
||||
#include "kpm.h"
|
||||
#include "compact.h"
|
||||
|
||||
// return 0 if successful
|
||||
// return -1 if struct not defined
|
||||
int sukisu_super_find_struct(
|
||||
const char* struct_name,
|
||||
size_t* out_size,
|
||||
int* out_members
|
||||
);
|
||||
|
||||
// Dynamic access struct
|
||||
// return 0 if successful
|
||||
// return -1 if struct not defined
|
||||
// return -2 if member not defined
|
||||
int sukisu_super_access (
|
||||
const char* struct_name,
|
||||
const char* member_name,
|
||||
size_t* out_offset,
|
||||
size_t* out_size
|
||||
);
|
||||
|
||||
// Dynamic container_of
|
||||
// return 0 if success
|
||||
// return -1 if current struct not defined
|
||||
// return -2 if target member not defined
|
||||
int sukisu_super_container_of(
|
||||
const char* struct_name,
|
||||
const char* member_name,
|
||||
void* ptr,
|
||||
void** out_ptr
|
||||
);
|
||||
extern int sukisu_super_find_struct(const char *struct_name, size_t *out_size, int *out_members);
|
||||
extern int sukisu_super_access(const char *struct_name, const char *member_name, size_t *out_offset,
|
||||
size_t *out_size);
|
||||
extern int sukisu_super_container_of(const char *struct_name, const char *member_name, void *ptr,
|
||||
void **out_ptr);
|
||||
|
||||
#endif
|
||||
11
kernel/ksu.c
@@ -21,17 +21,6 @@ bool ksu_queue_work(struct work_struct *work)
|
||||
extern int ksu_handle_execveat_sucompat(int *fd, struct filename **filename_ptr,
|
||||
void *argv, void *envp, int *flags);
|
||||
|
||||
extern int ksu_handle_execveat_ksud(int *fd, struct filename **filename_ptr,
|
||||
void *argv, void *envp, int *flags);
|
||||
|
||||
int ksu_handle_execveat(int *fd, struct filename **filename_ptr, void *argv,
|
||||
void *envp, int *flags)
|
||||
{
|
||||
ksu_handle_execveat_ksud(fd, filename_ptr, argv, envp, flags);
|
||||
return ksu_handle_execveat_sucompat(fd, filename_ptr, argv, envp,
|
||||
flags);
|
||||
}
|
||||
|
||||
extern void ksu_sucompat_init();
|
||||
extern void ksu_sucompat_exit();
|
||||
extern void ksu_ksud_init();
|
||||
|
||||
@@ -2,33 +2,20 @@
|
||||
|
||||
|
||||
// extern kernelsu functions
|
||||
extern bool ksu_execveat_hook __read_mostly;
|
||||
extern bool ksu_vfs_read_hook __read_mostly;
|
||||
extern bool ksu_input_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);
|
||||
extern int ksu_handle_faccessat(int *dfd, const char __user **filename_user, int *mode, int *flags);
|
||||
extern int ksu_handle_sys_read(unsigned int fd, char __user **buf_ptr, size_t *count_ptr);
|
||||
extern int ksu_handle_stat(int *dfd, const char __user **filename_user, int *flags);
|
||||
extern int ksu_handle_input_handle_event(unsigned int *type, unsigned int *code, int *value);
|
||||
extern int ksu_handle_devpts(struct inode*);
|
||||
// end kernelsu functions
|
||||
|
||||
|
||||
// tracepoint callback functions
|
||||
void ksu_trace_execveat_hook_callback(void *data, int *fd, struct filename **filename_ptr,
|
||||
void *argv, void *envp, int *flags)
|
||||
{
|
||||
if (unlikely(ksu_execveat_hook))
|
||||
ksu_handle_execveat(fd, filename_ptr, argv, envp, flags);
|
||||
else
|
||||
ksu_handle_execveat_sucompat(fd, filename_ptr, NULL, NULL, NULL);
|
||||
}
|
||||
|
||||
void ksu_trace_execveat_sucompat_hook_callback(void *data, int *fd, struct filename **filename_ptr,
|
||||
void *argv, void *envp, int *flags)
|
||||
{
|
||||
if (!ksu_execveat_hook)
|
||||
ksu_handle_execveat_sucompat(fd, filename_ptr, argv, envp, flags);
|
||||
}
|
||||
|
||||
@@ -58,33 +45,25 @@ void ksu_trace_input_hook_callback(void *data, unsigned int *type, unsigned int
|
||||
ksu_handle_input_handle_event(type, code, value);
|
||||
}
|
||||
|
||||
void ksu_trace_devpts_hook_callback(void *data, struct inode *inode)
|
||||
{
|
||||
ksu_handle_devpts(inode);
|
||||
}
|
||||
// end tracepoint callback functions
|
||||
|
||||
|
||||
// register tracepoint callback functions
|
||||
void ksu_trace_register(void)
|
||||
{
|
||||
register_trace_ksu_trace_execveat_hook(ksu_trace_execveat_hook_callback, NULL);
|
||||
register_trace_ksu_trace_execveat_sucompat_hook(ksu_trace_execveat_sucompat_hook_callback, NULL);
|
||||
register_trace_ksu_trace_faccessat_hook(ksu_trace_faccessat_hook_callback, NULL);
|
||||
register_trace_ksu_trace_sys_read_hook(ksu_trace_sys_read_hook_callback, NULL);
|
||||
register_trace_ksu_trace_stat_hook(ksu_trace_stat_hook_callback, NULL);
|
||||
register_trace_ksu_trace_input_hook(ksu_trace_input_hook_callback, NULL);
|
||||
register_trace_ksu_trace_devpts_hook(ksu_trace_devpts_hook_callback, NULL);
|
||||
}
|
||||
|
||||
// unregister tracepoint callback functions
|
||||
void ksu_trace_unregister(void)
|
||||
{
|
||||
unregister_trace_ksu_trace_execveat_hook(ksu_trace_execveat_hook_callback, NULL);
|
||||
unregister_trace_ksu_trace_execveat_sucompat_hook(ksu_trace_execveat_sucompat_hook_callback, NULL);
|
||||
unregister_trace_ksu_trace_faccessat_hook(ksu_trace_faccessat_hook_callback, NULL);
|
||||
unregister_trace_ksu_trace_sys_read_hook(ksu_trace_sys_read_hook_callback, NULL);
|
||||
unregister_trace_ksu_trace_stat_hook(ksu_trace_stat_hook_callback, NULL);
|
||||
unregister_trace_ksu_trace_input_hook(ksu_trace_input_hook_callback, NULL);
|
||||
unregister_trace_ksu_trace_devpts_hook(ksu_trace_devpts_hook_callback, NULL);
|
||||
}
|
||||
|
||||
@@ -7,10 +7,6 @@
|
||||
#include <linux/fs.h>
|
||||
#include <linux/tracepoint.h>
|
||||
|
||||
DECLARE_TRACE(ksu_trace_execveat_hook,
|
||||
TP_PROTO(int *fd, struct filename **filename_ptr, void *argv, void *envp, int *flags),
|
||||
TP_ARGS(fd, filename_ptr, argv, envp, flags));
|
||||
|
||||
DECLARE_TRACE(ksu_trace_execveat_sucompat_hook,
|
||||
TP_PROTO(int *fd, struct filename **filename_ptr, void *argv, void *envp, int *flags),
|
||||
TP_ARGS(fd, filename_ptr, argv, envp, flags));
|
||||
@@ -31,10 +27,6 @@ DECLARE_TRACE(ksu_trace_input_hook,
|
||||
TP_PROTO(unsigned int *type, unsigned int *code, int *value),
|
||||
TP_ARGS(type, code, value));
|
||||
|
||||
DECLARE_TRACE(ksu_trace_devpts_hook,
|
||||
TP_PROTO(struct inode *inode),
|
||||
TP_ARGS(inode));
|
||||
|
||||
#endif /* _KSU_TRACE_H */
|
||||
|
||||
#undef TRACE_INCLUDE_PATH
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
#define CREATE_TRACE_POINTS
|
||||
#include "ksu_trace.h"
|
||||
|
||||
EXPORT_TRACEPOINT_SYMBOL_GPL(ksu_trace_execveat_hook);
|
||||
EXPORT_TRACEPOINT_SYMBOL_GPL(ksu_trace_execveat_sucompat_hook);
|
||||
EXPORT_TRACEPOINT_SYMBOL_GPL(ksu_trace_faccessat_hook);
|
||||
EXPORT_TRACEPOINT_SYMBOL_GPL(ksu_trace_sys_read_hook);
|
||||
EXPORT_TRACEPOINT_SYMBOL_GPL(ksu_trace_stat_hook);
|
||||
EXPORT_TRACEPOINT_SYMBOL_GPL(ksu_trace_input_hook);
|
||||
EXPORT_TRACEPOINT_SYMBOL_GPL(ksu_trace_devpts_hook);
|
||||
|
||||
405
kernel/ksud.c
@@ -54,19 +54,15 @@ static struct work_struct stop_execve_hook_work;
|
||||
static struct work_struct stop_input_hook_work;
|
||||
#else
|
||||
bool ksu_vfs_read_hook __read_mostly = true;
|
||||
bool ksu_execveat_hook __read_mostly = true;
|
||||
bool ksu_input_hook __read_mostly = true;
|
||||
#endif
|
||||
bool ksu_execveat_hook __read_mostly = true;
|
||||
|
||||
u32 ksu_devpts_sid;
|
||||
|
||||
// Detect whether it is on or not
|
||||
static bool is_boot_phase = true;
|
||||
|
||||
#ifdef CONFIG_COMPAT
|
||||
bool ksu_is_compat __read_mostly = false;
|
||||
#endif
|
||||
|
||||
void on_post_fs_data(void)
|
||||
{
|
||||
static bool done = false;
|
||||
@@ -87,88 +83,11 @@ void on_post_fs_data(void)
|
||||
is_boot_phase = false;
|
||||
}
|
||||
|
||||
#define MAX_ARG_STRINGS 0x7FFFFFFF
|
||||
struct user_arg_ptr {
|
||||
#ifdef CONFIG_COMPAT
|
||||
bool is_compat;
|
||||
#endif
|
||||
union {
|
||||
const char __user *const __user *native;
|
||||
#ifdef CONFIG_COMPAT
|
||||
const compat_uptr_t __user *compat;
|
||||
#endif
|
||||
} ptr;
|
||||
};
|
||||
|
||||
static const char __user *get_user_arg_ptr(struct user_arg_ptr argv, int nr)
|
||||
// since _ksud handler only uses argv and envp for comparisons
|
||||
// this can probably work
|
||||
// adapted from ksu_handle_execveat_ksud
|
||||
static int ksu_handle_bprm_ksud(const char *filename, const char *argv1, const char *envp, size_t envp_len)
|
||||
{
|
||||
const char __user *native;
|
||||
|
||||
#ifdef CONFIG_COMPAT
|
||||
if (unlikely(argv.is_compat)) {
|
||||
compat_uptr_t compat;
|
||||
|
||||
if (get_user(compat, argv.ptr.compat + nr))
|
||||
return ERR_PTR(-EFAULT);
|
||||
|
||||
ksu_is_compat = true;
|
||||
return compat_ptr(compat);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (get_user(native, argv.ptr.native + nr))
|
||||
return ERR_PTR(-EFAULT);
|
||||
|
||||
return native;
|
||||
}
|
||||
|
||||
/*
|
||||
* count() counts the number of strings in array ARGV.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Make sure old GCC compiler can use __maybe_unused,
|
||||
* Test passed in 4.4.x ~ 4.9.x when use GCC.
|
||||
*/
|
||||
|
||||
static int __maybe_unused count(struct user_arg_ptr argv, int max)
|
||||
{
|
||||
int i = 0;
|
||||
|
||||
if (argv.ptr.native != NULL) {
|
||||
for (;;) {
|
||||
const char __user *p = get_user_arg_ptr(argv, i);
|
||||
|
||||
if (!p)
|
||||
break;
|
||||
|
||||
if (IS_ERR(p))
|
||||
return -EFAULT;
|
||||
|
||||
if (i >= max)
|
||||
return -E2BIG;
|
||||
++i;
|
||||
|
||||
if (fatal_signal_pending(current))
|
||||
return -ERESTARTNOHAND;
|
||||
cond_resched();
|
||||
}
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
// IMPORTANT NOTE: the call from execve_handler_pre WON'T provided correct value for envp and flags in GKI version
|
||||
int ksu_handle_execveat_ksud(int *fd, struct filename **filename_ptr,
|
||||
struct user_arg_ptr *argv,
|
||||
struct user_arg_ptr *envp, int *flags)
|
||||
{
|
||||
#ifndef CONFIG_KSU_KPROBES_HOOK
|
||||
if (!ksu_execveat_hook) {
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
struct filename *filename;
|
||||
|
||||
static const char app_process[] = "/system/bin/app_process";
|
||||
static bool first_app_process = true;
|
||||
|
||||
@@ -178,113 +97,142 @@ int ksu_handle_execveat_ksud(int *fd, struct filename **filename_ptr,
|
||||
static const char old_system_init[] = "/init";
|
||||
static bool init_second_stage_executed = false;
|
||||
|
||||
if (!filename_ptr)
|
||||
// return early when disabled
|
||||
if (!ksu_execveat_hook)
|
||||
return 0;
|
||||
|
||||
filename = *filename_ptr;
|
||||
if (IS_ERR(filename)) {
|
||||
if (!filename)
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (unlikely(!memcmp(filename->name, system_bin_init,
|
||||
sizeof(system_bin_init) - 1) &&
|
||||
argv)) {
|
||||
// /system/bin/init executed
|
||||
int argc = count(*argv, MAX_ARG_STRINGS);
|
||||
pr_info("/system/bin/init argc: %d\n", argc);
|
||||
if (argc > 1 && !init_second_stage_executed) {
|
||||
const char __user *p = get_user_arg_ptr(*argv, 1);
|
||||
if (p && !IS_ERR(p)) {
|
||||
char first_arg[16];
|
||||
ksu_strncpy_from_user_nofault(
|
||||
first_arg, p, sizeof(first_arg));
|
||||
pr_info("/system/bin/init first arg: %s\n",
|
||||
first_arg);
|
||||
if (!strcmp(first_arg, "second_stage")) {
|
||||
pr_info("/system/bin/init second_stage executed\n");
|
||||
// debug! remove me!
|
||||
pr_info("%s: filename: %s argv1: %s envp_len: %zu\n", __func__, filename, argv1, envp_len);
|
||||
|
||||
#ifdef CONFIG_KSU_DEBUG
|
||||
const char *envp_n = envp;
|
||||
unsigned int envc = 1;
|
||||
do {
|
||||
pr_info("%s: envp[%d]: %s\n", __func__, envc, envp_n);
|
||||
envp_n += strlen(envp_n) + 1;
|
||||
envc++;
|
||||
} while (envp_n < envp + 256);
|
||||
#endif
|
||||
|
||||
if (init_second_stage_executed)
|
||||
goto first_app_process;
|
||||
|
||||
// /system/bin/init with argv1
|
||||
if (!init_second_stage_executed
|
||||
&& (!memcmp(filename, system_bin_init, sizeof(system_bin_init) - 1))) {
|
||||
if (argv1 && !strcmp(argv1, "second_stage")) {
|
||||
pr_info("%s: /system/bin/init second_stage executed\n", __func__);
|
||||
apply_kernelsu_rules();
|
||||
init_second_stage_executed = true;
|
||||
ksu_android_ns_fs_check();
|
||||
}
|
||||
} else {
|
||||
pr_err("/system/bin/init parse args err!\n");
|
||||
}
|
||||
}
|
||||
} else if (unlikely(!memcmp(filename->name, old_system_init,
|
||||
sizeof(old_system_init) - 1) &&
|
||||
argv)) {
|
||||
// /init executed
|
||||
int argc = count(*argv, MAX_ARG_STRINGS);
|
||||
pr_info("/init argc: %d\n", argc);
|
||||
if (argc > 1 && !init_second_stage_executed) {
|
||||
/* This applies to versions between Android 6 ~ 7 */
|
||||
const char __user *p = get_user_arg_ptr(*argv, 1);
|
||||
if (p && !IS_ERR(p)) {
|
||||
char first_arg[16];
|
||||
ksu_strncpy_from_user_nofault(
|
||||
first_arg, p, sizeof(first_arg));
|
||||
pr_info("/init first arg: %s\n", first_arg);
|
||||
if (!strcmp(first_arg, "--second-stage")) {
|
||||
pr_info("/init second_stage executed\n");
|
||||
|
||||
// /init with argv1
|
||||
if (!init_second_stage_executed
|
||||
&& (!memcmp(filename, old_system_init, sizeof(old_system_init) - 1))) {
|
||||
if (argv1 && !strcmp(argv1, "--second-stage")) {
|
||||
pr_info("%s: /init --second-stage executed\n", __func__);
|
||||
apply_kernelsu_rules();
|
||||
init_second_stage_executed = true;
|
||||
ksu_android_ns_fs_check();
|
||||
}
|
||||
} else {
|
||||
pr_err("/init parse args err!\n");
|
||||
}
|
||||
} else if (argc == 1 && !init_second_stage_executed && envp) {
|
||||
/* This applies to versions between Android 8 ~ 9 */
|
||||
int envc = count(*envp, MAX_ARG_STRINGS);
|
||||
if (envc > 0) {
|
||||
int n;
|
||||
for (n = 1; n <= envc; n++) {
|
||||
const char __user *p =
|
||||
get_user_arg_ptr(*envp, n);
|
||||
if (!p || IS_ERR(p)) {
|
||||
continue;
|
||||
}
|
||||
char env[256];
|
||||
// Reading environment variable strings from user space
|
||||
if (ksu_strncpy_from_user_nofault(
|
||||
env, p, sizeof(env)) < 0)
|
||||
continue;
|
||||
// Parsing environment variable names and values
|
||||
char *env_name = env;
|
||||
char *env_value = strchr(env, '=');
|
||||
if (env_value == NULL)
|
||||
continue;
|
||||
// Replace equal sign with string terminator
|
||||
*env_value = '\0';
|
||||
env_value++;
|
||||
// Check if the environment variable name and value are matching
|
||||
if (!strcmp(env_name,
|
||||
"INIT_SECOND_STAGE") &&
|
||||
(!strcmp(env_value, "1") ||
|
||||
!strcmp(env_value, "true"))) {
|
||||
pr_info("/init second_stage executed\n");
|
||||
|
||||
if (!envp || !envp_len)
|
||||
goto first_app_process;
|
||||
|
||||
// /init without argv1/useless-argv1 but usable envp
|
||||
// untested! TODO: test and debug me!
|
||||
if (!init_second_stage_executed && (!memcmp(filename, old_system_init, sizeof(old_system_init) - 1))) {
|
||||
|
||||
// we hunt for "INIT_SECOND_STAGE"
|
||||
const char *envp_n = envp;
|
||||
unsigned int envc = 1;
|
||||
do {
|
||||
if (strstarts(envp_n, "INIT_SECOND_STAGE"))
|
||||
break;
|
||||
envp_n += strlen(envp_n) + 1;
|
||||
envc++;
|
||||
} while (envp_n < envp + envp_len);
|
||||
pr_info("%s: envp[%d]: %s\n", __func__, envc, envp_n);
|
||||
|
||||
if (!strcmp(envp_n, "INIT_SECOND_STAGE=1")
|
||||
|| !strcmp(envp_n, "INIT_SECOND_STAGE=true") ) {
|
||||
pr_info("%s: /init +envp: INIT_SECOND_STAGE executed\n", __func__);
|
||||
apply_kernelsu_rules();
|
||||
init_second_stage_executed =
|
||||
true;
|
||||
init_second_stage_executed = true;
|
||||
ksu_android_ns_fs_check();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (unlikely(first_app_process && !memcmp(filename->name, app_process,
|
||||
sizeof(app_process) - 1))) {
|
||||
first_app_process:
|
||||
if (first_app_process && !memcmp(filename, app_process, sizeof(app_process) - 1)) {
|
||||
first_app_process = false;
|
||||
pr_info("exec app_process, /data prepared, second_stage: %d\n",
|
||||
init_second_stage_executed);
|
||||
on_post_fs_data(); // we keep this for old ksud
|
||||
pr_info("%s: exec app_process, /data prepared, second_stage: %d\n", __func__, init_second_stage_executed);
|
||||
on_post_fs_data();
|
||||
stop_execve_hook();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ksu_handle_pre_ksud(const char *filename)
|
||||
{
|
||||
if (likely(!ksu_execveat_hook))
|
||||
return 0;
|
||||
|
||||
// not /system/bin/init, not /init, not /system/bin/app_process (64/32 thingy)
|
||||
// return 0;
|
||||
if (likely(strcmp(filename, "/system/bin/init") && strcmp(filename, "/init")
|
||||
&& !strstarts(filename, "/system/bin/app_process") ))
|
||||
return 0;
|
||||
|
||||
if (!current || !current->mm)
|
||||
return 0;
|
||||
|
||||
// https://elixir.bootlin.com/linux/v4.14.1/source/include/linux/mm_types.h#L429
|
||||
// unsigned long arg_start, arg_end, env_start, env_end;
|
||||
unsigned long arg_start = current->mm->arg_start;
|
||||
unsigned long arg_end = current->mm->arg_end;
|
||||
unsigned long env_start = current->mm->env_start;
|
||||
unsigned long env_end = current->mm->env_end;
|
||||
|
||||
size_t arg_len = arg_end - arg_start;
|
||||
size_t envp_len = env_end - env_start;
|
||||
|
||||
if (arg_len <= 0 || envp_len <= 0) // this wont make sense, filter it
|
||||
return 0;
|
||||
|
||||
#define ARGV_MAX 32 // this is enough for argv1
|
||||
#define ENVP_MAX 256 // this is enough for INIT_SECOND_STAGE
|
||||
char args[ARGV_MAX];
|
||||
size_t argv_copy_len = (arg_len > ARGV_MAX) ? ARGV_MAX : arg_len;
|
||||
char envp[ENVP_MAX];
|
||||
size_t envp_copy_len = (envp_len > ENVP_MAX) ? ENVP_MAX : envp_len;
|
||||
|
||||
// we cant use strncpy on here, else it will truncate once it sees \0
|
||||
if (ksu_copy_from_user_retry(args, (void __user *)arg_start, argv_copy_len))
|
||||
return 0;
|
||||
|
||||
if (ksu_copy_from_user_retry(envp, (void __user *)env_start, envp_copy_len))
|
||||
return 0;
|
||||
|
||||
args[ARGV_MAX - 1] = '\0';
|
||||
envp[ENVP_MAX - 1] = '\0';
|
||||
|
||||
// we only need argv1 !
|
||||
// abuse strlen here since it only gets length up to \0
|
||||
char *argv1 = args + strlen(args) + 1;
|
||||
if (argv1 >= args + argv_copy_len) // out of bounds!
|
||||
argv1 = "";
|
||||
|
||||
return ksu_handle_bprm_ksud(filename, argv1, envp, envp_copy_len);
|
||||
}
|
||||
|
||||
static ssize_t (*orig_read)(struct file *, char __user *, size_t, loff_t *);
|
||||
static ssize_t (*orig_read_iter)(struct kiocb *, struct iov_iter *);
|
||||
static struct file_operations fops_proxy;
|
||||
@@ -476,25 +424,121 @@ bool ksu_is_safe_mode()
|
||||
#ifdef CONFIG_KSU_KPROBES_HOOK
|
||||
static int sys_execve_handler_pre(struct kprobe *p, struct pt_regs *regs)
|
||||
{
|
||||
/*
|
||||
asmlinkage int sys_execve(const char __user *filenamei,
|
||||
const char __user *const __user *argv,
|
||||
const char __user *const __user *envp, struct pt_regs *regs)
|
||||
*/
|
||||
struct pt_regs *real_regs = PT_REAL_REGS(regs);
|
||||
const char __user **filename_user =
|
||||
(const char **)&PT_REGS_PARM1(real_regs);
|
||||
const char __user *const __user *__argv =
|
||||
(const char __user *const __user *)PT_REGS_PARM2(real_regs);
|
||||
struct user_arg_ptr argv = { .ptr.native = __argv };
|
||||
struct filename filename_in, *filename_p;
|
||||
const char __user *filename_user = (const char __user *)PT_REGS_PARM1(real_regs);
|
||||
const char __user *const __user *__argv = (const char __user *const __user *)PT_REGS_PARM2(real_regs);
|
||||
const char __user *const __user *__envp = (const char __user *const __user *)PT_REGS_PARM3(real_regs);
|
||||
char path[32];
|
||||
|
||||
if (!filename_user)
|
||||
return 0;
|
||||
|
||||
memset(path, 0, sizeof(path));
|
||||
ksu_strncpy_from_user_nofault(path, *filename_user, 32);
|
||||
filename_in.name = path;
|
||||
// filename stage
|
||||
if (ksu_copy_from_user_retry(path, filename_user, sizeof(path)))
|
||||
return 0;
|
||||
|
||||
filename_p = &filename_in;
|
||||
return ksu_handle_execveat_ksud(AT_FDCWD, &filename_p, &argv, NULL,
|
||||
NULL);
|
||||
path[sizeof(path) - 1] = '\0';
|
||||
|
||||
// not /system/bin/init, not /init, not /system/bin/app_process (64/32 thingy)
|
||||
// we dont care !!
|
||||
if (likely(strcmp(path, "/system/bin/init") && strcmp(path, "/init")
|
||||
&& !strstarts(path, "/system/bin/app_process") ))
|
||||
return 0;
|
||||
|
||||
// argv stage
|
||||
char argv1[32] = {0};
|
||||
// memzero_explicit(argv1, 32);
|
||||
if (__argv) {
|
||||
const char __user *arg1_user = NULL;
|
||||
// grab argv[1] pointer
|
||||
// this looks like
|
||||
/*
|
||||
* 0x1000 ./program << this is __argv
|
||||
* 0x1001 -o
|
||||
* 0x1002 arg
|
||||
*/
|
||||
if (ksu_copy_from_user_retry(&arg1_user, __argv + 1, sizeof(arg1_user)))
|
||||
goto no_argv1; // copy argv[1] pointer fail, probably no argv1 !!
|
||||
|
||||
if (arg1_user)
|
||||
ksu_copy_from_user_retry(argv1, arg1_user, sizeof(argv1));
|
||||
}
|
||||
|
||||
no_argv1:
|
||||
argv1[sizeof(argv1) - 1] = '\0';
|
||||
|
||||
// envp stage
|
||||
#define ENVP_MAX 256
|
||||
char envp[ENVP_MAX] = {0};
|
||||
char *dst = envp;
|
||||
size_t envp_len = 0;
|
||||
int i = 0; // to track user pointer offset from __envp
|
||||
|
||||
// memzero_explicit(envp, ENVP_MAX);
|
||||
|
||||
if (__envp) {
|
||||
do {
|
||||
const char __user *env_entry_user = NULL;
|
||||
// this is also like argv above
|
||||
/*
|
||||
* 0x1001 PATH=/bin
|
||||
* 0x1002 VARIABLE=value
|
||||
* 0x1002 some_more_env_var=1
|
||||
*/
|
||||
|
||||
// check if pointer exists
|
||||
if (ksu_copy_from_user_retry(&env_entry_user, __envp + i, sizeof(env_entry_user)))
|
||||
break;
|
||||
|
||||
// check if no more env entry
|
||||
if (!env_entry_user)
|
||||
break;
|
||||
|
||||
// probably redundant to while condition but ok
|
||||
if (envp_len >= ENVP_MAX - 1)
|
||||
break;
|
||||
|
||||
// copy strings from env_entry_user pointer that we collected
|
||||
// also break if failed
|
||||
if (ksu_copy_from_user_retry(dst, env_entry_user, ENVP_MAX - envp_len))
|
||||
break;
|
||||
|
||||
// get the length of that new copy above
|
||||
// get lngth of dst as far as ENVP_MAX - current collected envp_len
|
||||
size_t len = strnlen(dst, ENVP_MAX - envp_len);
|
||||
if (envp_len + len + 1 > ENVP_MAX)
|
||||
break; // if more than 255 bytes, bail
|
||||
|
||||
dst[len] = '\0';
|
||||
// collect total number of copied strings
|
||||
envp_len = envp_len + len + 1;
|
||||
// increment dst address since we need to put something on next iter
|
||||
dst = dst + len + 1;
|
||||
// pointer walk, __envp + i
|
||||
i++;
|
||||
} while (envp_len < ENVP_MAX);
|
||||
}
|
||||
|
||||
/*
|
||||
at this point, we shoul've collected envp from
|
||||
* 0x1001 PATH=/bin
|
||||
* 0x1002 VARIABLE=value
|
||||
* 0x1002 some_more_env_var=1
|
||||
to
|
||||
* 0x1234 PATH=/bin\0VARIABLE=value\0some_more_env_var=1\0\0\0\0
|
||||
*/
|
||||
|
||||
envp[ENVP_MAX - 1] = '\0';
|
||||
|
||||
#ifdef CONFIG_KSU_DEBUG
|
||||
pr_info("%s: filename: %s argv[1]:%s envp_len: %zu\n", __func__, path, argv1, envp_len);
|
||||
#endif
|
||||
return ksu_handle_bprm_ksud(path, argv1, envp, envp_len);
|
||||
}
|
||||
|
||||
static int sys_read_handler_pre(struct kprobe *p, struct pt_regs *regs)
|
||||
@@ -531,6 +575,7 @@ static struct kprobe input_event_kp = {
|
||||
.pre_handler = input_handle_event_handler_pre,
|
||||
};
|
||||
|
||||
|
||||
static void do_stop_vfs_read_hook(struct work_struct *work)
|
||||
{
|
||||
unregister_kprobe(&vfs_read_kp);
|
||||
@@ -564,9 +609,9 @@ static void stop_execve_hook()
|
||||
bool ret = schedule_work(&stop_execve_hook_work);
|
||||
pr_info("unregister execve kprobe: %d!\n", ret);
|
||||
#else
|
||||
ksu_execveat_hook = false;
|
||||
pr_info("stop execve_hook\n");
|
||||
#endif
|
||||
ksu_execveat_hook = false;
|
||||
}
|
||||
|
||||
static void stop_input_hook()
|
||||
|
||||
@@ -8,4 +8,8 @@ void on_post_fs_data(void);
|
||||
bool ksu_is_safe_mode(void);
|
||||
|
||||
extern u32 ksu_devpts_sid;
|
||||
|
||||
extern bool ksu_execveat_hook __read_mostly;
|
||||
extern int ksu_handle_pre_ksud(const char *filename);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -20,7 +20,8 @@ static inline bool ksu_is_manager_uid_valid()
|
||||
|
||||
static inline bool is_manager()
|
||||
{
|
||||
return unlikely(ksu_is_any_manager(current_uid().val) || ksu_manager_uid == current_uid().val);
|
||||
return unlikely(ksu_is_any_manager(current_uid().val) ||
|
||||
(ksu_manager_uid != KSU_INVALID_UID && ksu_manager_uid == current_uid().val));
|
||||
}
|
||||
|
||||
static inline uid_t ksu_get_manager_uid()
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
extern void escape_to_root();
|
||||
|
||||
#ifndef CONFIG_KSU_KPROBES_HOOK
|
||||
static bool ksu_sucompat_non_kp __read_mostly = true;
|
||||
static bool ksu_sucompat_hook_state __read_mostly = true;
|
||||
#endif
|
||||
|
||||
static void __user *userspace_stack_buffer(const void *d, size_t len)
|
||||
@@ -55,7 +55,7 @@ int ksu_handle_faccessat(int *dfd, const char __user **filename_user, int *mode,
|
||||
const char su[] = SU_PATH;
|
||||
|
||||
#ifndef CONFIG_KSU_KPROBES_HOOK
|
||||
if (!ksu_sucompat_non_kp) {
|
||||
if (!ksu_sucompat_hook_state) {
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
@@ -82,7 +82,7 @@ int ksu_handle_stat(int *dfd, const char __user **filename_user, int *flags)
|
||||
const char su[] = SU_PATH;
|
||||
|
||||
#ifndef CONFIG_KSU_KPROBES_HOOK
|
||||
if (!ksu_sucompat_non_kp) {
|
||||
if (!ksu_sucompat_hook_state) {
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
@@ -121,6 +121,12 @@ int ksu_handle_stat(int *dfd, const char __user **filename_user, int *flags)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ksu_handle_execveat(int *fd, struct filename **filename_ptr, void *argv,
|
||||
void *envp, int *flags)
|
||||
{
|
||||
return ksu_handle_execveat_sucompat(fd, filename_ptr, argv, envp, flags);
|
||||
}
|
||||
|
||||
// the call from execve_handler_pre won't provided correct value for __never_use_argument, use them after fix execve_handler_pre, keeping them for consistence for manually patched code
|
||||
int ksu_handle_execveat_sucompat(int *fd, struct filename **filename_ptr,
|
||||
void *__never_use_argv, void *__never_use_envp,
|
||||
@@ -131,7 +137,7 @@ int ksu_handle_execveat_sucompat(int *fd, struct filename **filename_ptr,
|
||||
const char su[] = SU_PATH;
|
||||
|
||||
#ifndef CONFIG_KSU_KPROBES_HOOK
|
||||
if (!ksu_sucompat_non_kp) {
|
||||
if (!ksu_sucompat_hook_state) {
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
@@ -165,7 +171,7 @@ int ksu_handle_execve_sucompat(int *fd, const char __user **filename_user,
|
||||
char path[sizeof(su) + 1];
|
||||
|
||||
#ifndef CONFIG_KSU_KPROBES_HOOK
|
||||
if (!ksu_sucompat_non_kp){
|
||||
if (!ksu_sucompat_hook_state){
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
@@ -189,8 +195,20 @@ int ksu_handle_execve_sucompat(int *fd, const char __user **filename_user,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ksu_inline_handle_devpts(struct inode *inode)
|
||||
// dummified
|
||||
int ksu_handle_devpts(struct inode *inode)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int __ksu_handle_devpts(struct inode *inode)
|
||||
{
|
||||
|
||||
#ifndef CONFIG_KSU_KPROBES_HOOK
|
||||
if (!ksu_sucompat_hook_state)
|
||||
return 0;
|
||||
#endif
|
||||
|
||||
if (!current->mm) {
|
||||
return 0;
|
||||
}
|
||||
@@ -201,35 +219,17 @@ static int ksu_inline_handle_devpts(struct inode *inode)
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!ksu_is_allow_uid(uid))
|
||||
if (likely(!ksu_is_allow_uid(uid)))
|
||||
return 0;
|
||||
|
||||
if (ksu_devpts_sid) {
|
||||
struct inode_security_struct *sec = selinux_inode(inode);
|
||||
if (sec) {
|
||||
|
||||
if (ksu_devpts_sid && sec)
|
||||
sec->sid = ksu_devpts_sid;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int __ksu_handle_devpts(struct inode *inode)
|
||||
{
|
||||
#ifndef CONFIG_KSU_KPROBES_HOOK
|
||||
if (!ksu_sucompat_non_kp) {
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
return ksu_inline_handle_devpts(inode);
|
||||
}
|
||||
|
||||
// dead code, we are phasing out ksu_handle_devpts for LSM hooks.
|
||||
int __maybe_unused ksu_handle_devpts(struct inode *inode)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_KSU_KPROBES_HOOK
|
||||
static int faccessat_handler_pre(struct kprobe *p, struct pt_regs *regs)
|
||||
{
|
||||
@@ -263,7 +263,6 @@ static int execve_handler_pre(struct kprobe *p, struct pt_regs *regs)
|
||||
NULL);
|
||||
}
|
||||
|
||||
#ifdef MODULE
|
||||
static struct kprobe *su_kps[4];
|
||||
static int pts_unix98_lookup_pre(struct kprobe *p, struct pt_regs *regs)
|
||||
{
|
||||
@@ -275,11 +274,8 @@ static int pts_unix98_lookup_pre(struct kprobe *p, struct pt_regs *regs)
|
||||
inode = (struct inode *)PT_REGS_PARM2(regs);
|
||||
#endif
|
||||
|
||||
return ksu_inline_handle_devpts(inode);
|
||||
return ksu_handle_devpts(inode);
|
||||
}
|
||||
#else
|
||||
static struct kprobe *su_kps[3];
|
||||
#endif
|
||||
|
||||
static struct kprobe *init_kprobe(const char *name,
|
||||
kprobe_pre_handler_t handler)
|
||||
@@ -320,11 +316,9 @@ void ksu_sucompat_init()
|
||||
su_kps[0] = init_kprobe(SYS_EXECVE_SYMBOL, execve_handler_pre);
|
||||
su_kps[1] = init_kprobe(SYS_FACCESSAT_SYMBOL, faccessat_handler_pre);
|
||||
su_kps[2] = init_kprobe(SYS_NEWFSTATAT_SYMBOL, newfstatat_handler_pre);
|
||||
#ifdef MODULE
|
||||
su_kps[3] = init_kprobe("pts_unix98_lookup", pts_unix98_lookup_pre);
|
||||
#endif
|
||||
#else
|
||||
ksu_sucompat_non_kp = true;
|
||||
ksu_sucompat_hook_state = true;
|
||||
pr_info("ksu_sucompat_init: hooks enabled: execve/execveat_su, faccessat, stat\n");
|
||||
#endif
|
||||
}
|
||||
@@ -332,11 +326,12 @@ void ksu_sucompat_init()
|
||||
void ksu_sucompat_exit()
|
||||
{
|
||||
#ifdef CONFIG_KSU_KPROBES_HOOK
|
||||
for (int i = 0; i < ARRAY_SIZE(su_kps); i++) {
|
||||
int i;
|
||||
for (i = 0; i < ARRAY_SIZE(su_kps); i++) {
|
||||
destroy_kprobe(&su_kps[i]);
|
||||
}
|
||||
#else
|
||||
ksu_sucompat_non_kp = false;
|
||||
ksu_sucompat_hook_state = false;
|
||||
pr_info("ksu_sucompat_exit: hooks disabled: execve/execveat_su, faccessat, stat\n");
|
||||
#endif
|
||||
}
|
||||
|
||||
125
kernel/throne_comm.c
Normal file
@@ -0,0 +1,125 @@
|
||||
#include <linux/fs.h>
|
||||
#include <linux/proc_fs.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/version.h>
|
||||
|
||||
#include "klog.h"
|
||||
#include "throne_comm.h"
|
||||
|
||||
#define PROC_UID_SCANNER "ksu_uid_scanner"
|
||||
|
||||
static struct proc_dir_entry *proc_entry = NULL;
|
||||
static struct workqueue_struct *scanner_wq = NULL;
|
||||
static struct work_struct scan_work;
|
||||
|
||||
// Signal userspace to rescan
|
||||
static bool need_rescan = false;
|
||||
|
||||
static void rescan_work_fn(struct work_struct *work)
|
||||
{
|
||||
// Signal userspace through proc interface
|
||||
need_rescan = true;
|
||||
pr_info("requested userspace uid rescan\n");
|
||||
}
|
||||
|
||||
void ksu_request_userspace_scan(void)
|
||||
{
|
||||
if (scanner_wq) {
|
||||
queue_work(scanner_wq, &scan_work);
|
||||
}
|
||||
}
|
||||
|
||||
void ksu_handle_userspace_update(void)
|
||||
{
|
||||
// Called when userspace notifies update complete
|
||||
need_rescan = false;
|
||||
pr_info("userspace uid list updated\n");
|
||||
}
|
||||
|
||||
static int uid_scanner_show(struct seq_file *m, void *v)
|
||||
{
|
||||
if (need_rescan) {
|
||||
seq_puts(m, "RESCAN\n");
|
||||
} else {
|
||||
seq_puts(m, "OK\n");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int uid_scanner_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
return single_open(file, uid_scanner_show, NULL);
|
||||
}
|
||||
|
||||
static ssize_t uid_scanner_write(struct file *file, const char __user *buffer,
|
||||
size_t count, loff_t *pos)
|
||||
{
|
||||
char cmd[16];
|
||||
|
||||
if (count >= sizeof(cmd))
|
||||
return -EINVAL;
|
||||
|
||||
if (copy_from_user(cmd, buffer, count))
|
||||
return -EFAULT;
|
||||
|
||||
cmd[count] = '\0';
|
||||
|
||||
// Remove newline if present
|
||||
if (count > 0 && cmd[count-1] == '\n')
|
||||
cmd[count-1] = '\0';
|
||||
|
||||
if (strcmp(cmd, "UPDATED") == 0) {
|
||||
ksu_handle_userspace_update();
|
||||
pr_info("received userspace update notification\n");
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static const struct proc_ops uid_scanner_proc_ops = {
|
||||
.proc_open = uid_scanner_open,
|
||||
.proc_read = seq_read,
|
||||
.proc_write = uid_scanner_write,
|
||||
.proc_lseek = seq_lseek,
|
||||
.proc_release = single_release,
|
||||
};
|
||||
|
||||
int ksu_throne_comm_init(void)
|
||||
{
|
||||
// Create workqueue
|
||||
scanner_wq = alloc_workqueue("ksu_scanner", WQ_UNBOUND, 1);
|
||||
if (!scanner_wq) {
|
||||
pr_err("failed to create scanner workqueue\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
INIT_WORK(&scan_work, rescan_work_fn);
|
||||
|
||||
// Create proc entry
|
||||
proc_entry = proc_create(PROC_UID_SCANNER, 0600, NULL, &uid_scanner_proc_ops);
|
||||
if (!proc_entry) {
|
||||
pr_err("failed to create proc entry\n");
|
||||
destroy_workqueue(scanner_wq);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
pr_info("throne communication initialized\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ksu_throne_comm_exit(void)
|
||||
{
|
||||
if (proc_entry) {
|
||||
proc_remove(proc_entry);
|
||||
proc_entry = NULL;
|
||||
}
|
||||
|
||||
if (scanner_wq) {
|
||||
destroy_workqueue(scanner_wq);
|
||||
scanner_wq = NULL;
|
||||
}
|
||||
|
||||
pr_info("throne communication cleaned up\n");
|
||||
}
|
||||
12
kernel/throne_comm.h
Normal file
@@ -0,0 +1,12 @@
|
||||
#ifndef __KSU_H_THRONE_COMM
|
||||
#define __KSU_H_THRONE_COMM
|
||||
|
||||
void ksu_request_userspace_scan(void);
|
||||
|
||||
void ksu_handle_userspace_update(void);
|
||||
|
||||
int ksu_throne_comm_init(void);
|
||||
|
||||
void ksu_throne_comm_exit(void);
|
||||
|
||||
#endif
|
||||
@@ -5,6 +5,8 @@
|
||||
#include <linux/string.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/version.h>
|
||||
#include <linux/stat.h>
|
||||
#include <linux/namei.h>
|
||||
|
||||
#include "allowlist.h"
|
||||
#include "klog.h" // IWYU pragma: keep
|
||||
@@ -13,10 +15,13 @@
|
||||
#include "throne_tracker.h"
|
||||
#include "kernel_compat.h"
|
||||
#include "dynamic_manager.h"
|
||||
#include "throne_comm.h"
|
||||
|
||||
uid_t ksu_manager_uid = KSU_INVALID_UID;
|
||||
|
||||
#define SYSTEM_PACKAGES_LIST_PATH "/data/system/packages.list.tmp"
|
||||
#define KSU_UID_LIST_PATH "/data/misc/user_uid/uid_list"
|
||||
#define USER_DATA_PATH "/data/user_de/0"
|
||||
#define USER_DATA_PATH_LEN 256
|
||||
|
||||
struct uid_data {
|
||||
struct list_head list;
|
||||
@@ -24,6 +29,111 @@ struct uid_data {
|
||||
char package[KSU_MAX_PACKAGE_NAME];
|
||||
};
|
||||
|
||||
// Try read whitelist first, fallback if failed
|
||||
static int read_uid_whitelist(struct list_head *uid_list)
|
||||
{
|
||||
struct file *fp;
|
||||
char *file_content = NULL;
|
||||
char *line, *next_line;
|
||||
loff_t file_size;
|
||||
loff_t pos = 0;
|
||||
int count = 0;
|
||||
ssize_t bytes_read;
|
||||
|
||||
fp = ksu_filp_open_compat(KSU_UID_LIST_PATH, O_RDONLY, 0);
|
||||
if (IS_ERR(fp)) {
|
||||
pr_info("whitelist not found, fallback needed\n");
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
file_size = fp->f_inode->i_size;
|
||||
if (file_size <= 0) {
|
||||
pr_info("whitelist file is empty\n");
|
||||
filp_close(fp, NULL);
|
||||
return -ENODATA;
|
||||
}
|
||||
|
||||
file_content = kzalloc(file_size + 1, GFP_ATOMIC);
|
||||
if (!file_content) {
|
||||
pr_err("failed to allocate memory for whitelist file (%lld bytes)\n", file_size);
|
||||
filp_close(fp, NULL);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
bytes_read = ksu_kernel_read_compat(fp, file_content, file_size, &pos);
|
||||
if (bytes_read != file_size) {
|
||||
pr_err("failed to read whitelist file: read %zd bytes, expected %lld bytes\n",
|
||||
bytes_read, file_size);
|
||||
kfree(file_content);
|
||||
filp_close(fp, NULL);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
file_content[file_size] = '\0';
|
||||
filp_close(fp, NULL);
|
||||
|
||||
pr_info("successfully read whitelist file (%lld bytes), parsing lines...\n", file_size);
|
||||
|
||||
line = file_content;
|
||||
while (line && *line) {
|
||||
next_line = strchr(line, '\n');
|
||||
if (next_line) {
|
||||
*next_line = '\0';
|
||||
next_line++;
|
||||
}
|
||||
|
||||
char *trimmed_line = line;
|
||||
while (*trimmed_line == ' ' || *trimmed_line == '\t' || *trimmed_line == '\r') {
|
||||
trimmed_line++;
|
||||
}
|
||||
|
||||
if (strlen(trimmed_line) > 0) {
|
||||
char *line_copy = trimmed_line;
|
||||
char *uid_str = strsep(&line_copy, " \t");
|
||||
char *package_name = line_copy;
|
||||
|
||||
if (package_name) {
|
||||
while (*package_name == ' ' || *package_name == '\t') {
|
||||
package_name++;
|
||||
}
|
||||
}
|
||||
|
||||
if (uid_str && package_name && strlen(package_name) > 0) {
|
||||
u32 uid;
|
||||
if (!kstrtou32(uid_str, 10, &uid)) {
|
||||
struct uid_data *data = kzalloc(sizeof(struct uid_data), GFP_ATOMIC);
|
||||
if (data) {
|
||||
data->uid = uid;
|
||||
size_t pkg_len = strlen(package_name);
|
||||
size_t copy_len = min(pkg_len, (size_t)(KSU_MAX_PACKAGE_NAME - 1));
|
||||
strncpy(data->package, package_name, copy_len);
|
||||
data->package[copy_len] = '\0';
|
||||
|
||||
list_add_tail(&data->list, uid_list);
|
||||
count++;
|
||||
|
||||
if (count % 100 == 0) {
|
||||
pr_info("parsed %d packages so far...\n", count);
|
||||
}
|
||||
} else {
|
||||
pr_err("failed to allocate memory for uid_data\n");
|
||||
}
|
||||
} else {
|
||||
pr_warn("invalid uid format in line: %s\n", trimmed_line);
|
||||
}
|
||||
} else {
|
||||
pr_warn("invalid line format: %s\n", trimmed_line);
|
||||
}
|
||||
}
|
||||
|
||||
line = next_line;
|
||||
}
|
||||
|
||||
kfree(file_content);
|
||||
pr_info("successfully loaded %d uids from whitelist\n", count);
|
||||
return count > 0 ? 0 : -ENODATA;
|
||||
}
|
||||
|
||||
static int get_pkg_from_apk_path(char *pkg, const char *path)
|
||||
{
|
||||
int len = strlen(path);
|
||||
@@ -88,7 +198,8 @@ static void crown_manager(const char *apk, struct list_head *uid_data, int signa
|
||||
if (strncmp(np->package, pkg, KSU_MAX_PACKAGE_NAME) == 0) {
|
||||
pr_info("Crowning manager: %s(uid=%d, signature_index=%d)\n", pkg, np->uid, signature_index);
|
||||
|
||||
if (signature_index == 1 || signature_index == 2) {
|
||||
// Dynamic Sign index (1) or multi-manager signatures (2+)
|
||||
if (signature_index == DYNAMIC_SIGN_INDEX || signature_index >= 2) {
|
||||
ksu_add_manager(np->uid, signature_index);
|
||||
|
||||
if (!ksu_is_manager_uid_valid()) {
|
||||
@@ -138,6 +249,137 @@ struct my_dir_context {
|
||||
#define FILLDIR_ACTOR_STOP -EINVAL
|
||||
#endif
|
||||
|
||||
struct uid_scan_stats {
|
||||
size_t total_found;
|
||||
size_t errors_encountered;
|
||||
};
|
||||
|
||||
struct user_data_context {
|
||||
struct dir_context ctx;
|
||||
struct list_head *uid_list;
|
||||
struct uid_scan_stats *stats;
|
||||
};
|
||||
|
||||
FILLDIR_RETURN_TYPE user_data_actor(struct dir_context *ctx, const char *name,
|
||||
int namelen, loff_t off, u64 ino,
|
||||
unsigned int d_type)
|
||||
{
|
||||
struct user_data_context *my_ctx =
|
||||
container_of(ctx, struct user_data_context, ctx);
|
||||
|
||||
if (!my_ctx || !my_ctx->uid_list) {
|
||||
return FILLDIR_ACTOR_STOP;
|
||||
}
|
||||
|
||||
if (!strncmp(name, "..", namelen) || !strncmp(name, ".", namelen))
|
||||
return FILLDIR_ACTOR_CONTINUE;
|
||||
|
||||
if (d_type != DT_DIR)
|
||||
return FILLDIR_ACTOR_CONTINUE;
|
||||
|
||||
if (namelen >= KSU_MAX_PACKAGE_NAME) {
|
||||
pr_warn("Package name too long: %.*s\n", namelen, name);
|
||||
if (my_ctx->stats)
|
||||
my_ctx->stats->errors_encountered++;
|
||||
return FILLDIR_ACTOR_CONTINUE;
|
||||
}
|
||||
|
||||
char package_path[USER_DATA_PATH_LEN];
|
||||
if (snprintf(package_path, sizeof(package_path), "%s/%.*s",
|
||||
USER_DATA_PATH, namelen, name) >= sizeof(package_path)) {
|
||||
pr_err("Path too long for package: %.*s\n", namelen, name);
|
||||
if (my_ctx->stats)
|
||||
my_ctx->stats->errors_encountered++;
|
||||
return FILLDIR_ACTOR_CONTINUE;
|
||||
}
|
||||
|
||||
struct path path;
|
||||
int err = kern_path(package_path, LOOKUP_FOLLOW, &path);
|
||||
if (err) {
|
||||
pr_debug("Package path lookup failed: %s (err: %d)\n", package_path, err);
|
||||
if (my_ctx->stats)
|
||||
my_ctx->stats->errors_encountered++;
|
||||
return FILLDIR_ACTOR_CONTINUE;
|
||||
}
|
||||
|
||||
struct kstat stat;
|
||||
err = vfs_getattr(&path, &stat, STATX_UID, AT_STATX_SYNC_AS_STAT);
|
||||
path_put(&path);
|
||||
|
||||
if (err) {
|
||||
pr_debug("Failed to get attributes for: %s (err: %d)\n", package_path, err);
|
||||
if (my_ctx->stats)
|
||||
my_ctx->stats->errors_encountered++;
|
||||
return FILLDIR_ACTOR_CONTINUE;
|
||||
}
|
||||
|
||||
uid_t uid = from_kuid(&init_user_ns, stat.uid);
|
||||
if (uid == (uid_t)-1) {
|
||||
pr_warn("Invalid UID for package: %.*s\n", namelen, name);
|
||||
if (my_ctx->stats)
|
||||
my_ctx->stats->errors_encountered++;
|
||||
return FILLDIR_ACTOR_CONTINUE;
|
||||
}
|
||||
|
||||
struct uid_data *data = kzalloc(sizeof(struct uid_data), GFP_ATOMIC);
|
||||
if (!data) {
|
||||
pr_err("Failed to allocate memory for package: %.*s\n", namelen, name);
|
||||
if (my_ctx->stats)
|
||||
my_ctx->stats->errors_encountered++;
|
||||
return FILLDIR_ACTOR_CONTINUE;
|
||||
}
|
||||
|
||||
data->uid = uid;
|
||||
size_t copy_len = min(namelen, KSU_MAX_PACKAGE_NAME - 1);
|
||||
strncpy(data->package, name, copy_len);
|
||||
data->package[copy_len] = '\0';
|
||||
|
||||
list_add_tail(&data->list, my_ctx->uid_list);
|
||||
|
||||
if (my_ctx->stats)
|
||||
my_ctx->stats->total_found++;
|
||||
|
||||
pr_info("UserDE UID: Found package: %s, uid: %u\n", data->package, data->uid);
|
||||
|
||||
return FILLDIR_ACTOR_CONTINUE;
|
||||
}
|
||||
|
||||
int scan_user_data_for_uids(struct list_head *uid_list)
|
||||
{
|
||||
struct file *dir_file;
|
||||
struct uid_scan_stats stats = {0};
|
||||
int ret = 0;
|
||||
|
||||
if (!uid_list) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
dir_file = ksu_filp_open_compat(USER_DATA_PATH, O_RDONLY, 0);
|
||||
if (IS_ERR(dir_file)) {
|
||||
pr_err("UserDE UID: Failed to open %s: %ld\n", USER_DATA_PATH, PTR_ERR(dir_file));
|
||||
return PTR_ERR(dir_file);
|
||||
}
|
||||
|
||||
struct user_data_context ctx = {
|
||||
.ctx.actor = user_data_actor,
|
||||
.uid_list = uid_list,
|
||||
.stats = &stats
|
||||
};
|
||||
|
||||
ret = iterate_dir(dir_file, &ctx.ctx);
|
||||
filp_close(dir_file, NULL);
|
||||
|
||||
if (stats.errors_encountered > 0) {
|
||||
pr_warn("Encountered %zu errors while scanning user data directory\n",
|
||||
stats.errors_encountered);
|
||||
}
|
||||
|
||||
pr_info("UserDE UID: Scanned user data directory, found %zu packages with %zu errors\n",
|
||||
stats.total_found, stats.errors_encountered);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
FILLDIR_RETURN_TYPE my_actor(struct dir_context *ctx, const char *name,
|
||||
int namelen, loff_t off, u64 ino,
|
||||
unsigned int d_type)
|
||||
@@ -196,12 +438,14 @@ FILLDIR_RETURN_TYPE my_actor(struct dir_context *ctx, const char *name,
|
||||
}
|
||||
|
||||
int signature_index = -1;
|
||||
bool is_multi_manager = ksu_is_dynamic_manager_apk(dirpath, &signature_index);
|
||||
bool is_multi_manager = is_dynamic_manager_apk(
|
||||
dirpath, &signature_index);
|
||||
|
||||
pr_info("Found new base.apk at path: %s, is_multi_manager: %d, signature_index: %d\n",
|
||||
dirpath, is_multi_manager, signature_index);
|
||||
|
||||
if (is_multi_manager && (signature_index == 1 || signature_index == 2)) {
|
||||
// Check for dynamic sign or multi-manager signatures
|
||||
if (is_multi_manager && (signature_index == DYNAMIC_SIGN_INDEX || signature_index >= 2)) {
|
||||
crown_manager(dirpath, my_ctx->private_data, signature_index);
|
||||
|
||||
struct apk_path_hash *apk_data = kmalloc(sizeof(struct apk_path_hash), GFP_ATOMIC);
|
||||
@@ -327,61 +571,29 @@ static bool is_uid_exist(uid_t uid, char *package, void *data)
|
||||
|
||||
void track_throne()
|
||||
{
|
||||
struct file *fp =
|
||||
ksu_filp_open_compat(SYSTEM_PACKAGES_LIST_PATH, O_RDONLY, 0);
|
||||
if (IS_ERR(fp)) {
|
||||
pr_err("%s: open " SYSTEM_PACKAGES_LIST_PATH " failed: %ld\n",
|
||||
__func__, PTR_ERR(fp));
|
||||
return;
|
||||
}
|
||||
|
||||
struct list_head uid_list;
|
||||
INIT_LIST_HEAD(&uid_list);
|
||||
|
||||
char chr = 0;
|
||||
loff_t pos = 0;
|
||||
loff_t line_start = 0;
|
||||
char buf[KSU_MAX_PACKAGE_NAME];
|
||||
for (;;) {
|
||||
ssize_t count =
|
||||
ksu_kernel_read_compat(fp, &chr, sizeof(chr), &pos);
|
||||
if (count != sizeof(chr))
|
||||
break;
|
||||
if (chr != '\n')
|
||||
continue;
|
||||
pr_info("track_throne triggered, attempting whitelist read\n");
|
||||
|
||||
count = ksu_kernel_read_compat(fp, buf, sizeof(buf),
|
||||
&line_start);
|
||||
// Try read whitelist first
|
||||
int ret = read_uid_whitelist(&uid_list);
|
||||
|
||||
struct uid_data *data =
|
||||
kzalloc(sizeof(struct uid_data), GFP_ATOMIC);
|
||||
if (!data) {
|
||||
filp_close(fp, 0);
|
||||
if (ret < 0) {
|
||||
pr_info("whitelist read failed (%d), request userspace scan, falling back to user_de \n", ret);
|
||||
|
||||
int ret_user = scan_user_data_for_uids(&uid_list);
|
||||
|
||||
if (ret_user < 0) {
|
||||
goto out;
|
||||
} else {
|
||||
pr_info("UserDE UID: Successfully loaded %zu packages from user data directory\n", list_count_nodes(&uid_list));
|
||||
}
|
||||
|
||||
char *tmp = buf;
|
||||
const char *delim = " ";
|
||||
char *package = strsep(&tmp, delim);
|
||||
char *uid = strsep(&tmp, delim);
|
||||
if (!uid || !package) {
|
||||
pr_err("update_uid: package or uid is NULL!\n");
|
||||
break;
|
||||
} else {
|
||||
pr_info("loaded uids from whitelist successfully\n");
|
||||
}
|
||||
|
||||
u32 res;
|
||||
if (kstrtou32(uid, 10, &res)) {
|
||||
pr_err("update_uid: uid parse err\n");
|
||||
break;
|
||||
}
|
||||
data->uid = res;
|
||||
strncpy(data->package, package, KSU_MAX_PACKAGE_NAME);
|
||||
list_add_tail(&data->list, &uid_list);
|
||||
// reset line start
|
||||
line_start = pos;
|
||||
}
|
||||
filp_close(fp, 0);
|
||||
|
||||
// now update uid list
|
||||
struct uid_data *np;
|
||||
struct uid_data *n;
|
||||
@@ -403,7 +615,8 @@ void track_throne()
|
||||
// Check for dynamic managers
|
||||
if (!dynamic_manager_exist && ksu_is_dynamic_manager_enabled()) {
|
||||
list_for_each_entry (np, &uid_list, list) {
|
||||
if (ksu_is_any_manager(np->uid)) {
|
||||
// Check if this uid is a dynamic manager (not the traditional manager)
|
||||
if (ksu_is_any_manager(np->uid) && np->uid != ksu_get_manager_uid()) {
|
||||
dynamic_manager_exist = true;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
import com.android.build.gradle.internal.api.BaseVariantOutputImpl
|
||||
import com.android.build.gradle.tasks.PackageAndroidArtifact
|
||||
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
|
||||
|
||||
plugins {
|
||||
alias(libs.plugins.agp.app)
|
||||
@@ -41,6 +42,7 @@ android {
|
||||
release {
|
||||
isMinifyEnabled = true
|
||||
isShrinkResources = true
|
||||
vcsInfo.include = false
|
||||
proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro")
|
||||
}
|
||||
/**debug {
|
||||
@@ -54,8 +56,8 @@ android {
|
||||
prefab = true
|
||||
}
|
||||
|
||||
kotlinOptions {
|
||||
jvmTarget = "21"
|
||||
kotlin {
|
||||
jvmToolchain(21)
|
||||
}
|
||||
|
||||
packaging {
|
||||
|
||||
1
manager/app/proguard-rules.pro
vendored
@@ -40,7 +40,6 @@
|
||||
|
||||
|
||||
# MMRL:webui reflection
|
||||
-keep class com.dergoogler.mmrl.webui.model.ModId { *; }
|
||||
-keep class com.dergoogler.mmrl.webui.interfaces.** { *; }
|
||||
-keep class com.sukisu.ultra.ui.webui.WebViewInterface { *; }
|
||||
|
||||
|
||||
BIN
manager/app/src/main/assets/kpimg
Normal file
BIN
manager/app/src/main/assets/kptools
Normal file
@@ -54,7 +54,6 @@ struct susfs_feature_status {
|
||||
bool status_spoof_cmdline;
|
||||
bool status_open_redirect;
|
||||
bool status_magic_mount;
|
||||
bool status_overlayfs_auto_kstat;
|
||||
bool status_sus_su;
|
||||
};
|
||||
|
||||
|
||||
@@ -61,6 +61,10 @@
|
||||
GetEnvironment()->NewObject(env, cls, constructor, __VA_ARGS__); \
|
||||
})
|
||||
|
||||
#ifdef NDEBUG
|
||||
#define LogDebug(...) (void)0
|
||||
#else
|
||||
#define LogDebug(...) __android_log_print(ANDROID_LOG_DEBUG, "KernelSU", __VA_ARGS__)
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||