吾爱破解 - LCG - LSG |安卓破解|病毒分析|www.52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 24815|回复: 294
收起左侧

[其他原创] 你没看错:动手开发GUI简单操作系统(一)

    [复制链接]
TLHorse 发表于 2021-2-10 18:06
本帖最后由 TLHorse 于 2021-2-11 18:18 编辑

你没看错:动手开发GUI简单操作系统(一)
你没看错:动手开发GUI简单操作系统(二)
正在更新……

前言

今天我终于想好发布这篇文章,以前自己一直在摸索开发,保证100%原创。这个操作系统异常简单,没有Windows的高级,没有OS X的华丽,更没有Linux的强大——也别指望了,对于个人来说根本没多少生产力,只能用来学习知识,自己整着玩。但是,OS开发的资料太少了,“你没看错”系列中的每一行代码,确实是作者我本人摸滚打爬才得来的。

或许我的文字在各位大佬眼中会很简单。所以说,我尽力吧,简明易懂,不加废话。如果有不专业的地方,直接留言改正,谢谢。

我准备出一系列“你没看错”文章,一定会有后续的。OS一篇文章讲不完,我的写法是理论和实践相辅相成,一点点讲。

学习目标

第一天我们的目标很简单,主要是写启动扇区:

  1. 实现在启动扇区打印字符串
  2. 在启动扇区打印地址
  3. 添加读取磁盘的功能

这些实现主要是为以后加载内核、出现错误调试做准备。

要求知识

  1. 汇编语言不要求精通,但一定要熟悉,有基本了解;
  2. C语言要会,写内核要用;
  3. shell必须会敲命令,没得说;
  4. 可以先修一些附加技能,比如gdb、Makefile等,也可以先了解相关概念。

环境配置

在开发之前,我们需要配置开发环境。我使用的是Mac,终端用的是zsh。如果有能力,可以用Linux,因为Linux包含开发过程中大部分的工具。如果是Windows……那就去论坛下载个虚拟机,使用Linux吧。为了不让诸位一上来就被各种安装震慑住,我们开发一点安装一点。首先(假设你有Homebrew,一定要换源):

brew install qemu nasm # 怎么样?很简单吧

简单认识一下:qemu是个开源的模拟器,nasm是Netwide汇编编译器。

加载启动扇区

我们的操作系统,从bootsector写起。这个bootsector是个启动扇区。当这个分区被识别有效后,系统就会启动。我们的首要目标是创建能被识别的bs。

为了检测磁盘是可启动的,BIOS会检测第511和512字节是否为十六进制AA55。记住0xAA55,这个数字是硬件开发者所设置的。

新建你的项目文件夹,给你自己的系统起个名字,比如我的叫Venus。创建bootsect.asm

loop:
    jmp loop ; 开始递归,在这里做无限循环。其实也可以用hlt或者jmp $实现。

times 510 - ($-$$) db 0 ; 在bs前放上510个0
dw 0xAA55 ; 在第511字节处,定义0xAA55,覆盖两个字节

我们编译、模拟两步走:

$ nasm -fbin boot_sect.asm -o boot_sect.bin 
$ qemu-system-x86_64 boot_sect.bin # 如果错误,改成qemu boot_sect.bin

bs-hlt.png

至此,你迈开了第一步!系统在引导之后,进入了无限循环。

输出至屏幕

先来了解一下中断:

在点击鼠标或键盘时(正如我现在在做的事情),计算机会立即给我反馈处理结果,计算机与我们之间是在进行实时交互的。而实时性的实现便是依赖了中断,中断是为了顺应人们对实时性交互的需求而产生的技术。中断之所以有用,是因为它会立刻停下当前的程序(软件)去做另外一件事。

我们希望启动后,让系统在屏幕上输出几个字符:'Venus'。我们需要用到int 0x10。这个中断用于控制屏幕输出,它好比一个约定俗成的函数,有两个参数,ax寄存器的低位al就是要输出的字符,高位ah就是控制输出模式的指示符。代码如下:

mov ah, 0x0E ; 指示符为0x0E代表tty模式(你应该知道tty是什么,TeleTYpe)
mov al, 'V'  ; 把al赋值'V'
int 0x10     ; 终端输出
mov al, 'e'  ; 重复以上流程
int 0x10
mov al, 'n'
int 0x10
mov al, 'u'
int 0x10
mov al, 's'
int 0x10

jmp $

; BIOS识别的数字
times 510 - ($-$$) db 0
dw 0xAA55 

我们编译、模拟两步走:


bs-print.png

完善打印功能

为了方便我们今后的调试,我们需要完善打印功能,这样出了什么差错直接print就OK了。我们的打印分为两种:打印字符串和打印地址。

打印字符串

都知道,C语言中的字符串结构长这样:

"Venus" -> 'V' 'e' 'n' 'u' 's' '0x0'

都是几个字符再加上一个空字节0x0。如果要打印字符串,而不是单个字符,在汇编里面,可以对应成一个栈来处理。同目录新建一个print.asm

print:
    pusha ; 将所有东西压入栈

; 记住:一直循环打印栈的字符,直到碰到字符串末0x0
; while (string[i] != 0) { print string[i]; i++ }

start:
    mov al, [bx] ; bx相当于字符串参数,是字符串的首位
    cmp al, 0    ; al和0比较
    je done      ; 如果相等,就到了字符串末尾,跳转到结束done

    mov ah, 0x0E ; 如果不相等,开始打印,先进入tty模式
    int 0x10     ; 直接中断。因为al参数已经有字符了

    add bx, 1    ; 如果你把这个栈+1,相当于地址后移一位,这样再打印就是下一个字符串
    jmp start    ; 递归

done:
    popa         ; 弹出栈  
    ret          ; 返回主程序

我们再空几行,实现一个附加功能——换行:

print_nl:        ; print NewLine
    pusha

    mov ah, 0x0E ; tty模式
    mov al, 0x0A ; 把0x0A和0x0D合起来相当于\n
    int 0x10
    mov al, 0x0D ; 把0x0A和0x0D合起来相当于\n
    int 0x10

    popa
    ret

打印地址(4位)

打印地址也很有用的。但它涉及到一个把指定字符转换为ASCII的问题。因为传入的参数不是带引号的字符串,而是譬如0x1234这样的地址,那到底应该打印什么呢?转换方法如下:

字符与ASCII对应关系

数字转换:0~9是0x30~0x39,所以把数字加上0x30即是ASCII;
字母转换:A~F(当成1~6)是0x41~0x46,所以把字母加上0x40

代码

print_hex:
    pusha
    mov cx, 0 ; cx在循环指令和重复前缀中,作循环次数计数器

