主頁 > 後端開發 > 【JS 逆向百例】瀏覽器插件 Hook 實戰,亞航加密引數分析

【JS 逆向百例】瀏覽器插件 Hook 實戰,亞航加密引數分析

2021-10-15 17:16:15 後端開發

關注微信公眾號:K哥爬蟲,QQ交流群:808574309,持續分享爬蟲進階、JS/安卓逆向等技術干貨!

宣告

本文章中所有內容僅供學習交流,抓包內容、敏感網址、資料介面均已做脫敏處理,嚴禁用于商業用途和非法用途,否則由此產生的一切后果均與作者無關,若有侵權,請聯系我立即洗掉!

逆向目標

  • 目標:亞航 airasia 航班狀態查詢,請求頭 Authorization 引數
  • 主頁:aHR0cHM6Ly93d3cuYWlyYXNpYS5jb20vZmxpZ2h0c3RhdHVzLw==
  • 介面:aHR0cHM6Ly9rLmFwaWFpcmFzaWEuY29tL2ZsaWdodHN0YXR1cy9zdGF0dXMvb2QvdjMv
  • 逆向引數:
    • Request Headers:authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI......

逆向程序

抓包分析

來到航班狀態查詢頁面,隨便輸入出發地和目的地,點擊查找航班,例如查詢澳門到吉隆坡的航班,MFM 和 KUL 分別是澳門和吉隆坡國際機場的代碼,查詢介面由最基本的 URL + 機場代碼 + 日期組成,類似于:https://xxxxxxxxxx/MFM/KUL/28/09/2021 ,其中請求頭 Request Headers 里有個 authorization 引數,通過觀察發現,不管是清除 cookie 還是更換瀏覽器,此引數的值是一直不變的,經過測驗,直接復制該引數到代碼里也是可行的,但本次我們的目的是通過撰寫瀏覽器插件來 Hook 這個引數,找到它生成的地方,

有關 Hook 的詳細知識,在 K 哥前期的文章有詳細介紹:JS 逆向之 Hook,吃著火鍋唱著歌,突然就被麻匪劫了!

01.png

瀏覽器插件 Hook

瀏覽器插件事實上叫做瀏覽器擴展(extensions),它能夠增強瀏覽器功能,比如屏蔽廣告、管理瀏覽器代理、更改瀏覽器外觀等,

既然是通過撰寫瀏覽器插件的方式進行 Hook,那么首先我們肯定是要簡單了解一下如何撰寫瀏覽器插件了,撰寫瀏覽器插件也有對應的規范,在以前,不同瀏覽器的插件撰寫方式都不太一樣,到現在基本上都和 Google Chrome 插件的撰寫方式一樣了,Google Chrome 的插件除了能運行在 Chrome 瀏覽器之外,還可以運行在所有 webkit 內核的國產瀏覽器,比如 360 極速瀏覽器、360 安全瀏覽器、搜狗瀏覽器、QQ 瀏覽器等等,另外,Firefox 火狐瀏覽器也有很多人使用,火狐瀏覽器插件的開發方式變化了很多次,但是從 2017 年 11 月底開始,插件必須使用 WebExtensions APIs 進行構建,其目的也是為了和其他瀏覽器統一,一般的 Google Chrome 插件也能直接運行在火狐瀏覽器上,但是火狐瀏覽器插件需要要經過 Mozilla 簽名后才能安裝,否則只能臨時除錯,重啟瀏覽器后插件就沒有了,這一點較為不便,

一個瀏覽器插件的開發說簡單也簡單,說復雜也復雜,不過對于我們做爬蟲逆向的開發人員來說,我們主要是利用插件對代碼進行 Hook,我們只需要知道一個插件是由一個 manifest.json 和一個 JavaScript 腳本檔案組成的就夠了,接下來 K 哥以本案例中請求頭的 authorization 引數為例,帶領大家開發一個 Hook 插件,當然,如果你想深入研究瀏覽器插件的開發,可以參考 Google Chrome 擴展檔案和 Firefox Browser 擴展檔案,

按照 Google Chrome 插件的開發規范,首先新建一個檔案夾,該檔案夾下包含一個 manifest.json 檔案和一個 JS Hook 腳本,當然,如果你想為你的插件配置一個圖示的話,也可以將圖示放到該檔案夾下,圖示格式官方建議 PNG,也可以是 WebKit 支持的任何格式,包括 BMP、GIF、ICO 和 JPEG 等,注意:manifest.json 檔案名不可更改!正常的插件目錄類似如下結構:

JavaScript Hook
    ├─ manifest.json        // 組態檔,檔案名不可更改
    ├─ icons.png            // 圖示
    └─ javascript_hook.js   // Hook 腳本,檔案名順便取

manifest.json

manifest.json 是一個 Chrome 插件中最重要也是必不可少的檔案,它用來配置所有和插件相關的配置,必須放在根目錄,其中,manifest_version、name、version 這 3 個引數是必不可少的,本案例中,manifest.json 檔案配置如下:(完整配置參考 Chrome manifest file format)

{
    "name": "JavaScript Hook",          // 插件名稱
    "version": "1.0",                   // 插件版本
    "description": "JavaScript Hook",   // 插件描述
    "manifest_version": 2,              // 清單版本,必須是2或者3
    "icons": {                          // 插件圖示
		"16": "/icons.png",             // 圖示路徑,插件圖示不同尺寸也可以是同一張圖
		"48": "/icons.png",
		"128": "/icons.png"
	},
    "content_scripts": [{
        "matches": ["<all_urls>"],      // 匹配所有地址
        "js": ["javascript_hook.js"],   // 注入的代碼檔案名和路徑,如果有多個,則依次注入
        "all_frames": true,             // 允許將內容腳本嵌入頁面的所有框架中
        "permissions": ["tabs"],        // 權限申請,tabs 表示標簽
        "run_at": "document_start"      // 代碼注入的時間
    }]
}

這里需要注意以下幾點:

  • manifest_version:配置清單版本,目前支持 2 和 3,2 將會在將來被逐步淘汰,將來也可能推出 4 或者更高版本,可以在官網查看 Manifest V2 和 Manifest V3 的區別,3 有更高的隱私安全要求,這里推薦使用 2,
  • content_scripts:Chrome 插件中向頁面注入腳本的一種形式,包括地址匹配(支持正則運算式),要注入的 JS、CSS 腳本,代碼注入的時間(建議 document_start,網頁開始加載時就注入)等,

javascript_hook.js

javascript_hook.js 檔案里就是 Hook 代碼了:

var hook = function () {
    var org = window.XMLHttpRequest.prototype.setRequestHeader;
    window.XMLHttpRequest.prototype.setRequestHeader = function (key, value) {
        if (key == 'Authorization') {
            debugger;
        }
        return org.apply(this, arguments);
    }
}
var script = document.createElement('script');
script.textContent = '(' + hook + ')()';
(document.head || document.documentElement).appendChild(script);
script.parentNode.removeChild(script);

XMLHttpRequest.setRequestHeader() 是設定 HTTP 請求頭部的方法,定義了一個變數 org 來保存原始方法,window.XMLHttpRequest.prototype.setRequestHeader 這里有個原型物件 prototype,所有的 JavaScript 物件都會從一個 prototype 原型物件中繼承屬性和方法,具體可以參考菜鳥教程 JavaScript prototype 的介紹,一旦程式在設定請求頭中的 Authorization 時,就會進入我們的 Hook 代碼,通過 debugger 斷下,最后依然將所有引數回傳給 org,也就是 XMLHttpRequest.setRequestHeader() 這個原始方法,保證資料正常傳輸,然后創建 script 標簽,script 標簽內容是將 Hook 函式變成 IIFE 自執行函式,然后將其插入到網頁中,

到此我們瀏覽器插件就撰寫完成了,接下來介紹如何在 Google Chrome 和 Firefox Browser 中使用,

Google Chrome

在瀏覽器地址欄輸入 chrome://extensions 或者依次點擊右上角【自定義及控制 Google Chrome】—>【更多工具】—>【擴展程式】,進入擴展程式頁面,再依次選擇開啟【開發者模式】—>【加載已解壓的擴展程式】,選擇整個 Hook 插件檔案夾(檔案夾里應包含 manifest.json、javascript_hook.js 和圖示檔案),如下圖所示:

02.png

Firefox Browser

火狐瀏覽器不能直接安裝未經過 Mozilla 簽名認證的插件,只能通過除錯附加組件的方式進行安裝,插件的格式必須是 .xpi、.jar、.zip 的,所以需要我們將 manifest.json、javascript_hook.js 和圖示檔案一起打包,打包需要注意不要包含頂層目錄,直接全選右鍵壓縮即可,否則在安裝時會提示 does not contain a valid manifest,

