主頁 > 後端開發 > JS 逆向之 Hook,吃著火鍋唱著歌,突然就被麻匪劫了!

JS 逆向之 Hook,吃著火鍋唱著歌,突然就被麻匪劫了!

2021-09-30 06:15:39 後端開發

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

什么是 Hook?

Hook 中文譯為鉤子,Hook 實際上是 Windows 中提供的一種用以替換 DOS 下“中斷”的系統機制,Hook 的概念在 Windows 桌面軟體開發很常見,特別是各種事件觸發的機制,在對特定的系統事件進行 Hook 后,一旦發生已 Hook 事件,對該事件進行 Hook 的程式就會收到系統的通知,這時程式就能在第一時間對該事件做出回應,在程式中將其理解為“劫持”可能會更好理解,我們可以通過 Hook 技術來劫持某個物件,把某個物件的程式拉出來替換成我們自己改寫的代碼片段,修改引數或替換回傳值,從而控制它與其他物件的互動,

通俗來講,Hook 其實就是攔路打劫,馬邦德帶著老婆,出了城,吃著火鍋,還唱著歌,突然就被麻匪劫了,張麻子劫下縣長馬邦德的火車,搖身一變化身縣長,帶著手下趕赴鵝城上任,Hook 的程序,就是張麻子頂替馬邦德的程序,

01.png

JS 逆向中的 Hook

在 JavaScript 逆向中,替換原函式的程序都可以被稱為 Hook,以下先用一段簡單的代碼理解 Hook 的程序:

function a() {
  console.log("I'm a.");
}

a = function b() {
  console.log("I'm b.");
};

a()  // I'm b.

直接覆寫原函式是最簡單的做法,以上代碼將 a 函式進行了重寫,再次呼叫 a 函式將會輸出 I'm b.,如果還想執行原來 a 函式的內容,可以使用中間變數進行儲存:

function a() {
  console.log("I'm a.");
}

var c = a;

a = function b() {
  console.log("I'm b.");
};

a()  // I'm b.
c()  // I'm a.

此時,呼叫 a 函式會輸出 I'm b.,呼叫 c 函式會輸出 I'm a.

這種原函式直接覆寫的方法通常只用來進行臨時除錯,實用性不大,但是它能夠幫助我們理解 Hook 的程序,在實際 JS 逆向程序中,我們會用到更加高級一點的方法,比如 Object.defineProperty()

Object.defineProperty()

基本語法:Object.defineProperty(obj, prop, descriptor),它的作用就是直接在一個物件上定義一個新屬性,或者修改一個物件的現有屬性,接收的三個引數含義如下:

obj:需要定義屬性的當前物件;

prop:當前需要定義的屬性名;

descriptor:屬性描述符,可以取以下值:

屬性名 默認值 含義
get undefined 存取描述符,目標屬性獲取值的方法
set undefined 存取描述符,目標屬性設定值的方法
value undefined 資料描述符,設定屬性的值
writable false 資料描述符,目標屬性的值是否可以被重寫
enumerable false 目標屬性是否可以被列舉
configurable false 目標屬性是否可以被洗掉或是否可以再次修改特性

通常情況下,物件的定義與賦值是這樣的:

var people = {}
people.name = "Bob"
people["age"] = "18"

console.log(people)
// { name: 'Bob', age: '18' }

使用 Object.defineProperty() 方法:

var people = {}

Object.defineProperty(people, 'name', {
   value: 'Bob',
   writable: true  // 是否可以被重寫
})

console.log(people.name)  // 'Bob'

people.name = "Tom"
console.log(people.name)  // 'Tom'

在 Hook 中,使用最多的是存取描述符,即 get 和 set,

get:屬性的 getter 函式,如果沒有 getter,則為 undefined,當訪問該屬性時,會呼叫此函式,執行時不傳入任何引數,但是會傳入 this 物件(由于繼承關系,這里的 this 并不一定是定義該屬性的物件),該函式的回傳值會被用作屬性的值,

