色哟哟视频在线观看-色哟哟视频在线-色哟哟欧美15最新在线-色哟哟免费在线观看-国产l精品国产亚洲区在线观看-国产l精品国产亚洲区久久

0
  • 聊天消息
  • 系統消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發帖/加入社區
會員中心
創作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

通過篡改特定代碼數據修復單片機BUG的方法

strongerHuang ? 來源:嵌入式基地 ? 2023-03-03 09:15 ? 次閱讀

分享文章之前,想問大家,你們開發的產品如果有bug了,你們回通過什么什么方式修復bug? 今天就來給大家分享一種通過篡改特定代碼數據修復單片機BUG的方法。

概述

嵌入式產品開發中,難以避免地會因為各種原因導致最后出貨的產品存在各種各樣的BUG,通常會給產品進行固件升級來解決問題。 記得之前在公司維護一款BLE產品的時候,由于前期平臺預研不足,OTA參數設置不當,導致少數產品出現不能OTA的情況,經過分析只需改變代碼中的某個參數數值即可。 但是產品在用戶手里,OTA是唯一能更新代碼的方式,只能給用戶重發產品。后來在想,是否可以提前做好一個接口,支持動態地傳輸少量代碼到產品中臨時運行,通過修改特定位置的Flash代碼數據來修復產品的棘手BUG? 多留一個后門,有時候令產品出棘手問題的往往是那么一兩行代碼或者幾個初始化的參數不對,那么這種方法也可以應應急,雖然操作比較騷。

創建演示工程

本文以STM32F103C8T6單片機為例創建演示工程,分為app和bootloader兩個工程。即將mcu的Flash分為“app”和“bootloader”兩個區域, bootloader放在0x8000000為起始的24KB區域內,app放在0x8006000為起始的后續區域。bootloader完成對app的Flash數據修改。 1、app工程
注意app的工程需要在keil上修改ROM起始地址。d8199212-b93e-11ed-bfe3-dac502259ad0.png
還要在app代碼的開頭設置向量偏移(調用一行代碼):

NVIC_SetVectorTable(NVIC_VectTab_FLASH,0x6000);

app工程的邏輯為:先順序執行3個不同速度的LED閃燈過程(20ms、200ms、500ms、切換亮滅),最后進入到一個循環狀態每秒切換一次LED的狀態閃爍。代碼如下:

voidinit_led(void)
{
GPIO_InitTypeDefGPIO_InitStructure;

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);

GPIO_InitStructure.GPIO_Pin=GPIO_Pin_All;
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOB,&GPIO_InitStructure);

GPIO_ResetBits(GPIOB,GPIO_Pin_10);
GPIO_SetBits(GPIOB,GPIO_Pin_10);
}

voidled_blings_1(void)
{
uint32_ti;

for(i=0;i10;i++)
{
GPIO_SetBits(GPIOB,GPIO_Pin_10);
delay_ms(20);

GPIO_ResetBits(GPIOB,GPIO_Pin_10);
delay_ms(20);
}
}

voidled_blings_2(void)
{
uint32_ti;

for(i=0;i10;i++)
{
GPIO_SetBits(GPIOB,GPIO_Pin_10);
delay_ms(200);

GPIO_ResetBits(GPIOB,GPIO_Pin_10);
delay_ms(200);
}
}

voidled_blings_3(void)
{
uint32_ti;

for(i=0;i10;i++)
{
GPIO_SetBits(GPIOB,GPIO_Pin_10);
delay_ms(500);

GPIO_ResetBits(GPIOB,GPIO_Pin_10);
delay_ms(500);
}
}

intmain()
{
NVIC_SetVectorTable(NVIC_VectTab_FLASH,0x6000);

SysTick_Init(72);

init_led();

led_blings_1();
led_blings_2();
led_blings_3();

while(1)
{
GPIO_SetBits(GPIOB,GPIO_Pin_10);
delay_ms(1000);

GPIO_ResetBits(GPIOB,GPIO_Pin_10);
delay_ms(1000);
}
}

為了分析匯編和查看bin文件數據,我們需要在keil中添加兩條命令,分別生成.dis反匯編和.bin的代碼文件。(具體的目錄情況依葫蘆畫瓢)