; 参数dx:要打印的地址
hex_loop:
    cmp cx, 4 ; cx是不是已经循环了四次?
    je end    ; 如果是,跳转到end结束

    ; 如果不是:开始处理

    mov ax, dx     ; 在ax上对字符处理,(dx是我们的地址参数)
    and ax, 0x000F ; 先把这个地址只保留最后一位。比如0x1234就变成0x0004
    add al, 0x30   ; 加上30,这样4就会变成ASCII:34(别忘了这个al是ax的一部分,是一个寄存器——
    cmp al, 0x39   ; 如果发现这个数字>9,不是0~9,那么这个数字就是字母,加上7,就会是A~F中的一个
    jle step2      ; Jump if Lower or Equal:al小于等于0x39跳转至step2
    add al, 7

step2:
    ; 第二步:我们的ASCII字符应该放在哪个地址呢?
    ; 地址BX:基地址+字符串长度(5位,别忘了还有最后的0x0)-字符索引
    mov bx, HEX_OUT + 5 ; 基+长
    sub bx, cx          ; -索引
    mov [bx], al        ; 把al中的字符移到[bx],中括号表示地址的内容
    ror dx, 4           ; ROll Right:0x1234 -> 0x4123 -> 0x3412 -> 0x2341 -> 0x1234. ror帮我们实现类似遍历字符串的效果。你可以去掉这行指令,看看会发生什么

    add cx, 1           ; 循环计数器+1
    jmp hex_loop        ; 回到循环

end:
    mov bx, HEX_OUT     ; 把HEX_OUT设置到bx里,作为下一个call的参数
    call print          ; 调用写好的print.asm

    popa
    ret

HEX_OUT:
    db '0x0000', 0 ; 这是我们输出的地址,先定义下来

尝试使用打印功能

编写bootsect.asm

[org 0x7C00]

mov bx, GREETINGS ; 设置参数
call print        ; 打印
call print_nl     ; 换行

mov dx, 0x4567    ; 设置参数(地址)
call print_hex    ; 打印十六进制

mov bx, SHUTDOWN  ; 同上
call print
call print_nl

jmp $             ; 挂起程序,无限循环(hlt也行)

%include "boot_sect_print.asm"
%include "boot_sect_print_hex.asm"

; 定义两个数据,注意末尾一定要带0字节
GREETINGS:
    db 'Welcome to Venus', 0

SHUTDOWN:
    db 'Shutdown', 0

times 510-($-$$) db 0
dw 0xAA55

说明两个地方:

  1. [org 0x7C00]:org是用来设置程序基址的。因为BIOS将bs加载到0x7C00的位置,所以我们设置基址为0x7C00。这行指令的中括号去掉也行。
  2. %include:用来引用文件,后面跟上空格和双引号,双引号里写文件名称。值得注意的是,%include命令相当于把引用的文件直接替换到程序中,不做任何操作。

还是按老办法编译、模拟:


bs-final.png

读取磁盘

好了,最枯燥却最有用的功能来了,读取磁盘。我们总不能神经质地把整个系统都放在启动扇区。我们先了解一下磁盘(这个部分必须看):

磁盘基础

盘片、片面和磁头


dsk-struct1.png

硬盘中一般会有多个盘片组成,每个盘片包含两个面,每个盘面都对应地有一个读写磁头。受到硬盘整体体积和生产成本的限制,盘片数量都受到限制,一般都在5片以内。盘片的编号自下向上从0开始,如最下边的盘片有0面和1面,再上一个盘片就编号为2面和3面。

扇区(sector)和磁道(track)


dsk-struct2.png

上图显示的是一个盘面,盘面中一圈圈灰色同心圆为一条条磁道,从圆心向外画直线,可以将磁道划分为若干个弧段,每个磁道上一个弧段被称之为一个扇区(图践绿色部分)。扇区是磁盘的最小组成单元,通常是512字节。(由于不断提高磁盘的大小,部分厂商设定每个扇区的大小是4096字节)。

磁头(head)和柱面(cylinder)


dsk-struct3.png

硬盘通常由重叠的一组盘片构成,每个盘面都被划分为数目相等的磁道,并从外缘的“0”开始编号,具有相同编号的磁道形成一个圆柱,称之为磁盘的柱面。磁盘的柱面数与一个盘面上的磁道数是相等的。由于每个盘面都有自己的磁头,因此,盘面数等于总的磁头数。

开始读取吧!

我就直接放代码了,没什么技术含量,只不过有一些关键的寄存器数值与中断号码需要明白:

; 参数:
;   - dh:扇区个数
;   - dl:磁盘
; 读取的数据存入es:bx

disk_load:
    pusha              ; 压入栈
                       ; 将dx也压入栈
    push dx            ; dx一会会被读取磁盘的操作覆盖,所以先压入栈保存

    mov ah, 0x02       ; BIOS 读取扇区的功能编号
    mov al, dh         ; AL - 扇区读取个数,也就是我们的dh
    mov cl, 0x02       ; CL - 从哪里开始读取,因为第一个扇区是启动扇区,所以这里是0x02
    mov ch, 0x00       ; CH - 柱面编号(0x0-0x3FF)
    mov dh, 0x00       ; DH - 磁头编号(0x0-0xF)

    int 0x13           ; 读取磁盘的中断标号
    jc disk_error      ; Jump if Carry:如果CF被设置,就是出现了错误,跳转

    ; 如果没有错误
    pop dx             ; dx我们用完了,弹出栈
    cmp al, dh         ; 此时bios会把al设置为扇区个数,对比一下
    jne sectors_error  ; 如果两者不一样,读取扇区出现了错误,跳转
    popa               ; 如果一样,停止程序
    ret

; 剩下的是错误处理部分,大家都明白
disk_error:
    mov bx, DISK_ERROR
    call print
    call print_nl
    mov dh, ah
    call print_hex
    jmp disk_loop

sectors_error:
    mov bx, SECTORS_ERROR
    call print

disk_loop:
    jmp $

DISK_ERROR: db "Disk read error", 0
SECTORS_ERROR: db "Incorrect number of sectors read", 0

我们将启动扇区代码bootsect.asm做出如下更改:

[org 0x7C00]
mov bp, 0x8000 ; 把栈顶设成0x8000,这样不与BIOS相干
mov sp, bp     ; 同上
mov bx, 0x9000 ; es:bx == 0x0000:0x9000 == 0x09000

; 现在我们要设置disk_load参数
mov dh, 2 ; 读取两个扇区
; 此处不用设置dl,BIOS已经帮我们设置过了
call disk_load ; 调用

mov dx, [0x9000] ; 获取第一扇区
call print_hex
call print_nl

mov dx, [0x9000 + 512] ; 获取第二扇区(注意偏移地址,跟下面数据对应)
call print_hex

jmp $

%include "print.asm"
%include "print_hex.asm"
%include "disk.asm"

times 510 - ($-$$) db 0
dw 0xAA55

; 上面是bs(第一个扇区)
times 256 dw 0x1234 ; 第2
times 256 dw 0x5678 ; 第3
; 上面的第二第三也不一定,因为有的磁盘一个扇区512,现在有的4096
; …………

dsk-final.png

后记

别着急,这只是第一天呢,离加载内核还远着呢,项目里只有四个文件。我给大家指指路,我们已经可以读取磁盘,接下来我们需要:

  1. 加载启动扇区
  2. 读取磁盘,加载内核
  3. 从命令行转成GUI图形界面
  4. 设置GDT(代码最简单,但是最困难的部分,也消耗了我的大部分研究时间)
  5. 切换到32bit保护模式
  6. 执行内核:kernel_main
  7. 正式切换到C语言!

剩下的几个步骤我会划分成几天的内容,发布文章讲解。

其实我写着写着突然想到这不就跟革命斗争一样吗,在执行内核前是多么煎熬,执行内核切换C语言后跟解放了一样。

一点点来吧。

相信我!一定有后续!!!很快就出

THE END

点评

大神就是大神,这不是螺丝刀刻系统盘的那位么?赞  发表于 2021-2-19 10:36
加油!别在意那些瞎BB的人!!沉在坛里这么多年,没见多少个发这类技术贴的(没能力?没时间?)  发表于 2021-2-10 23:50

免费评分

参与人数 261吾爱币 +271 热心值 +235 收起 理由
erh + 1 + 1 热心回复!
ronini + 1 + 1 谢谢@Thanks!
云之梦歌 + 1 + 1 用心讨论,共获提升!
zc55100 + 1 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
LOVEFYL + 1 + 1 热心回复!
duanshifeng1994 + 1 + 1 我很赞同!
S_G + 1 + 1 热心回复!
wwxcz123 + 1 + 1 我很赞同!
CoderWang119 + 1 这也太强了叭!
Jack-yu + 1 + 1 给大佬递杯咖啡
九九 + 1 + 1 我很赞同!
霏映 + 1 热心回复!
某某某人类 + 1 + 1 不明觉厉
suiyunonghen + 1 + 1 我很赞同!
ainidexiho + 1 谢谢@Thanks!
Duker + 1 第一天来吾爱,看来论坛大牛挺多
帅个本 + 1 热心回复!
bpzm1987 + 1 + 1 热心回复!
哈机路压 + 1 谢谢@Thanks!
soga + 1 + 1 我很赞同!
我就是小白 + 1 + 1 用心讨论,共获提升!
myhexdon254 + 1 我很赞同!
无间孤独行者 + 1 + 1 我很赞同!
jsang + 1 + 1 谢谢@Thanks!
didipipi + 1 + 1 期待回形针戳硬盘
吕益达 + 1 + 1 谢谢@Thanks!
牧羊的人 + 1 + 1 鼓励转贴优秀软件安全工具和文档!
某些人 + 1 + 1 谢谢@Thanks!
linzi369 + 1 + 1 谢谢@Thanks!
蓦留 + 1 + 1 不明觉厉
华月方昊 + 1 + 1 我很赞同!
bob3274 + 1 + 1 谢谢@Thanks!
dingyx99 + 1 + 1 支持作者
小北风 + 1 + 1 鼓励转贴优秀软件安全工具和文档!
wenxueroom + 1 我很赞同!
看门猫 + 1 + 1 鼓励转贴优秀软件安全工具和文档!
zhaobisheng + 1 + 1 用心讨论,共获提升!
nonamer + 1 谢谢@Thanks!
linweifu + 1 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
benxiang + 1 + 1 谢谢@Thanks!
whc2001 + 2 + 1 大佬会玩
weilai1917 + 1 + 1 用心讨论,共获提升!
永无止境000 + 1 + 1 谢谢@Thanks!
Moeyuuko + 1 谢谢@Thanks!
LATErMAN + 1 + 1 谢谢@Thanks!
ggdggd + 1 + 1 欢迎分析讨论交流,吾爱破解论坛有你更精彩!
wo2007 + 1 + 1 谢谢@Thanks!
FJFJ + 1 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
BobCoder + 1 + 1 给大佬敬茶
guch8017 + 1 + 1 我很赞同!
JackieJK + 1 + 1 用心讨论,共获提升!
虚幻的立方 + 1 + 1 大佬加油!
执念i_ + 1 + 1 热心回复!
dechong + 1 谢谢@Thanks!
zhi54 + 1 + 1 固态硬盘可不是这样的柱状图哦.
dreamren + 1 + 1 用心讨论,共获提升!
kenkuuga + 1 + 1 谢谢@Thanks!
loooooooong + 1 + 1 技术永无止境
AnkhSpirit + 1 + 1 用心讨论,共获提升!
w3528810 + 1 谢谢@Thanks!
启年啊 + 1 学习
不正义的伙伴 + 1 + 1 谢谢@Thanks!
z376409017 + 1 我很赞同!
Insert + 1 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
wkiwi + 1 + 1 看不懂,但是感觉很nb
freechoice + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
a348892610 + 1 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
ycc926 + 1 + 1 用心讨论,共获提升!
ma4907758 + 1 热心回复!
zmcom + 1 + 1 用心讨论,共获提升!
m1n9yu3 + 1 + 1 谢谢@Thanks!
miranda0131 + 1 + 1 热心回复!
2286624681 + 1 + 1 热心回复!
1051496412 + 1 + 1 我很赞同!
52poze + 1 + 1 感谢楼主分享原创作品
千囚栀愿^_^ + 1 + 1 用心讨论,共获提升!
plasniper + 1 + 1 我很赞同!
itfanr + 1 我很赞同!
Nachtmusik + 1 感谢您的宝贵建议,我们会努力争取做得更好!
aiosnt + 1 正道的光
gogobn + 1 + 1 支持楼主写个类似ntoskrnl.exe出来
eihouwang001 + 1 + 1 大佬,牛。。。。
xuexixiaobai + 1 + 1 受教了,加油
zqguang3708 + 2 + 1 大佬牛逼!!!!
我真的很狂哦 + 1 + 1 热心回复!
laughingsir38 + 1 + 1 热心回复!
瞧丶王先森 + 1 + 1 用心讨论,共获提升!
baifaxiaolaotou + 1 热心回复!
5721 + 1 + 1 我很赞同!
喜提菜鸟一枚qwq + 1 + 1 用心讨论,共获提升!
宅の士 + 1 用心讨论,共获提升!
zls黑战魔 + 1 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
努力加载中 + 1 + 1 用心讨论,共获提升!
873934580 + 1 加油加油
hollow + 1 用心讨论,共获提升!
Henny + 1 用心讨论,共获提升!
48433abc + 1 + 1 我很赞同!
玉米诱惑 + 1 + 1 用心讨论,共获提升!
陆先生 + 1 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
lcd1990 + 1 鼓励转贴优秀软件安全工具和文档!

查看全部评分

本帖被以下淘专辑推荐:

  • · Aarow|主题: 991, 订阅: 304

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

涛之雨 发表于 2021-2-10 19:19
TLHorse 发表于 2021-2-10 18:14
啊?似乎我这篇文章写得很失败

我失望极了 查看82 回复4

好的作品需要时间来检测,而不是多少人查看和多少人去回复。
与其满眼都是回复“感谢楼主”,“学到了,感谢”之类的灌水(注:论坛禁止恶意灌水!)还不如看到几个回复都是认真阅读后的感悟或是疑问等。
(小声逼逼估计很多人也像我一样看不懂,不敢说话,自己写操作系统这种东西太过高深,过于遥远。。。)

点评

大佬是没有时间打字的,比如我!  详情 回复 发表于 2021-2-12 17:37

免费评分

参与人数 4吾爱币 +2 热心值 +3 收起 理由
侃遍天下无二人 + 1 涛哥我研究生阶段打算学这个方向 :-)
php8 + 1 我很赞同!
hsanren + 1 + 1 谢谢@Thanks!
我是不会改名的 + 1 我很赞同!