在瀏覽器地址欄輸入 about:addons 或者依次點擊右上角【打開應用程式選單】—>【擴展和主題】,也可以直接使用快捷鍵 Ctrl + Shift + A 來到擴展頁面,在管理您的擴展目錄旁有個設定按鈕,點擊選擇【除錯附加組件】,在臨時擴展專案下,選擇【臨時載入附加組件】,選擇 Hook 插件的壓縮包即可,

也可以直接在瀏覽器地址欄輸入 about:debugging#/runtime/this-firefox,直接進入到臨時擴展頁面,如下圖所示:

03.png

自此,瀏覽器 Hook 插件我們就開發安裝完畢了,重新來到航班查詢頁面,隨便輸入出發地和目的地,點擊查找航班,就可以看到此時已經成功斷下:

04.png

TamperMonkey 插件 Hook

前面我們已經介紹了如何自己撰寫一個瀏覽器插件,但是不同瀏覽器插件的撰寫始終是大同小異的,有可能你撰寫的某個插件在其他瀏覽器上運行不了,而 TamperMonkey 就可以幫助我們解決這個問題,TamperMonkey 俗稱油猴插件,它本身就是一個瀏覽器擴展,是最為流行的用戶腳本管理器,基本上支持所有帶有擴展功能的瀏覽器,實作了腳本的一次撰寫,所有平臺都能運行,用戶可以在 GreasyFork、OpenUserJS 等平臺直接獲取別人發布的腳本,功能眾多且強大,同樣的,我們也可以利用 TamperMonkey 來實作 Hook,

TamperMonkey 可以直接在各大瀏覽器擴展商店里面安裝,也可以去 TamperMonkey 官網進行安裝,安裝程序這里不再贅述,

安裝完成后點擊圖示,添加新腳本,或者點擊管理面板,再點擊加號新建腳本,寫入以下 Hook 代碼:

// ==UserScript==
// @name         JavaScript Hook
// @namespace    http://tampermonkey.net/
// @version      0.1
// @description  JavaScript Hook 腳本
// @author       K哥爬蟲
// @include      *://*airasia.com/*
// @icon         https://profile.csdnimg.cn/1/B/8/3_kdl_csdn
// @grant        none
// @run-at       document-start
// ==/UserScript==

(function () {
    'use strict';
    var org = window.XMLHttpRequest.prototype.setRequestHeader;
    window.XMLHttpRequest.prototype.setRequestHeader = function (key, value) {
        if (key == 'Authorization') {
            debugger;
        }
        return org.apply(this, arguments);
    };
})();

05.png

整個代碼 JavaScript 部分是個 IIFE 立即執行函式,具體含義就不解釋了,前面瀏覽器插件開發時已經講過,重要的是上面幾行注釋,千萬不要以為這只是簡單的注釋,可有可無,在 TamperMonkey 中,可以將這部分視為基本的配置選項,各項都有其具體含義,完整的配置選項參考 TamperMonkey 官方檔案,常見配置項如下表所示(其中需要特別注意 @match@include@run-at 選項):

選項 含義
@name 腳本的名稱
@namespace 命名空間,用來區分相同名稱的腳本,一般寫作者名字或者網址就可以
@version 腳本版本,油猴腳本的更新會讀取這個版本號
@description 描述這個腳本是干什么用的
@author 撰寫這個腳本的作者的名字
@match 從字串的起始位置匹配正則運算式,只有匹配的網址才會執行對應的腳本,例如 * 匹配所有,https://www.baidu.com/* 匹配百度等,可以參考 Python re 模塊里面的 re.match() 方法,允許多個實體
@include 和 @match 類似,只有匹配的網址才會執行對應的腳本,但是 @include 不會從字串起始位置匹配,例如 *://*baidu.com/* 匹配百度,具體區別可以參考 TamperMonkey 官方檔案
@icon 腳本的 icon 圖示
@grant 指定腳本運行所需權限,如果腳本擁有相應的權限,就可以呼叫油猴擴展提供的 API 與瀏覽器進行互動,如果設定為 none 的話,則不使用沙箱環境,腳本會直接運行在網頁的環境中,這時候無法使用大部分油猴擴展的 API,如果不指定的話,油猴會默認添加幾個最常用的 API
@require 如果腳本依賴其他 JS 庫的話,可以使用 require 指令匯入,在運行腳本之前先加載其它庫
@run-at 腳本注入時機,該選項是能不能 hook 到的關鍵,有五個值可選:document-start:網頁開始時;document-body:body出現時;document-end:載入時或者之后執行;document-idle:載入完成后執行,默認選項;context-menu:在瀏覽器背景關系選單中單擊該腳本時,一般將其設定為 document-start