set:屬性的 setter 函式,如果沒有 setter,則為 undefined,當屬性值被修改時,會呼叫此函式,該方法接受一個引數,也就是被賦予的新值,會傳入賦值時的 this 物件,

用一個例子來演示:

var people = {
  name: 'Bob',
};
var count = 18;

// 定義一個 age 獲取值時回傳定義好的變數 count
Object.defineProperty(people, 'age', {
  get: function () {
    console.log('獲取值!');
    return count;
  },
  set: function (val) {
    console.log('設定值!');
    count = val + 1;
  },
});

console.log(people.age);
people.age = 20;
console.log(people.age);

輸出:

獲取值!
18
設定值!
獲取值!
21

通過這樣的方法,我們就可以在設定某個值的時候,添加一些代碼,比如 debugger;,讓其斷下,然后利用呼叫堆疊進行除錯,找到引數加密、或者引數生成的地方,需要注意的是,網站加載時首先要運行我們的 Hook 代碼,再運行網站自己的代碼,才能夠成功斷下,這個程序我們可以稱之為 Hook 代碼的注入,以下將介紹幾種主流的注入方法,

Hook 注入的幾種方法

以下以某奇藝 cookie 中的 __dfp 值為例,來演示具體如何注入 Hook,

1、Fiddler 插件注入

來到某奇藝首頁,可以看到其 cookie 里面有個 __dfp 值:

02.png

如果直接搜索是搜不到的,我們想通過 Hook 的方式,讓在生成 __dfp 值的地方斷下,就可以撰寫如下自執行函式:

(function () {
  'use strict';
  var cookieTemp = '';
  Object.defineProperty(document, 'cookie', {
    set: function (val) {
      if (val.indexOf('__dfp') != -1) {
        debugger;
      }
      console.log('Hook捕獲到cookie設定->', val);
      cookieTemp = val;
      return val;
    },
    get: function () {
      return cookieTemp;
    },
  });
})();

if (val.indexOf('__dfp') != -1) {debugger;} 的意思是檢索 __dfp 在字串中首次出現的位置,等于 -1 表示這個字串值沒有出現,反之則出現,如果出現了,那么就 debugger 斷下,這里要注意的是不能寫成 if (val == '__dfp') {debugger},因為 val 傳過來的值類似于 __dfp=xxxxxxxxxx,這樣寫是無法斷下的,

有了代碼該如何使用呢?也就是怎么注入 Hook 代碼呢?這里推薦 Fiddler 抓包工具搭配編程貓的插件使用,插件可以在公眾號輸入關鍵字【Fiddler插件】獲取,其原理可以理解為攔截 —> 加工 —> 放行的一個程序,利用 Fiddler 替換回應,在 Fiddler 攔截到資料后,在原始碼第一行插入 Hook 代碼,由于 Hook 代碼是一個自執行函式,那么網頁一旦加載,就必然會先運行 Hook 代碼,安裝完成后如下圖所示,打開抓包,點擊開啟注入 Hook:

03.png

瀏覽器清除 cookie 后重新進入某奇藝的頁面,可以看到成功斷下,在 console 控制臺可以看到捕獲的一些 cookie 值,此時的 val 就是 __dfp 的值,接下來在右側的 Call Stack 呼叫堆疊里就可以看到一些函式的呼叫程序,依次向上跟進就能夠找到最開始 __dfp 生成的地方,

04.png

2、TamperMonkey 注入

TamperMonkey 俗稱油猴插件,是一款免費的瀏覽器擴展和最為流行的用戶腳本管理器,支持很多主流的瀏覽器, 包括 Chrome、Microsoft Edge、Safari、Opera、Firefox、UC 瀏覽器、360 瀏覽器、QQ 瀏覽器等等,基本上實作了腳本的一次撰寫,所有平臺都能運行,可以說是基于瀏覽器的應用算是真正的跨平臺了,用戶可以在 GreasyFork、OpenUserJS 等平臺直接獲取別人發布的腳本,功能眾多且強大,比如視頻決議、去廣告等,