fromelf--text-a-c--output=all.disObjTemplate.axf

fromelf--bin--output=test.binObjTemplate.axf
d825f0c0-b93e-11ed-bfe3-dac502259ad0.png

先將app的代碼燒寫進單片機,注意燒寫設置里面選擇“Erase Sectors”只擦除需要燒寫的地方。

2、bootloader工程

在bootloader中分為兩部分,不變的代碼部分和變動的代碼部分(error_process函數)。 初次編譯的時候error_process寫為空函數,當我們有需求對App進行修改的時候,我們重新編譯工程對error_process函數進行填充。 為了重新編譯工程的時候不影響之前函數的鏈接地址,特意將error_process函數放到代碼區的最后0x8000800地址處,理由是原來工程大小是1.51KB,擦除頁大小是2KB,所以需要2KB對齊,對齊處的地址就選擇0x8000800為起始。代碼如下:

#defineFLASH_PAGE_SIZE2048
#defineERROR_PROCESS_CODE_ADDR0x8000800

voiderror_process(void)__attribute__((section(".ARM.__at_0x8000800")));

voidinit_led(void)
{
GPIO_InitTypeDefGPIO_InitStructure;

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);

GPIO_InitStructure.GPIO_Pin=GPIO_Pin_All;
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOB,&GPIO_InitStructure);

GPIO_ResetBits(GPIOB,GPIO_Pin_10);
GPIO_SetBits(GPIOB,GPIO_Pin_10);
}

uint32_tpageBuf[FLASH_PAGE_SIZE/4];

voiderror_process(void)
{

}

voideraseErrorProcessCode(void)
{
FLASH_Unlock();
FLASH_ClearFlag(FLASH_FLAG_BSY|FLASH_FLAG_EOP|
FLASH_FLAG_PGERR|FLASH_FLAG_WRPRTERR);
FLASH_ErasePage(ERROR_PROCESS_CODE_ADDR);
FLASH_Lock();
}

void(*boot_jump2App)();

voidboot_loadApp(uint32_taddr)
{
uint8_ti;

if(((*(vu32*)addr)&0x2FFE0000)==0x20000000)
{
boot_jump2App=(void(*)())*(vu32*)(addr+4);

__set_MSP(*(vu32*)addr);

for(i=0;i8;i++)
{
NVIC->ICER[i]=0xFFFFFFFF;
NVIC->ICPR[i]=0xFFFFFFFF;
}

boot_jump2App();

while(1);
}
}

intmain()
{
uint32_tflag;

SysTick_Init(72);

flag=*((uint32_t*)ERROR_PROCESS_CODE_ADDR);

if((flag!=0xFFFFFFFF)&&(flag!=0))
{
init_led();
GPIO_ResetBits(GPIOB,GPIO_Pin_10);

delay_ms(1000);
delay_ms(1000);

error_process();
eraseErrorProcessCode();
}

boot_loadApp(0x8006000);

while(1);
}

一進main函數就讀取0x8000800地址處的32位數據,如果不是全F或者全0那么這個地方是有函數體存在需要執行的,那么將LED亮起2秒鐘代表bootloader識別到有處理程序需要執行(當然這里還需要加一些error_process代碼數據是否完整之類的判斷機制,這里演示先略去)。執行完處理程序后將處理程序擦除(數據變為全F),避免以后每次上電都重復擦寫Flash。error_process函數代碼的數據由產品正常使用期間通過數據接口傳入直接寫入到0x8000800處(這部分的demo略去),編譯后查看生成的bin文件將error_process部分的代碼截取出來傳輸到Flash地址0x8000800處。bootloader的代碼燒寫進單片機時注意燒寫設置里面選擇“Erase Sectors”只擦除需要燒寫的地方。keil設置里ROM地址改回0x08000000。

修改app的特定參數

在app的工程中以“led_blings_1”函數為例,反匯編如下:

$t
i.led_blings_1
led_blings_1
0x08006558:b510..PUSH{r4,lr}
0x0800655a:2400.$MOVSr4,#0
0x0800655c:e010..B0x8006580;led_blings_1+40
0x0800655e:f44f6180O..aMOVr1,#0x400
0x08006562:4809.HLDRr0,[pc,#36];[0x8006588]=0x40010c00
0x08006564:f7fffea2....BLGPIO_SetBits;0x80062ac
0x08006568:2014.MOVSr0,#0x14
0x0800656a:f7ffffaf....BLdelay_ms;0x80064cc
0x0800656e:f44f6180O..aMOVr1,#0x400
0x08006572:4805.HLDRr0,[pc,#20];[0x8006588]=0x40010c00
0x08006574:f7fffe98....BLGPIO_ResetBits;0x80062a8
0x08006578:2014.MOVSr0,#0x14
0x0800657a:f7ffffa7....BLdelay_ms;0x80064cc
0x0800657e:1c64d.ADDSr4,r4,#1
0x08006580:2c0a.,CMPr4,#0xa
0x08006582:d3ec..BCC0x800655e;led_blings_1+6
0x08006584:bd10..POP{r4,pc}
$d
0x08006586:0000..DCW0
0x08006588:40010c00...@DCD1073810432

由于led是20ms交替亮滅一次,如果我們覺得這個參數有問題想改成100ms,從匯編上來說就是要改變兩行代碼:

0x08006568:2014.MOVSr0,#0x14

0x08006578:2014.MOVSr0,#0x14

改為

0x08006568:20642MOVSr0,#0x64

0x08006578:20642MOVSr0,#0x64

bootloader工程中error_process的函數實現如下:

voiderror_process(void)
{
#defineMODIFY_FUNC_ADDR_START0x08006558

uint32_talignPageAddr=MODIFY_FUNC_ADDR_START/FLASH_PAGE_SIZE*FLASH_PAGE_SIZE;
uint32_tcnt,i;

//1.copyoldcode
memcpy(pageBuf,(void*)alignPageAddr,FLASH_PAGE_SIZE);

//2.changecode.
//由于Flash操作2KB頁的特性,0x08006558不滿2kb,因此偏移為0x558,0x558/4=342
pageBuf[90+256]=(pageBuf[90+256]&0xFFFF0000)|0x2064;
pageBuf[94+256]=(pageBuf[94+256]&0xFFFF0000)|0x2064;

//3.eraseoldcode,copynewcode.
FLASH_Unlock();
FLASH_ClearFlag(FLASH_FLAG_BSY|FLASH_FLAG_EOP|
FLASH_FLAG_PGERR|FLASH_FLAG_WRPRTERR);
FLASH_ErasePage(alignPageAddr);

cnt=FLASH_PAGE_SIZE/4;
for(i=0;i4,pageBuf[i]);
}

FLASH_Lock();
}

由于Flash的2KB頁擦除特性,這里先將待修改代碼區的Flash頁數據拷貝到緩沖buffer里,然后修改buffer里的數據,之后擦除Flash相關頁,最后將buffer里修改后的數據重新寫回到Flash里去。error_process函數的反匯編如下:

$t
.ARM.__at_0x8000800
error_process
0x08000800:b570p.PUSH{r4-r6,lr}
0x08000802:4d1a.MLDRr5,[pc,#104];[0x800086c]=0x8006000
0x08000804:142a*.ASRSr2,r5,#16
0x08000806:4629)FMOVr1,r5
0x08000808:4819.HLDRr0,[pc,#100];[0x8000870]=0x20000008
0x0800080a:f7fffcbd....BL__aeabi_memcpy;0x8000188
0x0800080e:4818.HLDRr0,[pc,#96];[0x8000870]=0x20000008
0x08000810:f8d00568..h.LDRr0,[r0,#0x568]
0x08000814:f36f000fo...BFCr0,#0,#16
0x08000818:f2420164B.d.MOVr1,#0x2064
0x0800081c:4408.DADDr0,r0,r1
0x0800081e:4914.ILDRr1,[pc,#80];[0x8000870]=0x20000008
0x08000820:f8c10568..h.STRr0,[r1,#0x568]
0x08000824:4608.FMOVr0,r1
0x08000826:f8d00578..x.LDRr0,[r0,#0x578]
0x0800082a:f36f000fo...BFCr0,#0,#16
0x0800082e:f2420164B.d.MOVr1,#0x2064
0x08000832:4408.DADDr0,r0,r1
0x08000834:490e.ILDRr1,[pc,#56];[0x8000870]=0x20000008
0x08000836:f8c10578..x.STRr0,[r1,#0x578]
0x0800083a:f7fffd53..S.BLFLASH_Unlock;0x80002e4
0x0800083e:20355MOVSr0,#0x35
0x08000840:f7fffcca....BLFLASH_ClearFlag;0x80001d8
0x08000844:4628(FMOVr0,r5
0x08000846:f7fffccd....BLFLASH_ErasePage;0x80001e4
0x0800084a:14ae..ASRSr6,r5,#18
0x0800084c:2400.$MOVSr4,#0
0x0800084e:e007..B0x8000860;error_process+96
0x08000850:4a07.JLDRr2,[pc,#28];[0x8000870]=0x20000008
0x08000852:f8521024R.$.LDRr1,[r2,r4,LSL#2]
0x08000856:eb050084....ADDr0,r5,r4,LSL#2
0x0800085a:f7fffd0d....BLFLASH_ProgramWord;0x8000278
0x0800085e:1c64d.ADDSr4,r4,#1
0x08000860:42b4.BCMPr4,r6
0x08000862:d3f5..BCC0x8000850;error_process+80
0x08000864:f7fffcfe....BLFLASH_Lock;0x8000264
0x08000868:bd70p.POP{r4-r6,pc}
$d
0x0800086a:0000..DCW0
0x0800086c:08006000.`..DCD134242304
0x08000870:20000008...DCD536870920

那么這124個字節就是最終要傳輸到0x8000800處的函數數據。傳輸完畢后軟復位mcu,bootloader將app的Flash數據進行篡改,達到改變程序功能的目的。

為什么要在bootloader運行時篡改app的數據?按理說在app運行時接收到error_process函數的更新數據后可以立刻運行,但是由于涉及到對app自身代碼的修改,涉及Flash修改的一些相關函數有可能會被暫時破壞而導致代碼運行崩潰。

跳過app的某些函數

如果想跳過“led_blings_1”函數,有2種方法: 1、函數內部跳過

即將以下匯編語句

0x0800655a:2400.$MOVSr4,#0

修改為

0x0800655a:e013.$B0x08006584

在“led_blings_1”函數入口處指令修改直接跳轉到函數出口處。至于匯編的機器碼和用法文末有相關資料可以查閱。

因為修改處的字節偏移為0x55a,是pageBuf下標為342元素的高2Byte,需要在error_process函數中做如下修改:

pageBuf[342]=(pageBuf[342]&0x0000FFFF)|0xe0130000;

2、函數調用處跳過

main函數匯編如下:

$t
i.main
main
0x080065f8:f44f41c0O..AMOVr1,#0x6000
0x080065fc:f04f6000O..`MOVr0,#0x8000000
0x08006600:f7fffe5c...BLNVIC_SetVectorTable;0x80062bc
0x08006604:2048HMOVSr0,#0x48
0x08006606:f7ffff01....BLSysTick_Init;0x800640c
0x0800660a:f7ffff85....BLinit_led;0x8006518
0x0800660e:f7ffffa3....BLled_blings_1;0x8006558
0x08006612:f7ffffbb....BLled_blings_2;0x800658c
0x08006616:f7ffffd3....BLled_blings_3;0x80065c0
0x0800661a:e011..B0x8006640;main+72
0x0800661c:f44f6180O..aMOVr1,#0x400
0x08006620:4808.HLDRr0,[pc,#32];[0x8006644]=0x40010c00
0x08006622:f7fffe43..C.BLGPIO_SetBits;0x80062ac
0x08006626:f44f707aO.zpMOVr0,#0x3e8
0x0800662a:f7ffff4f..O.BLdelay_ms;0x80064cc
0x0800662e:f44f6180O..aMOVr1,#0x400
0x08006632:4804.HLDRr0,[pc,#16];[0x8006644]=0x40010c00
0x08006634:f7fffe38..8.BLGPIO_ResetBits;0x80062a8
0x08006638:f44f707aO.zpMOVr0,#0x3e8
0x0800663c:f7ffff46..F.BLdelay_ms;0x80064cc
0x08006640:e7ec..B0x800661c;main+36
$d
0x08006642:0000..DCW0
0x08006644:40010c00...@DCD1073810432

下面是調用語句

0x0800660e:f7ffffa3....BLled_blings_1;0x8006558

直接將此語句改為空語句nop(0xbf00)即可跳過調用,由于該命令占用4個字節,nop是兩個字節的命令,所以替換為兩個nop命令。

0x0800660e:bf00bf00....NOP

因為修改處的字節偏移為0x60e,是pageBuf下標為387元素的高2Byte和下標為388元素的低2Byte,需要在error_process函數中做如下修改:

pageBuf[387]=(pageBuf[387]&0x0000FFFF)|0xbf000000;
pageBuf[388]=(pageBuf[388]&0xFFFF0000)|0x0000bf00;

審核編輯:湯梓紅


聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規問題,請聯系本站處理。 舉報投訴
  • 單片機
    +關注

    關注

    6036

    文章

    44553

    瀏覽量

    634777
  • 嵌入式
    +關注

    關注

    5082

    文章

    19111

    瀏覽量

    304885
  • 函數
    +關注

    關注

    3

    文章

    4329

    瀏覽量

    62578
  • 代碼
    +關注

    關注

    30

    文章

    4780

    瀏覽量

    68540
  • BUG
    BUG
    +關注

    關注

    0

    文章

    155

    瀏覽量

    15665

原文標題:通過篡改特定代碼數據修復單片機BUG的方法

文章出處:【微信號:strongerHuang,微信公眾號:strongerHuang】歡迎添加關注!文章轉載請注明出處。

收藏 人收藏

    評論

    相關推薦

    單片機bug修復#單片機

    單片機修復
    笑君愁
    發布于 :2022年07月21日 20:15:58

    單片機Bug戰斗的那些經歷

    玩轉單片機有幾年的時間了,從接觸51開始就走上了看不到盡頭的程序員之路。也許大多會認為,敲了幾天幾夜代碼將作品或者項目完美完成的那一刻是最開心最得意的時候。我卻認為,真正快樂的是與Bug斗爭的過程
    發表于 11-05 17:09

    單片機Bug戰斗的那些經歷

    單片機有幾年的時間了,從接觸51開始就走上了看不到盡頭的程序員之路。也許大多會認為,敲了幾天幾夜代碼將作品或者項目完美完成的那一刻是最開心最得意的時候。我卻認為,真正快樂的是與Bug斗爭的過程,最后
    發表于 12-20 17:15

    單片機LED段碼數據生成器

    電子發燒友網站提供《單片機LED段碼數據生成器.exe》資料免費下載
    發表于 06-16 23:10 ?20次下載

    LED段碼數據生成器

    LED段碼數據生成器 單片機C51常用軟件 簡單方便。
    發表于 05-18 14:53 ?10次下載

    單片機的Bootloader可以實現用戶輕松升級程序

    用于更新自身應用軟件并獨立運行的代碼,常被用于升級產品和修復產品bug。STM8單片機如果要下載hex文件的話需要通過
    的頭像 發表于 10-23 16:57 ?4961次閱讀
    <b class='flag-5'>單片機</b>的Bootloader可以實現用戶輕松升級程序

    STM32 LoRa無線數傳模塊 PC通過串口傳輸數據單片機

    STM32F1單片機,燒錄代碼后,連接LoRa無線數傳模塊,在PC上面使用串口助手,通過串口傳輸數據單片機串口1,并在LCD顯示屏顯示
    發表于 11-19 11:51 ?79次下載
    STM32 LoRa無線數傳模塊 PC<b class='flag-5'>通過</b>串口傳輸<b class='flag-5'>數據</b>到<b class='flag-5'>單片機</b>

    通過ESP8266WIFI模塊讓51單片機向后端交互數據

    注意的是在程序燒錄進單片機之前,不能連接RXD和TXD。建議在燒錄代碼之前先用XCOM發送指令檢驗WIFI模塊是否能夠正常使用,不然一直調試單片機代碼也是沒有用的,一下
    發表于 11-23 16:20 ?14次下載
    <b class='flag-5'>通過</b>ESP8266WIFI模塊讓51<b class='flag-5'>單片機</b>向后端交互<b class='flag-5'>數據</b>

    新唐單片機代碼評審總結

    昨晚上,我們一個同事組織了一個小會議,大家一起討論了一個項目的單片機代碼,這個單片機用的是新唐單片機,期間大家也討論了一些問題,總結一下,希望對寫
    發表于 12-01 16:06 ?15次下載
    新唐<b class='flag-5'>單片機</b><b class='flag-5'>代碼</b>評審總結

    基于51單片機的紅外遙控解碼數碼管顯示設計資料源程序

    基于51單片機的紅外遙控解碼數碼管顯示設計資料源程序
    發表于 04-26 15:35 ?5次下載

    通過篡改特定代碼數據修復單片機BUG方法

    少量代碼到產品中臨時運行,通過修改特定位置的Flash代碼數據修復產品的棘手BUG?多留一個后
    的頭像 發表于 05-11 09:22 ?943次閱讀
    <b class='flag-5'>通過</b><b class='flag-5'>篡改</b><b class='flag-5'>特定</b><b class='flag-5'>代碼數據</b><b class='flag-5'>修復</b><b class='flag-5'>單片機</b><b class='flag-5'>BUG</b>的<b class='flag-5'>方法</b>

    基于89C51單片機的紅外解碼數碼管顯示源程序

    基于89C51單片機的紅外解碼數碼管顯示源程序
    發表于 05-15 11:07 ?3次下載

    單片機C代碼嵌套匯編的一些方法

    單片機C代碼嵌套匯編的一些方法
    的頭像 發表于 10-18 16:39 ?529次閱讀
    <b class='flag-5'>單片機</b>C<b class='flag-5'>代碼</b>嵌套匯編的一些<b class='flag-5'>方法</b>

    單片機解析g代碼方法

    的運動。 解析G代碼是將其轉化為單片機能夠理解和執行的指令集。單片機解析G代碼方法主要包括以下幾個方面:G
    的頭像 發表于 12-22 14:15 ?1762次閱讀

    單片機代碼自動生成器程序

    和輸入/輸出設備的芯片。它通常用于嵌入式系統中,能夠完成一系列特定的任務。開發人員編寫的單片機代碼負責指導單片機執行相應的任務。然而,編寫單片機
    的頭像 發表于 01-08 14:12 ?3184次閱讀
    主站蜘蛛池模板: bl高h肉文| 午夜理论片日本中文在线| 久久人人爽人人片AV人成| 国产精品亚洲AV毛片一区二区三区| mdapptv麻豆下载| 99麻豆精品国产人妻无码| 中国jjzz| 18美女腿打开无遮软件| 正在播放久久| 456亚洲人成在线播放网站| 制服的微热| 4hu四虎免费影院www| 22eee在线播放成人免费视频| 伊人久久综合影院首页| 亚洲一级特黄| 亚洲中文字幕永久在线| 一区二区视频在线观看高清视频在线| 亚洲免费一区二区| 亚洲色大成网站www久久九九| 亚洲欧美综合视频| 亚洲综合无码一区二区| 曰本老头同性xxxxx| 中文字幕在线久热精品| 99RE8国产这里只有精品| 97人人看碰人免费公开视频| 99精品免费在线观看| 把内衣脱了把奶露出来| 丁香美女社区| 国产视频成人| 九九热视频在线观看| 美女视频黄a视频全免费网站色窝| 麻1豆传媒2021精品| 欧美精品九九99久久在免费线| 强奷漂亮女老板在线播放| 日日噜噜噜夜夜爽爽狠狠图片| 少妇高潮久久久久7777| 亚欧成人毛片一区二区三区四区 | 成人无码在线视频区| 国产VA精品午夜福利视频| 国产色无码精品视频国产| 精品国产乱码久久久久久免费流畅|