查看全部评分

networkdwl 发表于 2021-2-10 21:59
从0开始的小小怪 发表于 2021-2-11 12:05
TLHorse 发表于 2021-2-10 22:49
好的,你们推荐的东西,我一定学习学习,毕竟在下也不是什么高级人才,也是个小白
没有GUI咱不怕,毕竟g ...

https://github.com/chyyuu/os_course_info 这是总览,东西还是挺多的,期待你的后续

免费评分

参与人数 3吾爱币 +3 热心值 +3 收起 理由
agi学习者 + 1 + 1 谢谢@Thanks!
fengbolee + 1 + 1 用心讨论,共获提升!
TLHorse + 1 + 1 谢谢@Thanks!

查看全部评分

 楼主| TLHorse 发表于 2021-2-10 19:21
涛之雨 发表于 2021-2-10 19:19
好的作品需要时间来检测,而不是多少人查看和多少人去回复。
与其满眼都是回复“感谢楼主”,“学到了, ...

好,你这回复给了我不少信心啊,一定坚持
MSLOS 发表于 2021-2-10 19:32
TLHorse 发表于 2021-2-10 19:18
技术就是拿来折腾的
不管怎么样,现在许多台式机启动还都是传统BlOS,mbr这类的啊

应该叫兼容传统MBR启动, 自从15年以后的PC几乎都是uefi模式启动的多
_paopao 发表于 2021-2-10 18:49
期待后续~
liuchenxii 发表于 2021-2-10 18:58
大哥加油,虽然我啥都看不懂,但还是想看看大哥的后续
testunpack 发表于 2021-2-10 18:57
反正我也看不懂。。。。。
高山小溪美人 发表于 2021-2-10 18:29
火鉗流氓
RemMai 发表于 2021-2-10 18:12
你以为我看的懂?
 楼主| TLHorse 发表于 2021-2-10 18:14
本帖最后由 TLHorse 于 2021-2-10 18:29 编辑
RemMai 发表于 2021-2-10 18:12
你以为我看的懂?

啊?似乎我这篇文章写得很失败

我失望极了 查看82 回复4

点评

好的作品需要时间来检测,而不是多少人查看和多少人去回复。 与其满眼都是回复“感谢楼主”,“学到了,感谢”之类的灌水(注:论坛禁止恶意灌水!)还不如看到几个回复都是认真阅读后的感悟或是疑问等。 (小声逼  详情 回复 发表于 2021-2-10 19:19
ashi876 发表于 2021-2-10 18:18
行吧,目前还看不出什么。静待后篇
 楼主| TLHorse 发表于 2021-2-10 18:20
ashi876 发表于 2021-2-10 18:18
行吧,目前还看不出什么。静待后篇

好,我一定努力,
其实后面那几部分源代码我都写出来了,就差写文章的事了
E式丶男孩 发表于 2021-2-10 18:30
等着看后续,如果可以的话就该动手了
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则 警告:本版块禁止灌水或回复与主题无关内容,违者重罚!

快速回复 收藏帖子 返回列表 搜索

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-4-20 08:34

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表