我們依舊以某奇藝的 cookie 為例來演示如何撰寫 TamperMonkey 腳本,首先去應用商店安裝 TamperMonkey,安裝程序不再贅述,然后點擊圖示,添加新腳本,或者點擊管理面板,再點擊加號新建腳本,寫入以下代碼:

// ==UserScript==
// @name         Cookie Hook
// @namespace    http://tampermonkey.net/
// @version      0.1
// @description  Cookie Hook 腳本示例
// @author       K哥爬蟲
// @match        *
// @icon         https://www.kuaidaili.com/img/favicon.ico
// @grant        none
// @run-at       document-start
// ==/UserScript==

(function () {
  'use strict';
  var cookieTemp = '';
  Object.defineProperty(document, 'cookie', {
    set: function (val) {
      if (val.indexOf('__dfp') != -1) {
        debugger;
      }
      console.log('Hook捕獲到cookie設定->', val);
      cookieTemp = val;
      return val;
    },
    get: function () {
      return cookieTemp;
    },
  });
})();

05.png

主體的 JavaScript 自執行函式和前面都是一樣的,這里需要注意的是最前面的注釋,每個選項都是有意義的,所有的選項參考 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

清除 cookie,開啟 TamperMonkey 插件,再次來到某奇藝首頁,可以看到也成功被斷下,同樣的也可以跟進呼叫堆疊來進一步分析 __dfp 值的來源,

06.png

3、瀏覽器插件注入

瀏覽器插件官方叫法應該是瀏覽器擴展(Extension),瀏覽器插件能夠增強瀏覽器功能,同樣也能夠幫助我們 Hook,瀏覽器插件的撰寫并不復雜,以 Chrome 插件為例,只需要保證專案下有一個 manifest.json 檔案即可,它用來設定所有和插件相關的配置,必須放在根目錄,其中 manifest_versionnameversion 3個引數是必不可少的,如果想要深入學習,可以參考小茗同學的博客和 Google 官方檔案,需要注意的是,火狐瀏覽器插件不一定能在其他瀏覽器上運行,而 Chrome 插件除了能運行在 Chrome 瀏覽器之外,還可以運行在所有 webkit 內核的國產瀏覽器,比如 360 極速瀏覽器、360 安全瀏覽器、搜狗瀏覽器、QQ 瀏覽器等等,我們還是以某奇藝的 cookie 來演示如何撰寫一個 Chrome 瀏覽器 Hook 插件,

新建 manifest.json 檔案:

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

新建 cookie_hook.js 檔案:

var hook = function() {
    'use strict';
    var cookieTemp = '';
    Object.defineProperty(document, 'cookie', {
        set: function(val) {
            if (val.indexOf('__dfp') != -1) {
                debugger;
            }
            console.log('Hook捕獲到cookie設定->', val);
            cookieTemp = val;
            return val;
        },
        get: function() {
            return cookieTemp;
        },
    });
}
var script = document.createElement('script');
script.textContent = '(' + hook + ')()';
(document.head || document.documentElement).appendChild(script);
script.parentNode.removeChild(script);

將這兩個檔案放到同一個檔案夾,打開 chrome 的擴展程式, 打開開發者模式,加載已解壓的擴展程式,選擇創建的檔案夾即可:

07.png

來到某奇藝頁面,清除 cookie 后重新進入,可以看到同樣也成功斷下,跟蹤呼叫堆疊就可以找到其值生成的地方:

08.png

常用 Hook 代碼總匯

除了使用上述的 Object.defineProperty() 方法,還可以直接捕獲相關介面,然后重寫這個介面,以下列出了常見的 Hook 代碼,注意:以下只是關鍵的 Hook 代碼,具體注入的方式不同,要進行相關的修改,

Cookie Hook 用于定位 Cookie 中關鍵引數生成位置,以下代碼演示了當 Cookie 中匹配到了 __dfp 關鍵字, 則插入斷點:

(function () {
  'use strict';
  var cookieTemp = '';
  Object.defineProperty(document, 'cookie', {
    set: function (val) {
      if (val.indexOf('__dfp') != -1) {
        debugger;
      }
      console.log('Hook捕獲到cookie設定->', val);
      cookieTemp = val;
      return val;
    },
    get: function () {
      return cookieTemp;
    },
  });
})();
(function () {
    'use strict';
    var org = document.cookie.__lookupSetter__('cookie');
    document.__defineSetter__('cookie', function (cookie) {
        if (cookie.indexOf('__dfp') != -1) {
            debugger;
        }
        org = cookie;
    });
    document.__defineGetter__('cookie', function () {
        return org;
    });
})();

Hook Header

Header Hook 用于定位 Header 中關鍵引數生成位置,以下代碼演示了當 Header 中包含 Authorization 關鍵字時,則插入斷點:

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

Hook URL

URL Hook 用于定位請求 URL 中關鍵引數生成位置,以下代碼演示了當請求的 URL 里包含 login 關鍵字時,則插入斷點:

(function () {
    var open = window.XMLHttpRequest.prototype.open;
    window.XMLHttpRequest.prototype.open = function (method, url, async) {
        if (url.indexOf("login") != 1) {
            debugger;
        }
        return open.apply(this, arguments);
    };
})();

Hook JSON.stringify

JSON.stringify() 方法用于將 JavaScript 值轉換為 JSON 字串,在某些站點的加密程序中可能會遇到,以下代碼演示了遇到 JSON.stringify() 時,則插入斷點:

(function() {
    var stringify = JSON.stringify;
    JSON.stringify = function(params) {
        console.log("Hook JSON.stringify ——> ", params);
        debugger;
        return stringify(params);
    }
})();

Hook JSON.parse

JSON.parse() 方法用于將一個 JSON 字串轉換為物件,在某些站點的加密程序中可能會遇到,以下代碼演示了遇到 JSON.parse() 時,則插入斷點:

(function() {
    var parse = JSON.parse;
    JSON.parse = function(params) {
        console.log("Hook JSON.parse ——> ", params);
        debugger;
        return parse(params);
    }
})();

Hook eval

JavaScript eval() 函式的作用是計算 JavaScript 字串,并把它作為腳本代碼來執行,如果引數是一個運算式,eval() 函式將執行運算式,如果引數是 Javascript 陳述句,eval() 將執行 Javascript 陳述句,經常被用來動態執行 JS,以下代碼執行后,之后所有的 eval() 操作都會在控制臺列印輸出將要執行的 JS 原始碼:

(function() {
    // 保存原始方法
    window.__cr_eval = window.eval;
    // 重寫 eval
    var myeval = function(src) {
        console.log(src);
        console.log("=============== eval end ===============");
        debugger;
        return window.__cr_eval(src);
    }
    // 屏蔽 JS 中對原生函式 native 屬性的檢測
    var _myeval = myeval.bind(null);
    _myeval.toString = window.__cr_eval.toString;
    Object.defineProperty(window, 'eval', {
        value: _myeval
    });
})();

Hook Function

以下代碼執行后,所有的函式操作都會在控制臺列印輸出將要執行的 JS 原始碼:

(function() {
    // 保存原始方法
    window.__cr_fun = window.Function;
    // 重寫 function
    var myfun = function() {
        var args = Array.prototype.slice.call(arguments, 0, -1).join(","),
            src = https://www.cnblogs.com/ikdl/p/arguments[arguments.length - 1];
        console.log(src);
        console.log("=============== Function end ===============");
        debugger;
        return window.__cr_fun.apply(this, arguments);
    }
    // 屏蔽js中對原生函式native屬性的檢測
    myfun.toString = function() {
        return window.__cr_fun + ""
    }
    Object.defineProperty(window, 'Function', {
        value: myfun
    });
})();

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

標籤:Python

上一篇:Python掃碼登錄微博獲取cookie,附帶賬號密碼js逆向解密程序

下一篇:我終于連上了隔壁妹子家的Wifi,發現學了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