重新來到航班查詢頁面,啟用 TamperMonkey 腳本,如果配置正確的話,就可以看到我們撰寫的 Hook 腳本已開啟,隨便輸入出發地和目的地,點擊查找航班,就可以看到此時已經成功斷下:

06.png

引數逆向

不管你是使用瀏覽器插件還是 TamperMonkey 進行 Hook,此時 Hook 到的是設定請求頭 Authorization 的地方,也就是說 Authorization 的值是產生肯定經過了之前的某個函式或者方法,那么我們跟進開發者工具的 Call Stack 呼叫堆疊,就一定能夠找到這個方法,跟呼叫堆疊是一個考驗耐心的程序,花費時間也比較多,

通常情況下,我們是挨個函式查看其傳遞的引數有沒有包含我們目標引數,如果上一個函式里沒有而下一個函式里出現了,那么大概率加密程序就在這兩個函式之間,進入上一個函式再進行單步除錯,一般就能找到加密代碼,在本案例中,我們跟到 t.getData 函式埋下斷點進行單步除錯,可以看到其實后面在反復呼叫 t.subscribet.call,之所以不在這兩個函式處埋下斷點,是因為回圈過多不好除錯,而且 t.getData 通過名稱判斷也比較可疑,

07.png

重新點擊登陸,來到我們剛剛埋下斷點的地方,F11 或者點擊向下箭頭,進入函式內部進行單步除錯,除錯大約 7 步后,來到一個 t.getHttpHeader 函式,可以看到 Authorization 的值就是 "Bearer " + r.accessToken,我們在控制臺列印 r.accessToken 可以看到就是我們想要的值,如下圖所示:

08.png

那么重點是這個 r.accessToken,如果你嘗試直接往上找,你會發現找了很多行也沒有找到,直接搜索關鍵字 accessToken,可以發現在 zUnb 物件里面是直接定義死了的,直接拿來用即可,如下圖所示:

09.png

關于出發地、目的地的各個地方的代碼,是通過 JSON 傳遞過來的,很容易找到,可根據實際需求靈活處理,如下圖所示:

10.png

這個案例本身不難,直接搜索還能更快定位引數位置,但是本案例重點在于如何使用瀏覽器插件進行 Hook 操作,這對于某些無法經過搜索得到的引數,或者搜索結果太多難以定位的情況來說,是一個很好的解決方法,

完整代碼

GitHub 關注 K 哥爬蟲,持續分享爬蟲相關代碼!歡迎 star !https://github.com/kgepachong/

以下只演示部分關鍵代碼,不能直接運行!完整代碼倉庫地址:https://github.com/kgepachong/crawler/

Python 示例代碼

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import requests


status_url = '脫敏處理,完整代碼關注 GitHub:https://github.com/kgepachong/crawler'


def get_flight_status(departure, destination, date):
    headers = {
        'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.159 Safari/537.36',
        'authorization': '脫敏處理,完整代碼關注 GitHub:https://github.com/kgepachong/crawler'
    }
    complete_url = status_url + departure + '/' + destination + '/' + date
    response = requests.get(url=complete_url, headers=headers)
    print(response.text)


if __name__ == '__main__':
    departure = input('請輸入出發地代碼:')
    destination = input('請輸入目的地代碼:')
    date = input('請輸入日期(例如:29/09/2021):')

    # departure = 'MFM'
    # destination = 'KUL'
    # date = '29/09/2021'
    get_flight_status(departure, destination, date)

轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/315873.html

標籤:Python

上一篇:Python代碼閱讀(第15篇):串列求交集

