2024年10月16日 星期三

如何在 Visual Studio 2017 對 .NET Core 專案啟用分析器功能

在 Visual Studio 2017 裡面,有個好用的「分析器」功能,可以幫助開發人員寫好程式。他的作法是在背景執行一連串的程式碼分析,透過一組規則集 (RuleSet) 對專案中的程式碼進行剖析與檢查,如果有發現任何問題,就會在「錯誤清單」中呈現資訊、警告或錯誤,並且提供部分規則的自動修復功能。不過,在 Visual Studio 2017 對 .NET Core 專案的支援度並沒有太多文件著墨,我花了好多時間才研究出背後的差異之處。我打算透過本篇文章分享如何在 Visual Studio 2017 啟用與設定 .NET Core 專案的程式碼分析器功能。


基本上,在 Microsoft Docs 上關於 Visual Studio 2017 分析器的文件,所有的「操作步驟」大多都只適用於 .NET Framework 專案類型。到了 .NET Core 之後,雖然分析器的觀念都完全一樣,但是分析器的設定方式已經有點不一樣,這點真的困擾我一些時間。

以下我列出幾個重要的文件入口頁,先建立一些基本觀念再開始使用還是比較好:

早在十多年前,最知名的 FxCop 套件,就是用來分析 .NET Framework 專案原始碼,自動檢測潛在的程式碼問題。爾後被整併到 Visual Studio 之中,變成內建的程式分析器,雖然 FxCop 這個名詞變得少見,但內部還是一直使用這個名字。

直到 .NET Compiler Platform ("Roslyn") 出現之後,這一切都改變了。因為 Roslyn 將 NET 程式語言的編譯器全部開放原始碼,而且公開許多 C#/VB 程式碼剖析的 APIs,所以 Visual Studio 也開始進一步改用 Roslyn 改寫所有分析器。


簡單來說:目前 Visual Studio 2017 的分析器已經全數改用 Roslyn 進行程式碼分析。

在 .NET Framework 專案啟用分析器的步驟

在 Visual Studio 2017 建立任何一個 .NET Framework 專案範本,預設就會啟用內建的分析器。預設選取的規則集為 適用於 Managed 程式碼的 Managed 建議規則規則集 (Managed Recommended Rules rule set for managed code),這些規則的重點在於程式碼中最關鍵的問題,包括潛在的安全性漏洞、應用程式損毀,以及其他重要的邏輯和設計錯誤。

這份 適用於 Managed 程式碼的 Managed 建議規則規則集 文件的翻譯,在 Visual Studio 2017 中操作介面翻譯為 Microsoft 受控建議規則,有一點不太一樣。你可以從「專案屬性」的「程式碼分析」頁籤中看見。規則集檔案的路徑為 C:\Program Files (x86)\Microsoft Visual Studio\2017\Enterprise\Team Tools\Static Analysis Tools\Rule Sets\MinimumRecommendedRules.ruleset

這些規則集都是可以調整的,在 Visual Studio 2017 中完整的規則集文件,可以參考 程式碼分析規則集參考 文件。

在 Visual Studio 2017 的 .NET Framework 專案中開啟規則集,有兩種方法:

  1. 方案總管任意專案下,找到 [參考] / [分析器] 按滑鼠右鍵,點擊 [開啟使用中的規則集合]
  2. 方案總管任意專案按滑鼠右鍵,選擇「屬性」,切換到「程式碼分析」頁籤,點擊「開啟」按鈕。

請注意:在 .NET Core 專案中,從 VS2017 的方案總管中,會完全找不到可以開啟程式碼分析器規則集的功能,從專案屬性中也會找不到程式碼分析頁籤,這點是有點不便!

最後提醒:透過 程式碼分析規則編輯器 對規則集檔案所做的修改,在 .NET Core 的專案中,完全無法產生效果!

在 .NET Core 專案啟用分析器的步驟

我之前就是一直被 .NET Framework 專案的分析器設定步驟卡住,怎樣設定都無效,甚至設定到有點怒氣 XD

在 Visual Studio 2017 中對 .NET Core 專案設定分析器的完整設定步驟如下:

  1. 開啟 NuGet 套件管理員
  2. 點擊「瀏覽」頁籤並勾選「包括搶鮮版」
  3. 搜尋 fxcopanalyzers 關鍵字,並安裝 Microsoft.CodeAnalysis.FxCopAnalyzers 套件。

或直接透過套件管理器主控台輸入以下命令:(2018/09/24: 請記得安裝最新 Beta 版本。)

Install-Package Microsoft.CodeAnalysis.FxCopAnalyzers -IncludePrerelease

安裝好必要的分析器套件後,接下來就簡單測試一下。

你先隨便建立一個類別檔,並輸入以下內容,然後 [建置] 專案:

using System;
using System.IO;
...
...
public class NoDisposeMethod
{
    FileStream newFile;

    public NoDisposeMethod()
    {
        newFile = new FileStream(@"c:\temp.txt", FileMode.Open);
    }
}

上述程式碼會引發 CA1001:具有可處置欄位的類型應該是可處置的 警告,你可以從 錯誤清單 中發現這些訊息。

啟用完整解決方案分析

Visual Studio 2017 預設只會分析目前開啟過的檔案,如果要預設分析方案中所有專案的所有程式碼,可以參考以下設定:

  1. 打開選單 [工具] / [選項] / [文字編輯器] / [C#] / [進階]
  2. 勾選【啟用完整解決方案分析】(Enable full solution analysis)
  3. 啟用之後,必須做一次建置動作,分析器才會生效。

如何調整分析器規則集

在 .NET Framework 可以透過建立 *.ruleset 檔案,你可以透過 [新增項目] 精靈,搜尋 [程式碼分析規則集] 項目範本,即可建立一個自訂規則集。在 Visual Studio 2017 中有個 程式碼分析規則編輯器 可用,不過這套編輯器完全不適用於 .NET Core 專案,千萬不要透過這套來編輯規則集內容。

在 .NET Core 專案中,調整規則集的方式,已經整合進 方案總管,你只要從 [專案] / [相依性] / [分析器] 去挑選適合的規則,直接按滑鼠右鍵,就可以切換該條規則的嚴重性。

只要你第一次切換任何一條規則的嚴重性設定,該專案就會自動建立規則集設定檔(*.ruleset),並且全自動設定相容於 .NET Framework 的規則集。意思也就是說,只要你的方案中有 .NET Core 專案的存在,你就應該用 .NET Core 的規則集設定方法,而這些自動產生的規則集設定檔,也同時可以套用到 .NET Framework 專案中!

.NET Core 預設專案範本內建的分析器套件

  • 主控台應用程式 (.NET Core) & 類別庫 (.NET Core)

    • 這兩個專案範本預設沒有內建任何分析器 (Analyzer)。

    • 如果想要安裝額外的分析器,以下介紹的每個分析器套件,只要用 NuGet 安裝好就能直接使用。

    • 只有在 主控台應用程式 (.NET Core) 與 類別庫 (.NET Core) 專案範本中,才看的見 程式碼分析規則集 (Code Analysis Rule Set) 這個「項目範本」(Item Template)。如果你在 ASP.NET Core Web 應用程式的專案中新增項目時,就會搜尋不到這個項目範本,要特別注意!

  • ASP.NET Core Web 應用程式

    • 預設內建 3 個 NuGet 套件 (包含 4 個分析器)
    1. Microsoft.CodeAnalysis.Analyzers
      • 套件說明:提供使用 .NET Compiler Platform (Roslyn) APIs 的相關導引。
      • 專案位址:https://github.com/dotnet/roslyn-analyzers
      • 更多資訊:Microsoft.CodeAnalysis.Analyzers.md
      • Microsoft.CodeAnalysis.Analyzers
      • Microsoft.CodeAnalysis.CSharp.Analyzers
    2. Microsoft.AspNetCore.Mvc.Analyzers
      • 套件說明:專門針對 ASP.NET Core MVC 的 C# 分析器。
      • 專案位址:https://github.com/aspnet/Mvc/tree/master/src/Microsoft.AspNetCore.Mvc.Analyzers
      • Microsoft.AspNetCore.Mvc.Analyzers
    3. Microsoft.EntityFrameworkCore.Analyzers
      • 套件說明:專門針對 Entity Framework Core 提供的 C# 分析器。
      • 專案位址:https://github.com/aspnet/EntityFrameworkCore/tree/release/2.2/src/EFCore.Analyzers
      • Microsoft.EntityFrameworkCore.Analyzers

詳細介紹 Microsoft.CodeAnalysis.FxCopAnalyzers 分析器

這個 Microsoft.CodeAnalysis.FxCopAnalyzers 分析器,原始碼位於 dotnet/roslyn-analyzers 專案。主要的目的在於幫助開發人員更正確的使用 Roslyn 靜態分析的 APIs,我認為一般人不太需要啟用所有的規則集,畢竟不是所有人都需要如此嚴謹的撰寫 C# 程式碼。

要使用 Microsoft.CodeAnalysis.FxCopAnalyzers 可以直接透過 NuGet 安裝 Microsoft.CodeAnalysis.FxCopAnalyzers 即可。

安裝過程除了會安裝 Microsoft.CodeAnalysis.FxCopAnalyzers 之外,也會連同安裝以下 4 個分析器。

  1. Microsoft.CodeQuality.Analyzers
  2. Microsoft.NetFramework.Analyzers
  3. Microsoft.NetCore.Analyzers
  4. Text.Analyzers
    • 套件資訊:Contains analyzers for text included in code, such as comments.

安裝之後,會有以下分析器:

相關連結

 

資料來源:https://blog.miniasp.com/post/2018/09/24/Enabling-Code-Analysis-in-VS2017-for-NET-Core



延伸資料

[iThome第8屆鐵人賽 19]靜態程式碼分析之Assembly品質分析 - Code Analysis(以前的FxCop)

https://blog.alantsai.net/posts/2017/01/devopsseries-codeanalysis

2024年10月2日 星期三

From120s to 0.5s Programming Optimization

程式算法本身的複雜度

CPU的速度和設計架構

CPU的位元帶寬

自己的程式的寫法




將一個照片RGB格式的彩色圖像轉換成黑白照片。

轉換的公式如下:
  Y = 0.299 * R + 0.587 * G + 0.114 * B; 

圖像尺寸640*480*24bit,RGB圖像已經按照RGBRGB順序排列的格式了。


以下是輸入和輸出的定義:

已經完成了第一個最佳化
看得出來最佳化在哪裡嗎?


優化原則: 圖像是一個2D資料,我用一個1維陣列來存儲。 編譯器處理1維陣列的效率高過2維陣列

這大概是能想得出來的最簡單的寫法了,實在看不出有什麼毛病,好了,編譯一下跑一跑吧。

這個代碼分別用VC6.0和GCC編譯,產生2個版本,分別在PC上和embedded system上面跑。

速度多少?說出來嚇死你!


第一次試跑的成績

在PC上,因為有硬體浮點運算處理器,CPU頻率也夠高,計算速度為20秒

embedded system ,沒有以上2個優勢,浮點操作被編譯器分解成了整數運算,運算速度為120秒左右
這只是一副圖像的運算速度!!

上面這個代碼還沒有跑,就已經知道會很慢了,因為這其中有大量 的浮點運算。只要能不用浮點運算,一定能快很多。

那這個公式怎麼能用定點的整數運算替代呢?

可以如何化簡?
我們就先簡化算式D吧!

RGB的取值範圍都是0~255,都是整數,只是這個係數比較麻煩, 不過這個係數可以表示為:
這一下,能快多少呢?

化簡後的成績


PC上的速度2秒

Embedded system
上的速度45秒


這個代碼編譯後,又快了20%
還是太慢!

雖然快了不少,還是太慢了一些,
20秒處理一幅圖像,地球人都不能 接受!

仔細看一下這個式子!

RGB的取值有文章可做,RGB的取值永遠都大於等於0,小於等於
255,我們能不能將D,E,F都預先計算好呢?然後用查表算法計 算呢?
我們使用3個數組分別存放DEF的256種可能的取值,然後。。。


查表數組初始化



突破音障!
這一次的成績把我嚇出一身冷汗,執行時間居然從30秒一下提高到 了2秒!在PC上測試這段代碼,眼皮還沒眨一下,代碼就執行完了。

一下提高15倍,爽不爽?
下一程,幾 秒?

120秒 ->45秒 -> 30秒 -> 2秒
還能再 快嗎?

很多embedded sysytem 的32bitCPU,都至少有2個ALU,能不能讓2個ALU都跑起來?

2個ALU處理的數據不能有數據依賴,也就是說:
某個ALU的輸入條件不能是別的ALU的輸出,這樣
才可以並行
到這裡,似乎已經足夠快了,但是我們反覆實驗,發現,還有辦法再快!

Int D[256],E[256],F[256]; //查表數組 更改為:
Unsigned short D[256],E[256],F[256]; //查表數組

這是因為編譯器處理int類型和處理unsigned short類型的效率不一樣
將函數聲明為inline,這樣編譯器就會將其
嵌入到母函數中,可以減少CPU調用子函 數所產生的開銷
這2個小小的改進帶來的效益!


一次的成績是: 0.5秒

現在,我們已經達到了客戶的要求!

如果加上以下措施,應該還可以更快:

把查表的數據放置在CPU的高速數據CACHE
裡面

把函數calc_lum()用組合語言來寫

同樣的需求,寫法不一樣,速度可以從120秒 變化為0.5秒,說明CPU的潛能是很大的!看你 如何去挖掘。










2024年9月24日 星期二

Control I2C SSD1306 by GPIO

#define OLED_SDA D2 // D2

#define OLED_SCL D1 // D1


#define OLED_ADDRESS 0x3c  // SSD1306 I2C 地址


// I2C 延遲,控制速度

#define I2C_DELAY 50  // 試著增加延遲


void i2c_start() {

  pinMode(OLED_SDA, OUTPUT);

  pinMode(OLED_SCL, OUTPUT);

  digitalWrite(OLED_SDA, HIGH);

  digitalWrite(OLED_SCL, HIGH);

  delayMicroseconds(I2C_DELAY);

  digitalWrite(OLED_SDA, LOW);

  delayMicroseconds(I2C_DELAY);

  digitalWrite(OLED_SCL, LOW);

}


void i2c_stop() {

  pinMode(OLED_SDA, OUTPUT);

  pinMode(OLED_SCL, OUTPUT);

  digitalWrite(OLED_SDA, LOW);

  digitalWrite(OLED_SCL, HIGH);

  delayMicroseconds(I2C_DELAY);

  digitalWrite(OLED_SDA, HIGH);

}



bool i2c_write_byte(uint8_t byte) {

  pinMode(OLED_SDA, OUTPUT);

  for (int i = 0; i < 8; i++) {

    digitalWrite(OLED_SCL, LOW);

    if (byte & 0x80) {

      digitalWrite(OLED_SDA, HIGH);

    } else {

      digitalWrite(OLED_SDA, LOW);

    }

    byte <<= 1;

    delayMicroseconds(I2C_DELAY);

    digitalWrite(OLED_SCL, HIGH);

    delayMicroseconds(I2C_DELAY);

  }

  digitalWrite(OLED_SCL, LOW);

  pinMode(OLED_SDA, INPUT);  // 切換到輸入檢查 ACK

  delayMicroseconds(I2C_DELAY);

  digitalWrite(OLED_SCL, HIGH);

  bool ack = !digitalRead(OLED_SDA);  // ACK 應該為低

  delayMicroseconds(I2C_DELAY);

  digitalWrite(OLED_SCL, LOW);

  return ack;  // 返回 ACK 檢查結果

}


void oled_command(uint8_t command) {

  i2c_start();

  uint8_t address = OLED_ADDRESS << 1;

  Serial.print("Sending Address: ");

  Serial.println(address, HEX);


  if (!i2c_write_byte(address)) {

    Serial.println("Address not acknowledged");

  }

  if (!i2c_write_byte(0x00)) {

    Serial.println("Control byte not acknowledged");

  }

  if (!i2c_write_byte(command)) {

    Serial.println("Command not acknowledged");

  }

  i2c_stop();

}





void oled_init() {

  oled_command(0xAE);  // 關閉顯示

  oled_command(0xA8);  // 設置多路復用比率

  oled_command(0x3F);  // 1/64

  oled_command(0xD3);  // 設置顯示偏移

  oled_command(0x00);  // 無偏移

  oled_command(0x40);  // 設置起始行位置

  oled_command(0xA1);  // 設置重映射

  oled_command(0xC8);  // 掃描方向

  oled_command(0xDA);  // 設置 COM 硬體配置

  oled_command(0x12);  // 代碼段

  oled_command(0x81);  // 設置對比度

  oled_command(0x7F);

  oled_command(0xA4);  // 關閉整體顯示

  oled_command(0xA6);  // 設置普通顯示

  oled_command(0xD5);  // 設置顯示時鐘分頻比/振盪器頻率

  oled_command(0x80);

  oled_command(0x8D);  // 啟用充電泵

  oled_command(0x14);

  oled_command(0xAF);  // 打開顯示

}


void oled_clear() {

  for (uint8_t page = 0; page < 8; page++) {

    oled_command(0xB0 + page);  // 設置頁地址

    oled_command(0x00);         // 設置低列地址

    oled_command(0x10);         // 設置高列地址

    i2c_start();

    i2c_write_byte(OLED_ADDRESS << 1);  // 發送地址

    i2c_write_byte(0x40);               // 控制位元 (0x40 表示後面是數據)

    for (uint8_t col = 0; col < 128; col++) {

      i2c_write_byte(0x00);             // 清空顯示

    }

    i2c_stop();

  }

}



// 定義 5x7 點陣字體

const uint8_t font5x7[][6] = {

  

{0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // sp

{0x00, 0x00, 0x00, 0x2f, 0x00, 0x00}, // !

{0x00, 0x00, 0x07, 0x00, 0x07, 0x00}, // "

{0x00, 0x14, 0x7f, 0x14, 0x7f, 0x14}, // #

{0x00, 0x24, 0x2a, 0x7f, 0x2a, 0x12}, // $

{0x00, 0x62, 0x64, 0x08, 0x13, 0x23}, // %

{0x00, 0x36, 0x49, 0x55, 0x22, 0x50}, // &

{0x00, 0x00, 0x05, 0x03, 0x00, 0x00}, // '

{0x00, 0x00, 0x1c, 0x22, 0x41, 0x00}, // (

{0x00, 0x00, 0x41, 0x22, 0x1c, 0x00}, // )

{0x00, 0x14, 0x08, 0x3E, 0x08, 0x14}, // *

{0x00, 0x08, 0x08, 0x3E, 0x08, 0x08}, // +

{0x00, 0x00, 0x00, 0xA0, 0x60, 0x00}, // ,

{0x00, 0x08, 0x08, 0x08, 0x08, 0x08}, // -

{0x00, 0x00, 0x60, 0x60, 0x00, 0x00}, // .

{0x00, 0x20, 0x10, 0x08, 0x04, 0x02}, // /

{0x00, 0x3E, 0x51, 0x49, 0x45, 0x3E}, // 0

{0x00, 0x00, 0x42, 0x7F, 0x40, 0x00}, // 1

{0x00, 0x42, 0x61, 0x51, 0x49, 0x46}, // 2

{0x00, 0x21, 0x41, 0x45, 0x4B, 0x31}, // 3

{0x00, 0x18, 0x14, 0x12, 0x7F, 0x10}, // 4

{0x00, 0x27, 0x45, 0x45, 0x45, 0x39}, // 5

{0x00, 0x3C, 0x4A, 0x49, 0x49, 0x30}, // 6

{0x00, 0x01, 0x71, 0x09, 0x05, 0x03}, // 7

{0x00, 0x36, 0x49, 0x49, 0x49, 0x36}, // 8

{0x00, 0x06, 0x49, 0x49, 0x29, 0x1E}, // 9

{0x00, 0x00, 0x36, 0x36, 0x00, 0x00}, // :

{0x00, 0x00, 0x56, 0x36, 0x00, 0x00}, // ;

{0x00, 0x08, 0x14, 0x22, 0x41, 0x00}, // <

{0x00, 0x14, 0x14, 0x14, 0x14, 0x14}, // =

{0x00, 0x00, 0x41, 0x22, 0x14, 0x08}, // >

{0x00, 0x02, 0x01, 0x51, 0x09, 0x06}, // ?

{0x00, 0x32, 0x49, 0x59, 0x51, 0x3E}, // @

{0x00, 0x7C, 0x12, 0x11, 0x12, 0x7C}, // A

{0x00, 0x7F, 0x49, 0x49, 0x49, 0x36}, // B

{0x00, 0x3E, 0x41, 0x41, 0x41, 0x22}, // C

{0x00, 0x7F, 0x41, 0x41, 0x22, 0x1C}, // D

{0x00, 0x7F, 0x49, 0x49, 0x49, 0x41}, // E

{0x00, 0x7F, 0x09, 0x09, 0x09, 0x01}, // F

{0x00, 0x3E, 0x41, 0x49, 0x49, 0x7A}, // G

{0x00, 0x7F, 0x08, 0x08, 0x08, 0x7F}, // H

{0x00, 0x00, 0x41, 0x7F, 0x41, 0x00}, // I

{0x00, 0x20, 0x40, 0x41, 0x3F, 0x01}, // J

{0x00, 0x7F, 0x08, 0x14, 0x22, 0x41}, // K

{0x00, 0x7F, 0x40, 0x40, 0x40, 0x40}, // L

{0x00, 0x7F, 0x02, 0x0C, 0x02, 0x7F}, // M

{0x00, 0x7F, 0x04, 0x08, 0x10, 0x7F}, // N

{0x00, 0x3E, 0x41, 0x41, 0x41, 0x3E}, // O

{0x00, 0x7F, 0x09, 0x09, 0x09, 0x06}, // P

{0x00, 0x3E, 0x41, 0x51, 0x21, 0x5E}, // Q

{0x00, 0x7F, 0x09, 0x19, 0x29, 0x46}, // R

{0x00, 0x46, 0x49, 0x49, 0x49, 0x31}, // S

{0x00, 0x01, 0x01, 0x7F, 0x01, 0x01}, // T

{0x00, 0x3F, 0x40, 0x40, 0x40, 0x3F}, // U

{0x00, 0x1F, 0x20, 0x40, 0x20, 0x1F}, // V

{0x00, 0x3F, 0x40, 0x38, 0x40, 0x3F}, // W

{0x00, 0x63, 0x14, 0x08, 0x14, 0x63}, // X

{0x00, 0x07, 0x08, 0x70, 0x08, 0x07}, // Y

{0x00, 0x61, 0x51, 0x49, 0x45, 0x43}, // Z

{0x00, 0x00, 0x7F, 0x41, 0x41, 0x00}, // [

{0x00, 0x55, 0x2A, 0x55, 0x2A, 0x55}, // backslash

{0x00, 0x00, 0x41, 0x41, 0x7F, 0x00}, // ]

{0x00, 0x04, 0x02, 0x01, 0x02, 0x04}, // ^

{0x00, 0x40, 0x40, 0x40, 0x40, 0x40}, // _

{0x00, 0x00, 0x01, 0x02, 0x04, 0x00}, // '

{0x00, 0x20, 0x54, 0x54, 0x54, 0x78}, // a

{0x00, 0x7F, 0x48, 0x44, 0x44, 0x38}, // b

{0x00, 0x38, 0x44, 0x44, 0x44, 0x20}, // c

{0x00, 0x38, 0x44, 0x44, 0x48, 0x7F}, // d

{0x00, 0x38, 0x54, 0x54, 0x54, 0x18}, // e

{0x00, 0x08, 0x7E, 0x09, 0x01, 0x02}, // f

{0x00, 0x18, 0xA4, 0xA4, 0xA4, 0x7C}, // g

{0x00, 0x7F, 0x08, 0x04, 0x04, 0x78}, // h

{0x00, 0x00, 0x44, 0x7D, 0x40, 0x00}, // i

{0x00, 0x40, 0x80, 0x84, 0x7D, 0x00}, // j

{0x00, 0x7F, 0x10, 0x28, 0x44, 0x00}, // k

{0x00, 0x00, 0x41, 0x7F, 0x40, 0x00}, // l

{0x00, 0x7C, 0x04, 0x18, 0x04, 0x78}, // m

{0x00, 0x7C, 0x08, 0x04, 0x04, 0x78}, // n

{0x00, 0x38, 0x44, 0x44, 0x44, 0x38}, // o

{0x00, 0xFC, 0x24, 0x24, 0x24, 0x18}, // p

{0x00, 0x18, 0x24, 0x24, 0x18, 0xFC}, // q

{0x00, 0x7C, 0x08, 0x04, 0x04, 0x08}, // r

{0x00, 0x48, 0x54, 0x54, 0x54, 0x20}, // s

{0x00, 0x04, 0x3F, 0x44, 0x40, 0x20}, // t

{0x00, 0x3C, 0x40, 0x40, 0x20, 0x7C}, // u

{0x00, 0x1C, 0x20, 0x40, 0x20, 0x1C}, // v

{0x00, 0x3C, 0x40, 0x30, 0x40, 0x3C}, // w

{0x00, 0x44, 0x28, 0x10, 0x28, 0x44}, // x

{0x00, 0x1C, 0xA0, 0xA0, 0xA0, 0x7C}, // y

{0x00, 0x44, 0x64, 0x54, 0x4C, 0x44}, // z


  {0x7E, 0x10, 0x10, 0x10, 0x7E, 0x00},  // 'H'

  {0x7E, 0x4A, 0x4A, 0x4A, 0x42, 0x00},  // 'E'

  {0x7E, 0x40, 0x40, 0x40, 0x40, 0x00},  // 'L'

  {0x3C, 0x42, 0x42, 0x42, 0x3C, 0x00},  // 'O'

  {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF},  // '#'

};



const char ssd1306oled_font[][6] PROGMEM = {

{0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // sp

{0x00, 0x00, 0x00, 0x2f, 0x00, 0x00}, // !

{0x00, 0x00, 0x07, 0x00, 0x07, 0x00}, // "

{0x00, 0x14, 0x7f, 0x14, 0x7f, 0x14}, // #

{0x00, 0x24, 0x2a, 0x7f, 0x2a, 0x12}, // $

{0x00, 0x62, 0x64, 0x08, 0x13, 0x23}, // %

{0x00, 0x36, 0x49, 0x55, 0x22, 0x50}, // &

{0x00, 0x00, 0x05, 0x03, 0x00, 0x00}, // '

{0x00, 0x00, 0x1c, 0x22, 0x41, 0x00}, // (

{0x00, 0x00, 0x41, 0x22, 0x1c, 0x00}, // )

{0x00, 0x14, 0x08, 0x3E, 0x08, 0x14}, // *

{0x00, 0x08, 0x08, 0x3E, 0x08, 0x08}, // +

{0x00, 0x00, 0x00, 0xA0, 0x60, 0x00}, // ,

{0x00, 0x08, 0x08, 0x08, 0x08, 0x08}, // -

{0x00, 0x00, 0x60, 0x60, 0x00, 0x00}, // .

{0x00, 0x20, 0x10, 0x08, 0x04, 0x02}, // /

{0x00, 0x3E, 0x51, 0x49, 0x45, 0x3E}, // 0

{0x00, 0x00, 0x42, 0x7F, 0x40, 0x00}, // 1

{0x00, 0x42, 0x61, 0x51, 0x49, 0x46}, // 2

{0x00, 0x21, 0x41, 0x45, 0x4B, 0x31}, // 3

{0x00, 0x18, 0x14, 0x12, 0x7F, 0x10}, // 4

{0x00, 0x27, 0x45, 0x45, 0x45, 0x39}, // 5

{0x00, 0x3C, 0x4A, 0x49, 0x49, 0x30}, // 6

{0x00, 0x01, 0x71, 0x09, 0x05, 0x03}, // 7

{0x00, 0x36, 0x49, 0x49, 0x49, 0x36}, // 8

{0x00, 0x06, 0x49, 0x49, 0x29, 0x1E}, // 9

{0x00, 0x00, 0x36, 0x36, 0x00, 0x00}, // :

{0x00, 0x00, 0x56, 0x36, 0x00, 0x00}, // ;

{0x00, 0x08, 0x14, 0x22, 0x41, 0x00}, // <

{0x00, 0x14, 0x14, 0x14, 0x14, 0x14}, // =

{0x00, 0x00, 0x41, 0x22, 0x14, 0x08}, // >

{0x00, 0x02, 0x01, 0x51, 0x09, 0x06}, // ?

{0x00, 0x32, 0x49, 0x59, 0x51, 0x3E}, // @

{0x00, 0x7C, 0x12, 0x11, 0x12, 0x7C}, // A

{0x00, 0x7F, 0x49, 0x49, 0x49, 0x36}, // B

{0x00, 0x3E, 0x41, 0x41, 0x41, 0x22}, // C

{0x00, 0x7F, 0x41, 0x41, 0x22, 0x1C}, // D

{0x00, 0x7F, 0x49, 0x49, 0x49, 0x41}, // E

{0x00, 0x7F, 0x09, 0x09, 0x09, 0x01}, // F

{0x00, 0x3E, 0x41, 0x49, 0x49, 0x7A}, // G

{0x00, 0x7F, 0x08, 0x08, 0x08, 0x7F}, // H

{0x00, 0x00, 0x41, 0x7F, 0x41, 0x00}, // I

{0x00, 0x20, 0x40, 0x41, 0x3F, 0x01}, // J

{0x00, 0x7F, 0x08, 0x14, 0x22, 0x41}, // K

{0x00, 0x7F, 0x40, 0x40, 0x40, 0x40}, // L

{0x00, 0x7F, 0x02, 0x0C, 0x02, 0x7F}, // M

{0x00, 0x7F, 0x04, 0x08, 0x10, 0x7F}, // N

{0x00, 0x3E, 0x41, 0x41, 0x41, 0x3E}, // O

{0x00, 0x7F, 0x09, 0x09, 0x09, 0x06}, // P

{0x00, 0x3E, 0x41, 0x51, 0x21, 0x5E}, // Q

{0x00, 0x7F, 0x09, 0x19, 0x29, 0x46}, // R

{0x00, 0x46, 0x49, 0x49, 0x49, 0x31}, // S

{0x00, 0x01, 0x01, 0x7F, 0x01, 0x01}, // T

{0x00, 0x3F, 0x40, 0x40, 0x40, 0x3F}, // U

{0x00, 0x1F, 0x20, 0x40, 0x20, 0x1F}, // V

{0x00, 0x3F, 0x40, 0x38, 0x40, 0x3F}, // W

{0x00, 0x63, 0x14, 0x08, 0x14, 0x63}, // X

{0x00, 0x07, 0x08, 0x70, 0x08, 0x07}, // Y

{0x00, 0x61, 0x51, 0x49, 0x45, 0x43}, // Z

{0x00, 0x00, 0x7F, 0x41, 0x41, 0x00}, // [

{0x00, 0x55, 0x2A, 0x55, 0x2A, 0x55}, // backslash

{0x00, 0x00, 0x41, 0x41, 0x7F, 0x00}, // ]

{0x00, 0x04, 0x02, 0x01, 0x02, 0x04}, // ^

{0x00, 0x40, 0x40, 0x40, 0x40, 0x40}, // _

{0x00, 0x00, 0x01, 0x02, 0x04, 0x00}, // '

{0x00, 0x20, 0x54, 0x54, 0x54, 0x78}, // a

{0x00, 0x7F, 0x48, 0x44, 0x44, 0x38}, // b

{0x00, 0x38, 0x44, 0x44, 0x44, 0x20}, // c

{0x00, 0x38, 0x44, 0x44, 0x48, 0x7F}, // d

{0x00, 0x38, 0x54, 0x54, 0x54, 0x18}, // e

{0x00, 0x08, 0x7E, 0x09, 0x01, 0x02}, // f

{0x00, 0x18, 0xA4, 0xA4, 0xA4, 0x7C}, // g

{0x00, 0x7F, 0x08, 0x04, 0x04, 0x78}, // h

{0x00, 0x00, 0x44, 0x7D, 0x40, 0x00}, // i

{0x00, 0x40, 0x80, 0x84, 0x7D, 0x00}, // j

{0x00, 0x7F, 0x10, 0x28, 0x44, 0x00}, // k

{0x00, 0x00, 0x41, 0x7F, 0x40, 0x00}, // l

{0x00, 0x7C, 0x04, 0x18, 0x04, 0x78}, // m

{0x00, 0x7C, 0x08, 0x04, 0x04, 0x78}, // n

{0x00, 0x38, 0x44, 0x44, 0x44, 0x38}, // o

{0x00, 0xFC, 0x24, 0x24, 0x24, 0x18}, // p

{0x00, 0x18, 0x24, 0x24, 0x18, 0xFC}, // q

{0x00, 0x7C, 0x08, 0x04, 0x04, 0x08}, // r

{0x00, 0x48, 0x54, 0x54, 0x54, 0x20}, // s

{0x00, 0x04, 0x3F, 0x44, 0x40, 0x20}, // t

{0x00, 0x3C, 0x40, 0x40, 0x20, 0x7C}, // u

{0x00, 0x1C, 0x20, 0x40, 0x20, 0x1C}, // v

{0x00, 0x3C, 0x40, 0x30, 0x40, 0x3C}, // w

{0x00, 0x44, 0x28, 0x10, 0x28, 0x44}, // x

{0x00, 0x1C, 0xA0, 0xA0, 0xA0, 0x7C}, // y

{0x00, 0x44, 0x64, 0x54, 0x4C, 0x44}, // z

{0x00, 0x00, 0x08, 0x77, 0x41, 0x00}, // {

{0x00, 0x00, 0x00, 0x63, 0x00, 0x00}, // ¦

{0x00, 0x00, 0x41, 0x77, 0x08, 0x00}, // }

{0x00, 0x08, 0x04, 0x08, 0x08, 0x04}, // ~

/* end of normal char-set */

/* put your own signs/chars here, edit special_char too */

/* be sure that your first special char stand here */

{0x00, 0x3A, 0x40, 0x40, 0x20, 0x7A}, // ü, !!! Important: this must be special_char[0] !!!

{0x00, 0x3D, 0x40, 0x40, 0x40, 0x3D}, // Ü

{0x00, 0x21, 0x54, 0x54, 0x54, 0x79}, // ä

{0x00, 0x7D, 0x12, 0x11, 0x12, 0x7D}, // Ä

{0x00, 0x39, 0x44, 0x44, 0x44, 0x39}, // ö

{0x00, 0x3D, 0x42, 0x42, 0x42, 0x3D}, // Ö

{0x00, 0x02, 0x05, 0x02, 0x00, 0x00}, // °

{0x00, 0x7E, 0x01, 0x49, 0x55, 0x73}, // ß

{0x00, 0x7C, 0x10, 0x10, 0x08, 0x1C}, // µ

{0x00, 0x30, 0x48, 0x20, 0x48, 0x30}, // ω

{0x00, 0x5C, 0x62, 0x02, 0x62, 0x5C} // Ω

};


// 顯示字符的功能

void oled_display_char(uint8_t c) {

    i2c_start();

    i2c_write_byte(OLED_ADDRESS << 1);  // 發送地址

    i2c_write_byte(0x40);               // 控制位元 (0x40 表示後面是數據)

    

    // 發送 5x7 字符數據

    c = c - ' ';

    for (uint8_t i = 0; i < 6; i++) {

      i2c_write_byte(font5x7[c][i]);

    }

  

    // 6th byte is space between characters

    //i2c_write_byte(0x00);

  

    i2c_stop();

  

}


// 顯示字符串

void oled_display_string(uint8_t x, uint8_t y, const char* str) {

  oled_command(0xB0 + y);  // 設置頁地址

  oled_command(0x00);         // 設置低列地址

  oled_command(0x10 + x);         // 設置高列地址

    

  while (*str) {

    oled_display_char(*str);

    /*

    if (*str == 'H') oled_display_char(0);

    if (*str == 'E') oled_display_char(1);

    if (*str == 'L') oled_display_char(2);

    if (*str == 'O') oled_display_char(3);

    if (*str == '#') oled_display_char(4);

    */

    str++;

  }

}


void setup() {

  Serial.begin(9600);

  delay(100);


  //setup2();


  pinMode(OLED_SDA, OUTPUT);

  pinMode(OLED_SCL, OUTPUT);

  

  // 初始化 OLED

  oled_init();

  

  // 測試顯示,打開所有像素

  //oled_command(0xA5);  // 這個指令讓所有像素點亮


  // 清空顯示屏

  oled_clear();


  // 顯示 "HELLO"

  oled_display_string(0, 0, "12345678901234567890");

  oled_display_string(0, 1, "ABCDEFGHIJKLMNOPQRST");

  oled_display_string(0, 2, "abcdefghijklmnopqrst");

  oled_display_string(0, 3, "!@#$%^&*()_+-=,./;'?");

  oled_display_string(0, 7, "H####");

}


void loop() {

  // 空的 loop

}


/*

#include <Wire.h>


void setup2() {

  Wire.begin();

  Serial.begin(9600);

  Serial.println("\nI2C Scanner");

  for (uint8_t address = 1; address < 127; address++) {

    Wire.beginTransmission(address);

    if (Wire.endTransmission() == 0) {

      Serial.print("I2C device found at address 0x");

      if (address < 16) {

        Serial.print("0");

      }

      Serial.println(address, HEX);

    }

  }

  Serial.println("Done");

}

*/

2024年9月10日 星期二

6款較流行的開源漏洞掃描工具推薦及特點分析

 未修補的漏洞是網路犯罪分子最容易攻擊的目標之一。企業中很多的資料安全事件往往由於已知的漏洞造成的,儘管相關的安全補丁已經發布,但許多企業由於種種原因並不能及時發現並修補這些漏洞。

當組織想要開展全面且持續的漏洞掃描工作時,通常需要得到廣泛的安全社區支持。在此過程中,安全人員可以藉助一些的流行開源漏洞掃描工具。由於它們具有開放源程式碼的特性,使用者可以自由地查看、修改和定製這些工具,以滿足自身的安全需求。此外,這些工具會經常更新和改進以適應不斷變化的漏洞威脅。本文收集了6款目前較熱門的開源漏洞掃描工具(詳見下表),並從功能性、兼容性和可擴展性等方面對其應用特點進行了分析。

1、Nmap

1、Nmap

1、Nmap

Nmap是一款非常流行的自動化安全測試工具。它可以在各種主流作業系統上運行,並快速掃描大型網路。它通常會檢測以下資訊:網路上有哪些主機可用,主機在運行什麼服務,主機在運行哪些作業系統版本,使用哪種類型的資料包過濾器和防火牆,以及發動攻擊之前需要的其他有用情報。此外,Nmap的說明文件也很全面,還有針對命令列和GUI(圖形化操作界面)版本的眾多教程,很容易上手。

主要特點

ㆍ快速查詢開放埠,基於可用的 TCP 和 UDP 服務分析協議、應用程序和作業系統。

ㆍ擁有龐大的活躍使用者群,也被大多數網路和網路安全認證計劃所接受。

ㆍ對使用者友好,使用命令列控制元件自動執行漏洞掃描或將結果匯出到票證系統或安全工具中。

ㆍ包含一個不斷增長的檢測腳本庫,可用於增強網路發現和漏洞評估的功能。

ㆍ可基於協議請求的埠響應進行掃描,適用於所有具有開放埠的計算機、物聯網設備、網站、雲系統和網路設備。

不足

ㆍ沒有正式的客戶支持選項

ㆍ使用時需要一定的經驗或程式設計能力

ㆍ並非所有選項在 GUI 版本中都可用

2、OpenVAS

2、OpenVAS

OpenVAS是一個較全面的開源滲透測試軟體。在世界各地的滲透測試專家的幫助下,它得到了不斷的支持和更新,從而使其保持最新狀態。OpenVAS的其他特性還包括提供未經身份驗證的測試、目標掃描和web漏洞掃描。需要說明的是,OpenVAS工具的漏洞掃描能力最初是從Nessus產品派生而來,後者現在是Tenable公司的非開源商業化產品。

主要特點

ㆍ幾乎每天都會更新威脅資訊源,並定期提供產品更新和功能更新。

ㆍ免費版本的功能就非常全面,並在企業版本中提供更多功能和特性,同時提供客戶支持。

ㆍ能夠對終端、伺服器和雲等多種系統進行常見漏洞和曝光(CVE)的掃描。

ㆍ產品得到主流網路安全社區的支持,能夠在許多不同的認證課程中教授。

ㆍ可以為每個漏洞提供額外的上下文資訊,用於漏洞修復或攻擊路徑解釋。

不足

ㆍ對於初學者來說專業門檻較高

ㆍ在同時進行多個掃描任務時,可能會導致程序崩潰

ㆍ一些高級掃描功能需要使用付費版本

3、ZAP

3、ZAP

Zed Attack Proxy (ZAP)是一款使用者友好的滲透測試工具,能找出網路應用中的漏洞。它不僅提供自動化掃描器,也為想要手動查找漏洞的使用者提供了一套工具。ZAP通常預裝在Kali Linux上,它能夠將自身置於測試人員的瀏覽器和Web應用程序之間,攔截請求以充當”代理”。通過修改內容、轉發資料包和模擬其他使用者行為,ZAP也可以對應用程序進行漏洞掃描測試。

主要特點

ㆍ可執行常見的動態應用程序安全測試 (DAST),特別是針對跨站點腳本 (XSS) 漏洞,還能夠執行一些新型的測試工作,例如模糊測試;

ㆍ可提供 API 和 docker 集成以實現快速部署,並與 DevSecOp 工具集成,實現對開發團隊的自動化工單管理;

ㆍ通過Crash Override開源獎學金的支持,ZAP擁有多名全職開發人員,不再與OWASP有關聯;

ㆍ經常被滲透測試人員使用,可以很好地了解駭客可能發現的漏洞。

不足

ㆍ某些掃描功能需要額外的外掛

ㆍ需要一些專業知識才能使用

ㆍ相比其他工具,誤報率較高

4、OSV-Scanner

4、OSV-Scanner

OSV-Scanner是一款由Google公司開發的開源漏洞掃描工具,提供專門的軟體組成分析(SCA),可用於掃描靜態軟體,以確保開源軟體的程式設計程式碼安全漏洞,並保護開源軟體清單(SBOM)。在掃描項目時,OSV-Scanner 首先通過分析清單、軟體材料清單(SBOM)和程式碼提交哈希值來確定正在使用的所有依賴項。這些資訊用於查詢 OSV 資料庫,並報告與項目相關的漏洞。漏洞通過表格的形式或基於 JSON 的 OSV 格式(可選)進行報告。

主要特點

ㆍ能夠定期擴展支持的程式語言列表,包括C/C++、Dart、Elixir、Go、Java、JavaScript、PHP、Python、R、Ruby和Rust。

ㆍ可以從大量資訊源中獲取漏洞,包括Debian、Linux、Maven、npm、NuGet、OSS-Fuzz、Packagist、PyPl和RubyGems。

ㆍ允許API、可腳本化和與GitHub集成的調用,以實現漏洞掃描自動化。

ㆍ使用JSON儲存有關受影響版本的資訊,以便與開發人員工具包進行集成。

ㆍ檢查目錄、軟體清單(SBOM)、鎖定檔案、基於Debian的Docker映象或在Docker容器中運行的軟體。

不足

ㆍ只檢查開源庫中有的漏洞

ㆍ產品較新,尚未被納入到主流的認證教育中

5、CloudSploit

5、CloudSploit

CloudSploit是一款開源的雲基礎設施掃描引擎,目前被Aqua公司收購併繼續對其進行維護,以使使用者能夠下載、修改並享受這個專業工具的好處。CloudSploit可以根據使用者需求進行掃描,也可以配置為持續運行,並向安全和DevOps團隊發送漏洞警報。該工具不僅檢查已知的雲和容器部署漏洞,還能夠檢查常見的配置錯誤問題。

主要特點

ㆍ可持續掃描AWS、Azure、Google Cloud、Oracle Cloud等環境,以便對雲基礎設施的更改進行警報。

ㆍ通過安全人員常用的工具(如Slack、Splunk、OpsGenie、Amazon SNS等)發送實時警報和結果。

ㆍ可從命令列、腳本或構建系統(Jenkins、CircleCL、AWS CodeBuild 等)調用 API。

ㆍ提供了廣泛的雲支持,包括針對主要公共雲平臺(阿里雲、AWS、Azure、Google Cloud 等)的外掛嚴重程度。

不足

ㆍ某些功能需要付費使用

ㆍ必須與其他安全工具一起使用

ㆍ專注於公有云基礎設施安全性

6、sqlmap

6、sqlmap

sqlmap是一款專注但功能強大的免費資料庫漏洞掃描工具。儘管其適用範圍有限,但在一些需要進行嚴格合規和安全測試的數字化業務場景中,資料庫漏洞測試往往是至關重要的組成部分。SQLmap能夠自動化查找與SQL隱碼攻擊相關的威脅和攻擊的過程。相比其他的web應用程序滲透測試工具,SQLmap具有較強大的測試引擎和多種注入攻擊識別能力,並支持多種資料庫伺服器,如MySQL、Microsoft Access、IBM DB2和SQLite。

主要特點

ㆍ可通過DBMS憑據、IP地址、埠和資料庫名稱直接連接到資料庫進行漏洞掃描測試。

ㆍ支持可調用的(程式碼或GitHub)集成,可執行任意命令,檢索標準輸出並生成報告。

ㆍ可掃描多種類型的SQL隱碼攻擊,包括:基於布爾的盲注、基於時間的盲注、基於錯誤的注入、基於UNION查詢的注入、堆疊查詢和帶外注入等。

ㆍ自動識別和使用密碼哈希進行具有許可訪問許可權的測試,還可以進行密碼破解。

ㆍ支持超過30個資料庫管理系統。

不足

ㆍ沒有圖形使用者界面,需要通過命令列

ㆍ只針對資料庫中的漏洞

ㆍ需要一定的資料庫專業知識才能有效使用

資料來源:https://vitomag.com/code/d0vr1

資料來源:https://aict.nkust.edu.tw/digitrans/?p=5858

2024年7月31日 星期三

How to Enable Remote Desktop in Ubuntu 22.04

 

How to Enable Remote Desktop in Ubuntu 22.04 GUI

You can manage remote desktop settings using the Ubuntu Settings application or the grdctl tool on the command line. However, you would better perform the first enabling using the GUI, because it is necessary to create keys for FreeRDP, and it is much easier in the GUI than in the command line.

1. Enable Remote Desktop

Open Settings -> Sharing -> Remote Desktop.

And then enable Remote Desktop and Remote Control switches:

In the Authentication section, you can view or change your username and password:

2. Check Service Status

After that, you can ensure that gnome-remote-desktop service is running using the following command:

systemctl status --user gnome-remote-desktop

And then, ensure that the service is waiting for connections on port 3389:

ss -tulpn | grep 3389

資料來源:https://losst.pro/en/how-to-enable-remote-desktop-in-ubuntu-22-04-23-10

2024年7月28日 星期日

Ubuntu 22.04啟動rc.local服務

 Ubuntu 在20.X及22.X版本預設已把/etc/rc.local開機啟動服務關閉,小編今天要來介紹如何啟動該服務。

01、編輯rc-local服務檔案
指令語法:

sudo vi /lib/systemd/system/rc-local.service

在該文件最下方新增下列三行設定:

[Install]
WantedBy=multi-user.target
Alias=rc-local.service

 

02、設定開機啟動檔案
指令語法:

sudo vi /etc/rc.local

在該文件新增想要執行的指令:

#!/bin/sh -e
/usr/bin/mount --bind /opt/api_7927 /var/www/html/api_7927

 

03、設定開機啟動檔案為可執行

指令語法:

sudo chmod u+x /etc/rc.local

04、設定開機啟動rc-local服務

指令語法:

sudo systemctl enable rc-local
sudo systemctl start rc-local

資料來源: https://ailog.tw/lifelog/2023/11/03/ubuntu-rc-local/

2024年7月22日 星期一

關閉 Ubuntu 中的開放端口

如何關閉 Ubuntu 中的開放端口?

問題描述#

列出所有打開的端口,以關閉一些應用程序的端口。

最佳辦法#

如果要關閉端口,則必須終止進程或停止相關服務。
可以使用 netstat -nalp 和 lsof -i 工具來識別打開端口後面的進程 / 二進制文件。

netstat 可用於查看端口統計信息。
要顯示所有開放端口的列表:

sudo netstat -lnp

列出所有監聽端口號以及每個負責的進程。終止或終止進程以關閉端口。 (kill , pkill …)

關閉一個打開的端口:

sudo fuser -k port_no/tcp

例子:

sudo fuser -k 8080/tcp

次佳辦法#

要在 ubuntu 中關閉開放端口,可以使用以下命令

sudo kill $(sudo lsof -t -i:3000)

代替 3000 你可以指定你的端口號

lsof 命令將提供有關進程打開的文件的信息

-t :此標誌指定 lsof 應僅生成帶有進程標識符且沒有標頭的簡潔輸出 – 例如,以便可以將輸出通過管道傳輸到 kill (1)。此選項選擇 -w 選項。

-i :此標誌選擇任何 Internet 地址與 i 中指定的地址匹配的文件列表。如果未指定地址,此選項將選擇所有 Internet 和 x.25 (HP-UX) 網絡文件的列表。

防火牆規則應用#

sudo ufw allow 22

sudo ufw deny 22

附註#

關閉特定進程

kill $(ps -e|grep firefox|awk '{print $1}')

 

 資料來源:https://baicai.xlog.app/Ubuntu_kill_port_porc?locale=zh-TW