下一篇:【Python爬蟲】太刺激了!本來只想爬個視頻的,誰知自己淪陷進去了(附原始碼)

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 【C++】Microsoft C++、C 和匯編程式檔案

    ......

    uj5u.com 2020-09-10 00:57:23 more
  • 例外宣告

    相比于斷言適用于排除邏輯上不可能存在的狀態,例外通常是用于邏輯上可能發生的錯誤。 例外宣告 Item 1:當函式不可能拋出例外或不能接受拋出例外時,使用noexcept 理由 如果不打算拋出例外的話,程式就會認為無法處理這種錯誤,并且應當盡早終止,如此可以有效地阻止例外的傳播與擴散。 示例 //不可 ......

    uj5u.com 2020-09-10 00:57:27 more
  • Codeforces 1400E Clear the Multiset(貪心 + 分治)

    鏈接:https://codeforces.com/problemset/problem/1400/E 來源:Codeforces 思路:給你一個陣列,現在你可以進行兩種操作,操作1:將一段沒有 0 的區間進行減一的操作,操作2:將 i 位置上的元素歸零。最終問:將這個陣列的全部元素歸零后操作的最少 ......

    uj5u.com 2020-09-10 00:57:30 more
  • UVA11610 【Reverse Prime】

    本人看到此題沒有翻譯,就附帶了一個自己的翻譯版本 思考 這一題,它的第一個要求是找出所有 $7$ 位反向質數及其質因數的個數。 我們應該需要質數篩篩選1~$10^{7}$的所有數,這里就不慢慢介紹了。但是,重讀題,我們突然發現反向質數都是 $7$ 位,而將它反過來后的數字卻是 $6$ 位數,這就說明 ......

    uj5u.com 2020-09-10 00:57:36 more
  • 統計區間素數數量

    1 #pragma GCC optimize(2) 2 #include <bits/stdc++.h> 3 using namespace std; 4 bool isprime[1000000010]; 5 vector<int> prime; 6 inline int getlist(int ......

    uj5u.com 2020-09-10 00:57:47 more
  • C/C++編程筆記:C++中的 const 變數詳解,教你正確認識const用法

    1、C中的const 1、區域const變數存放在堆疊區中,會分配記憶體(也就是說可以通過地址間接修改變數的值)。測驗代碼如下: 運行結果: 2、全域const變數存放在只讀資料段(不能通過地址修改,會發生寫入錯誤), 默認為外部聯編,可以給其他源檔案使用(需要用extern關鍵字修飾) 運行結果: ......

    uj5u.com 2020-09-10 00:58:04 more
  • 【C++犯錯記錄】VS2019 MFC添加資源不懂如何修改資源宏ID

    1. 首先在資源視圖中,添加資源 2. 點擊新添加的資源,復制自動生成的ID 3. 在解決方案資源管理器中找到Resource.h檔案,編輯,使用整個專案搜索和替換的方式快速替換 宏宣告 4. Ctrl+Shift+F 全域搜索,點擊查找全部,然后逐個替換 5. 為什么使用搜索替換而不使用屬性視窗直 ......

    uj5u.com 2020-09-10 00:59:11 more
  • 【C++犯錯記錄】VS2019 MFC不懂的批量添加資源

    1. 打開資源頭檔案Resource.h,在其中預先定義好宏 ID(不清楚其實ID值應該設定多少,可以先新建一個相同的資源項,再在這個資源的ID值的基礎上遞增即可) 2. 在資源視圖中選中專案資源,按F7編輯資源檔案,按 ID 型別 相對路徑的形式添加 資源。(別忘了先把檔案拷貝到專案中的res檔案 ......

    uj5u.com 2020-09-10 01:00:19 more
  • C/C++編程筆記:關于C++的參考型別,專供新手入門使用

    今天要講的是C++中我最喜歡的一個用法——參考,也叫別名。 參考就是給一個變數名取一個變數名,方便我們間接地使用這個變數。我們可以給一個變數創建N個參考,這N + 1個變數共享了同一塊記憶體區域。(參考型別的變數會占用記憶體空間,占用的記憶體空間的大小和指標型別的大小是相同的。雖然參考是一個物件的別名,但 ......

    uj5u.com 2020-09-10 01:00:22 more
  • 【C/C++編程筆記】從頭開始學習C ++:初學者完整指南

    眾所周知,C ++的學習曲線陡峭,但是花時間學習這種語言將為您的職業帶來奇跡,并使您與其他開發人員區分開。您會更輕松地學習新語言,形成真正的解決問題的技能,并在編程的基礎上打下堅實的基礎。 C ++將幫助您養成良好的編程習慣(即清晰一致的編碼風格,在撰寫代碼時注釋代碼,并限制類內部的可見性),并且由 ......

    uj5u.com 2020-09-10 01:00:41 more
最新发布
  • Rust中的智能指標:Box<T> Rc<T> Arc<T> Cell<T> RefCell<T> Weak

    Rust中的智能指標是什么 智能指標(smart pointers)是一類資料結構,是擁有資料所有權和額外功能的指標。是指標的進一步發展 指標(pointer)是一個包含記憶體地址的變數的通用概念。這個地址參考,或 ” 指向”(points at)一些其 他資料 。參考以 & 符號為標志并借用了他們所 ......

    uj5u.com 2023-04-20 07:24:10 more
  • Java的值傳遞和參考傳遞

    值傳遞不會改變本身,參考傳遞(如果傳遞的值需要實體化到堆里)如果發生修改了會改變本身。 1.基本資料型別都是值傳遞 package com.example.basic; public class Test { public static void main(String[] args) { int ......

    uj5u.com 2023-04-20 07:24:04 more
  • [2]SpinalHDL教程——Scala簡單入門

    第一個 Scala 程式 shell里面輸入 $ scala scala> 1 + 1 res0: Int = 2 scala> println("Hello World!") Hello World! 檔案形式 object HelloWorld { /* 這是我的第一個 Scala 程式 * 以 ......

    uj5u.com 2023-04-20 07:23:58 more
  • 理解函式指標和回呼函式

    理解 函式指標 指向函式的指標。比如: 理解函式指標的偽代碼 void (*p)(int type, char *data); // 定義一個函式指標p void func(int type, char *data); // 宣告一個函式func p = func; // 將指標p指向函式func ......

    uj5u.com 2023-04-20 07:23:52 more
  • Django筆記二十五之資料庫函式之日期函式

    本文首發于公眾號:Hunter后端 原文鏈接:Django筆記二十五之資料庫函式之日期函式 日期函式主要介紹兩個大類,Extract() 和 Trunc() Extract() 函式作用是提取日期,比如我們可以提取一個日期欄位的年份,月份,日等資料 Trunc() 的作用則是截取,比如 2022-0 ......

    uj5u.com 2023-04-20 07:23:45 more
  • 一天吃透JVM面試八股文

    什么是JVM? JVM,全稱Java Virtual Machine(Java虛擬機),是通過在實際的計算機上仿真模擬各種計算機功能來實作的。由一套位元組碼指令集、一組暫存器、一個堆疊、一個垃圾回收堆和一個存盤方法域等組成。JVM屏蔽了與作業系統平臺相關的資訊,使得Java程式只需要生成在Java虛擬機 ......

    uj5u.com 2023-04-20 07:23:31 more
  • 使用Java接入小程式訂閱訊息!

    更新完微信服務號的模板訊息之后,我又趕緊把微信小程式的訂閱訊息給實作了!之前我一直以為微信小程式也是要企業才能申請,沒想到小程式個人就能申請。 訊息推送平臺🔥推送下發【郵件】【短信】【微信服務號】【微信小程式】【企業微信】【釘釘】等訊息型別。 https://gitee.com/zhongfuch ......

    uj5u.com 2023-04-20 07:22:59 more
  • java -- 緩沖流、轉換流、序列化流

    緩沖流 緩沖流, 也叫高效流, 按照資料型別分類: 位元組緩沖流:BufferedInputStream,BufferedOutputStream 字符緩沖流:BufferedReader,BufferedWriter 緩沖流的基本原理,是在創建流物件時,會創建一個內置的默認大小的緩沖區陣列,通過緩沖 ......

    uj5u.com 2023-04-20 07:22:49 more
  • Java-SpringBoot-Range請求頭設定實作視頻分段傳輸

    老實說,人太懶了,現在基本都不喜歡寫筆記了,但是網上有關Range請求頭的文章都太水了 下面是抄的一段StackOverflow的代碼...自己大修改過的,寫的注釋挺全的,應該直接看得懂,就不解釋了 寫的不好...只是希望能給視頻網站開發的新手一點點幫助吧. 業務場景:視頻分段傳輸、視頻多段傳輸(理 ......

    uj5u.com 2023-04-20 07:22:42 more
  • Windows 10開發教程_編程入門自學教程_菜鳥教程-免費教程分享

    教程簡介 Windows 10開發入門教程 - 從簡單的步驟了解Windows 10開發,從基本到高級概念,包括簡介,UWP,第一個應用程式,商店,XAML控制元件,資料系結,XAML性能,自適應設計,自適應UI,自適應代碼,檔案管理,SQLite資料庫,應用程式到應用程式通信,應用程式本地化,應用程式 ......

    uj5u.com 2023-04-20 07:22:35 more