SEH 進階(1)
SHE進階
瞭解了上一篇的文章之後,我們寫一個簡單的例子來驗證我們的想法,並學習新的知識。
不同的編譯器提供的增強版本SHE 可能不同,但是它們都是基於windows 底層SHE 的。我們使用Win10 1703 + VS2010 生成X86 Rlease 程式來驗證已經學過的知識,後面使用XP x86 7600 來學習編譯器版本的SHE。
編譯如下程式
#define WIN32_LEAN_AND_MEAN #include <windows.h> #include <stdio.h> DWORD scratch; EXCEPTION_DISPOSITION __cdecl except_handler( struct _EXCEPTION_RECORD *ExceptionRecord, void * EstablisherFrame, struct _CONTEXT *ContextRecord, void * DispatcherContext ) { unsigned i; // Indicate that we made it to our exception handler printf( "Hello from an exception handler\n" ); // Change EAX in the context record so that it points to someplace // where we can successfully write ContextRecord->Eax = (DWORD)&scratch; // Tell the OS to restart the faulting instruction return ExceptionContinueExecution; } int main() { DWORD handler = (DWORD)except_handler; __asm { // Build EXCEPTION_REGISTRATION record: push handler // Address of handler function push FS:[0] // Address of previous handler mov FS:[0],ESP // Install new EXECEPTION_REGISTRATION } __asm { mov eax,0 // Zero out EAX mov [eax], 1 // Write to EAX to deliberately cause a fault } printf( "After writing!\n" ); __asm { // Remove our EXECEPTION_REGISTRATION record mov eax,[ESP] // Get pointer to previous record mov FS:[0], EAX // Install previous record add esp, 8 // Clean our EXECEPTION_REGISTRATION off stack } return 0; }
當我們使用VS 並新增我們自定義的異常處理程式,將有以下警告,且程式執行崩潰
warning C4733: 內聯 asm 分配到“FS:0”: 處理程式未註冊為安全處理程式
解決方案:
在 Visual Studio 開發環境中設定此連結器選項
1. 開啟專案的“屬性頁”對話方塊。有關詳細資訊,請參見設定 Visual C++ 專案屬性。
2. 選擇 Linker 資料夾。
3. 選擇“命令列”屬性頁。
4. 將該選項/SAFESEH:NO鍵入“附加選項”框中。
之後我們的程式正確執行並輸出如下結果:
#include <windows.h> #include <stdio.h> EXCEPTION_DISPOSITION __cdecl _except_handler( struct _EXCEPTION_RECORD *ExceptionRecord, void * EstablisherFrame, struct _CONTEXT *ContextRecord, void * DispatcherContext ) { printf( "Home Grown handler: Exception Code: %08X Exception Flags %X", ExceptionRecord->ExceptionCode, ExceptionRecord->ExceptionFlags ); if ( ExceptionRecord->ExceptionFlags & 1 ) printf( " EH_NONCONTINUABLE" ); if ( ExceptionRecord->ExceptionFlags & 2 ) printf( " EH_UNWINDING" ); if ( ExceptionRecord->ExceptionFlags & 4 ) printf( " EH_EXIT_UNWIND" ); if ( ExceptionRecord->ExceptionFlags & 8 ) printf( " EH_STACK_INVALID" ); if ( ExceptionRecord->ExceptionFlags & 0x10 ) printf( " EH_NESTED_CALL" ); printf( "\n" ); // Punt... We don't want to handle this... Let somebody else handle it return ExceptionContinueSearch; } void HomeGrownFrame( void ) { DWORD handler = (DWORD)_except_handler; __asm { // Build EXCEPTION_REGISTRATION record: push handler // Address of handler function push FS:[0] // Address of previous handler mov FS:[0],ESP // Install new EXECEPTION_REGISTRATION } *(PDWORD)0 = 0; // Write to address 0 to cause a fault printf( "I should never get here!\n" ); __asm { // Remove our EXECEPTION_REGISTRATION record mov eax,[ESP] // Get pointer to previous record mov FS:[0], EAX // Install previous record add esp, 8 // Clean our EXECEPTION_REGISTRATION off stack } } int main() { _try { HomeGrownFrame(); } _except( EXCEPTION_EXECUTE_HANDLER ) { printf( "Caught the exception in main()\n" ); } return 0;
}
執行後出現如下結果:
我們看到,except_handler函式被呼叫了兩次,且傳入的引數不同。
為什麼同一個異常處理函式在這裡被呼叫了兩次?
首先我們觀察兩次呼叫的呼叫堆疊。
我們看到其呼叫堆疊並不相同。
從我們已經知道的知識來講。
HomeGrownFrame 函式內部安裝了一個自己的異常,在main函式中呼叫該函式之前已經進入一個異常函式中。因此我們可以得到類似於下面的堆疊:
0xFFFFFFFEH
__except_handler4
_try _catch 對應的異常處理函式
HomeGrownFrame 函式內部安裝的異常塊,except_handler
當該異常出現的時候,except_handler
為了搞清楚這個問題,我們要學習編譯器提供的SHE 的機制,這裡我們用Ring0 學習,其實機制是相同的。
Ring0層的實驗
最簡單的單個try 塊的情況
#include "SEHDemo.h"
void SEHTest() {
int i = 0;
__try
{
i++;
}
__except(EXCEPTION_EXECUTE_HANDLER)
{
DbgPrint("Hello\r\n");
}
}
NTSTATUS
DriverEntry(IN PDRIVER_OBJECT pDriverObj, IN PUNICODE_STRING pRegistryString)
{
SEHTest();
return STATUS_SUCCESS;
}
程式碼逆向如下
.text:00011010 ; _DWORD __stdcall SEHTest()
.text:00011010 [email protected] proc near ; CODE XREF: DriverEntry(x,x)+5 p
.text:00011010
.text:00011010 i = dword ptr -1Ch
.text:00011010 ms_exc = CPPEH_RECORD ptr -18h
對應結構體如下
struct EH3_EXCEPTION_REGISTRATION
{
DWORD Next;
DWORD ExceptionHandler;
DWORD ScopeTable;
DWORD TryLevel;
};
struct CPPEH_RECORD
{
DWORD OleEsp;
DWORD ExcPtr;
EH3_EXCEPTION_REGISTRATION registration;
};
stru_12108 對應的結構體
.rdata:00012108 stru_12108 dd 0FFFFFFFEh ; GSCookieOffset
.rdata:00012108 ; DATA XREF: SEHTest()+7 o
.rdata:00012108 dd 0 ; GSCookieXOROffset ; SEH scope table for function 11010
.rdata:00012108 dd 0FFFFFFD4h ; EHCookieOffset
.rdata:00012108 dd 0 ; EHCookieXOROffset
.rdata:00012108 dd 0FFFFFFFEh ; ScopeRecord.EnclosingLevel
.rdata:00012108 dd offset $LN5 ; ScopeRecord.FilterFunc
.rdata:00012108 dd offset $LN6 ; ScopeRecord.HandlerFunc
.text:00011010
.text:00011010 mov edi, edi
.text:00011012 push ebp
.text:00011013 mov ebp, esp
.text:00011015 push 0FFFFFFFEh
.text:00011017 push offset stru_12108
.text:0001101C push offset __except_handler4
.text:00011021 mov eax, large fs:0
.text:00011027 push eax
.text:00011028 add esp, -0Ch
.text:0001102B push ebx
.text:0001102C push esi
.text:0001102D push edi
對應堆疊如下
.text:0001102E mov eax, ___security_cookie
.text:00011033 xor [ebp+ms_exc.registration.ScopeTable], eax
.text:00011036 xor eax, ebp
.text:00011038 push eax
.text:00011039 lea eax, [ebp+ms_exc.registration]
.text:0001103C mov large fs:0, eax
.text:00011042 mov [ebp+ms_exc.old_esp], esp
.text:00011045 mov [ebp+i], 0
.text:0001104C mov [ebp+ms_exc.registration.TryLevel], 0
對應堆疊如下
;這三條指令是內部執行的指令 i++
.text:00011053 mov eax, [ebp+i]
.text:00011056 add eax, 1
.text:00011059 mov [ebp+i], eax
;下面是異常的解除安裝
.text:0001105C mov [ebp+ms_exc.registration.TryLevel], 0FFFFFFFEh
.text:00011063 jmp short loc_11082
.text:00011065
.text:00011065 $LN5: ; DATA XREF:.rdata:stru_12108o
.text:00011065 mov eax, 1 ; Exception filter 0 for function11010
.text:0001106A
.text:0001106A $LN7:
.text:0001106A retn
.text:0001106B ;---------------------------------------------------------------------------
.text:0001106B
.text:0001106B $LN6: ; DATA XREF: .rdata:stru_12108o
.text:0001106B mov esp, [ebp+ms_exc.old_esp] ; Exceptionhandler 0 for function 11010
.text:0001106E push offset Format ; "Hello\r\n"
.text:00011073 call _DbgPrint
.text:00011078 add esp, 4
.text:0001107B mov [ebp+ms_exc.registration.TryLevel],0FFFFFFFEh
對應的堆疊
.text:00011082 loc_11082: ; CODE XREF: SEHTest()+53 j
.text:00011082 mov ecx, [ebp+ms_exc.registration.Next]
.text:00011085 mov large fs:0, ecx
;恢復fs:0 即原來的異常處理函式
;堆疊平衡
.text:0001108C pop ecx
.text:0001108D pop edi
.text:0001108E pop esi
.text:0001108F pop ebx
.text:00011090 mov esp, ebp
.text:00011092 pop ebp
.text:00011093 retn
.text:00011093 [email protected] endp
上面是僅僅一個try 語句時候的情況,那麼,當有多個try 語句,是怎樣的情況呢?
void SEHTest(){
int i = 0;
__try {
i++;
}
__except(EXCEPTION_EXECUTE_HANDLER) {
DbgPrint("Hello\r\n");
}
__try{
i--;
}
__except(EXCEPTION_EXECUTE_HANDLER){
DbgPrint("Hello\r\n");
}
}
前面簡歷堆疊的操作是相同的,而且其第一個try 塊的操作也是相同的,不同的如下所示。在將結構體的TryLevel 設定為 0FFFFFFFEh 之後,原來的函式已經開始了結束函式的操作,而這裡是跳轉到了下一個目標地址。
.text:0001105C mov [ebp+ms_exc.registration.TryLevel], 0FFFFFFFEh
.text:00011063 jmp short loc_11082
.text:00011065 $LN6: ; DATA XREF: .rdata:stru_12108 o
.text:00011065 mov eax, 1 ; Exception filter 0 for function 11010
.text:0001106A
.text:0001106A $LN8:
.text:0001106A retn
.text:0001106B ; ---------------------------------------------------------------------------
.text:0001106B
.text:0001106B $LN7: ; DATA XREF: .rdata:stru_12108 o
.text:0001106B mov esp, [ebp+ms_exc.old_esp] ; Exception handler 0 for function 11010
.text:0001106E push offset Format ; "Hello\r\n"
.text:00011073 call _DbgPrint
.text:00011078 add esp, 4
.text:0001107B mov [ebp+ms_exc.registration.TryLevel], 0FFFFFFFEh
.text:00011082
該目標地址處的操作如下所示,將TryLevel 設定為1,而上面為0
.text:00011082 loc_11082: ; CODE XREF: SEHTest()+53 j
.text:00011082 mov [ebp+ms_exc.registration.TryLevel], 1
.text:00011089 mov ecx, [ebp+i]
.text:0001108C sub ecx, 1
.text:0001108F mov [ebp+i], ecx
進行i—操作
.text:00011092 mov [ebp+ms_exc.registration.TryLevel], 0FFFFFFFEh
同樣是將TryLevel 設定為 0FFFFFFFEh:
.text:00011099 jmp short loc_110B8
.text:0001109B ; ---------------------------------------------------------------------------
.text:0001109B
.text:0001109B $LN10: ; DATA XREF: .rdata:stru_12108 o
.text:0001109B mov eax, 1 ; Exception filter 1 for function 11010
.text:000110A0
.text:000110A0 $LN12:
.text:000110A0 retn
.text:000110A1 ; ---------------------------------------------------------------------------
.text:000110A1
.text:000110A1 $LN11: ; DATA XREF: .rdata:stru_12108 o
.text:000110A1 mov esp, [ebp+ms_exc.old_esp] ; Exception handler 1 for function 11010
.text:000110A4 push offset Format ; "Hello\r\n"
.text:000110A9 call _DbgPrint
.text:000110AE add esp, 4
.text:000110B1 mov [ebp+ms_exc.registration.TryLevel], 0FFFFFFFEh
.text:000110B8
.text:000110B8 loc_110B8: ; CODE XREF: SEHTest()+89 j
到這裡向下函式的執行是一樣的,即恢復原來的fs:0,然後恢復堆疊。
另外,上下兩個程式中引用的結構體,也發生了變化。上面的結構體的長度和下面的結構體長度不同。很明顯,這是一個不定長的結構
.rdata:00012108 stru_12108 dd 0FFFFFFFEh ; GSCookieOffset
.rdata:00012108 ; DATA XREF: SEHTest()+7 o
.rdata:00012108 dd 0 ; GSCookieXOROffset ; SEH scope table for function 11010
.rdata:00012108 dd 0FFFFFFD4h ; EHCookieOffset
.rdata:00012108 dd 0 ; EHCookieXOROffset
第一個異常塊
.rdata:00012108 dd 0FFFFFFFEh ; ScopeRecord.EnclosingLevel
.rdata:00012108 dd offset $LN6 ; ScopeRecord.FilterFunc
.rdata:00012108 dd offset $LN7 ; ScopeRecord.HandlerFunc
第二個異常塊
.rdata:00012108 dd 0FFFFFFFEh ; ScopeRecord.EnclosingLevel
.rdata:00012108 dd offset $LN10 ; ScopeRecord.FilterFunc
.rdata:00012108 dd offset $LN11 ; ScopeRecord.HandlerFunc
我們再次增加函式內try結構的個數如下
void SEHTest(){
int i = 0;
__try{
i++;
}
__except(EXCEPTION_EXECUTE_HANDLER){
DbgPrint("Hello\r\n");
}
__try{
i--;
}
__except(EXCEPTION_EXECUTE_HANDLER){
DbgPrint("Hello\r\n");
}
__try{
i += 2;
}
__except(EXCEPTION_EXECUTE_HANDLER){
DbgPrint("Hello\r\n");
}
}
我們發現其添加了如下結構
loc_110B8:
mov [ebp+ms_exc.registration.TryLevel], 2
mov edx, [ebp+i]
add edx, 2
mov [ebp+i], edx
mov [ebp+ms_exc.registration.TryLevel], 0FFFFFFFEh
jmp short loc_110EE
上述變長結構體變為這樣:
.rdata:00012108 stru_12108 dd 0FFFFFFFEh ; GSCookieOffset
.rdata:00012108 ; DATA XREF: SEHTest()+7 o
.rdata:00012108 dd 0 ; GSCookieXOROffset ; SEH scope table for function 11010
.rdata:00012108 dd 0FFFFFFD4h ; EHCookieOffset
.rdata:00012108 dd 0 ; EHCookieXOROffset
.rdata:00012108 dd 0FFFFFFFEh ; ScopeRecord.EnclosingLevel
.rdata:00012108 dd offset $LN7 ; ScopeRecord.FilterFunc
.rdata:00012108 dd offset $LN8 ; ScopeRecord.HandlerFunc
.rdata:00012108 dd 0FFFFFFFEh ; ScopeRecord.EnclosingLevel
.rdata:00012108 dd offset $LN11 ; ScopeRecord.FilterFunc
.rdata:00012108 dd offset $LN12 ; ScopeRecord.HandlerFunc
.rdata:00012108 dd 0FFFFFFFEh ; ScopeRecord.EnclosingLevel
.rdata:00012108 dd offset $LN15 ; ScopeRecord.FilterFunc
.rdata:00012108 dd offset $LN16 ; ScopeRecord.HandlerFunc
似乎發現了什麼,每新增一個try 塊,新增一個程式碼塊,且其TryLevel遞增,上面的結構體增加一個單位的子結構。
然後我們再新增巢狀形式的SHE,使其格式如下:
void SEHTest() {
int i = 0;
__try {
i++;
}
__except(EXCEPTION_EXECUTE_HANDLER) {
DbgPrint("Hello\r\n");
}
__try {
i--;
__try {
i += 2;
}
__except(EXCEPTION_EXECUTE_HANDLER) {
DbgPrint("Hello\r\n");
}
}
__except(EXCEPTION_EXECUTE_HANDLER) {
DbgPrint("Hello\r\n");
}
}
上面第一個程式碼塊的執行依然不變,但是,當執行下面的結構的時候,我們來看。
.text:00011082 loc_11082: ; CODE XREF: SEHTest()+53 j
.text:00011082 mov [ebp+ms_exc.registration.TryLevel], 1
.text:00011089 mov ecx, [ebp+i]
.text:0001108C sub ecx, 1
.text:0001108F mov [ebp+i], ecx
i-- 對應的程式碼塊,TryLevel 為 1,但是其執行結束後沒有將TryLevel 恢復到0FFFFFFFEh。而是直接執行了下面的巢狀的try 塊
.text:00011092 mov [ebp+ms_exc.registration.TryLevel], 2
.text:00011099 mov edx, [ebp+i]
.text:0001109C add edx, 2
.text:0001109F mov [ebp+i], edx
.text:000110A2 mov [ebp+ms_exc.registration.TryLevel], 1
i += 2 對應的程式碼塊,TryLevel為 2,然後其執行結束後,自動將TryLevel 設定為1,也不是0FFFFFFFEh。
.text:000110A9 jmp short loc_110C8
最後函式跳轉到如下地址,然後將其TryLevel 設定為 0FFFFFFFEh ,然後恢復fs:0 恢復堆疊等操作。
.text:000110C8 loc_110C8: ; CODE XREF: SEHTest()+99 j
.text:000110C8 mov [ebp+ms_exc.registration.TryLevel], 0FFFFFFFEh
.text:000110CF jmp short loc_110EE
我們可以看到,try 塊與其包含的程式碼塊的對應是通過TryLevel 來對應的,在進入一個新塊之前會設定這個塊的等級,然後在執行完這個塊之後會恢復到之前TryLevel。而這裡的TryLevel可以理解為對應的變長結構體的子結構體的下標。
下面我們來看這個子結構有什麼變化,因為與上個程式相比,儘管塊的個數沒有改變,但是塊的結構改變了------增加了巢狀的try 塊這樣的結構。
.rdata:00012108 stru_12108 dd 0FFFFFFFEh ; GSCookieOffset
.rdata:00012108 ; DATA XREF: SEHTest()+7 o
.rdata:00012108 dd 0 ; GSCookieXOROffset ; SEH scope table for function 11010
.rdata:00012108 dd 0FFFFFFD4h ; EHCookieOffset
.rdata:00012108 dd 0 ; EHCookieXOROffset
.rdata:00012108 dd 0FFFFFFFEh ; ScopeRecord.EnclosingLevel
.rdata:00012108 dd offset $LN7 ; ScopeRecord.FilterFunc
.rdata:00012108 dd offset $LN8 ; ScopeRecord.HandlerFunc
.rdata:00012108 dd 0FFFFFFFEh ; ScopeRecord.EnclosingLevel
.rdata:00012108 dd offset $LN11 ; ScopeRecord.FilterFunc
.rdata:00012108 dd offset $LN12 ; ScopeRecord.HandlerFunc
.rdata:00012108 dd 1 ; ScopeRecord.EnclosingLevel
.rdata:00012108 dd offset $LN15 ; ScopeRecord.FilterFunc
.rdata:00012108 dd offset $LN16 ; ScopeRecord.HandlerFunc
左邊是之前的,右邊是現在的。很容易發現,第三個結構的EnclosingLevel成員不同,綜合程式碼我們發現,其值跟程式碼中“進入3號try 塊之前的TryLevel 狀態以及,出3號try塊之後恢復的TryLevel 狀態相同”如果在對比前面兩個try 塊對應的子結構發現,這個EnclosingLevel 就是try 塊對應的外層的try 塊的下標。而0FFFFFFFEh 代表的是外層沒有try 塊(這個編譯器生成的try 塊)。
掌握了單個函式內部的try 巢狀之後我們來看try 塊內部呼叫函式的情況。
void SubFunc() {
int j;
__try {
j++;
}
__except(EXCEPTION_EXECUTE_HANDLER) {
DbgPrint("World\r\n");
}
}
void SEHTest() {
int i = 0;
__try {
SubFunc();
}
__except(EXCEPTION_EXECUTE_HANDLER) {
DbgPrint("Hello\r\n");
}
}
函式SEHTest 內部是最簡單的形式了。如下
.text:00011017 push offset stru_12108
.text:0001101C push offset __except_handler4
另外,看那個變長結構:
.rdata:00012104 dd rva _unwind_handler4
.rdata:00012108 stru_12108 dd 0FFFFFFFEh ; GSCookieOffset
.rdata:00012108 ; DATA XREF: SubFunc()+7 o
.rdata:00012108 dd 0 ; GSCookieXOROffset ; SEH scope table for function 11010
.rdata:00012108 dd 0FFFFFFD4h ; EHCookieOffset
.rdata:00012108 dd 0 ; EHCookieXOROffset
.rdata:00012108 dd 0FFFFFFFEh ; ScopeRecord.EnclosingLevel
.rdata:00012108 dd offset $LN5 ; ScopeRecord.FilterFunc
.rdata:00012108 dd offset $LN6 ; ScopeRecord.HandlerFunc
.rdata:00012128 stru_12128 dd 0FFFFFFFEh ; GSCookieOffset
.rdata:00012128 ; DATA XREF: SEHTest()+7 o
.rdata:00012128 dd 0 ; GSCookieXOROffset ; SEH scope table for function 110A0
.rdata:00012128 dd 0FFFFFFD4h ; EHCookieOffset
.rdata:00012128 dd 0 ; EHCookieXOROffset
.rdata:00012128 dd 0FFFFFFFEh ; ScopeRecord.EnclosingLevel
.rdata:00012128 dd offset $LN5_0 ; ScopeRecord.FilterFunc
.rdata:00012128 dd offset $LN6_0 ; ScopeRecord.HandlerFunc
兩個函式分別有兩個該結構。
到這裡我們就瞭解了編譯器的SHE 的結構了。一個函式如果有SEH 結構,將只進行一次註冊,即使用編譯器提供的__except_handler4函式,並用一個變長結構來表示單個函式內部try 塊的巢狀結構。如果try 塊中有函式呼叫,且該函式內部也有SHE 結構,其巢狀是通過系統SHE 機制來確保的。即,當子函式中的SHE 不能滿足需要,將遍歷其父函式的SHE 函式(對於同一編譯器來說是同一個異常處理函式)。這樣可以確保不同編譯器編譯的模組之間的呼叫不會因為SHE 的實現不同而導致錯誤。下一節我們來看看這個__except_handler4 函式,來解決文章開頭出現的問題。
相關推薦
SEH 進階(1)
SHE進階 瞭解了上一篇的文章之後,我們寫一個簡單的例子來驗證我們的想法,並學習新的知識。 不同的編譯器提供的增強版本SHE 可能不同,但是它們都是基於windows 底層SHE 的。我們使用Win10 1703 + VS2010 生成X86 Rlease 程式來驗證已經學
c#進階(1)—— Task Parallel Library 並行執行與串行執行
-128 serve 模擬 程序 www 操作 內存 兩個 1-1 本文參考的博文出處:http://www.cnblogs.com/stoneniqiu/p/4857021.html 總體說明: (1)、理解硬件線程和軟件線程 硬件線程也稱為邏輯內核,一個物理內核可以
python進階(1)——模組:開箱即用
一.開箱即用 之前總結的將模組作為函式匯入程式中:https://mp.csdn.net/postedit/80904368 二.查明模組包含什麼:dir() dir(copy) 使用help獲取幫助 help(copy) help(
爬蟲進階(1)
import random import requests from fake_useragent import UserAgent from retrying import retry # 裝飾器 下載錯誤重複下載 import hashlib # 資訊摘要 md5 import q
Hibernate 進階(1)
Hibernate 關聯關係 單向關聯:例如物品和使用者構建單向關係,則可以通過使用者檢視物品,去不能通過物品檢視使用者。 雙向關聯:兩者之間可以互相訪問。 關係可以分為:單向1-N,單向N-1,單向N-N
vue進階(1) ---自定義元件
vue自定義元件 1、區域性元件,區域性元件必須要手動掛載,不然無法生效 2、全域性元件,全域性元件不需要手動掛載,但是不常用,儘量不要在全域性上掛載變數或者元件(可能會影響瀏覽器效能) 3、配合
python 進階(1)
python進階(1)參考慕課python教程1.函數語言程式設計1.1 函數語言程式設計簡介a.python不是純函數語言程式設計b.python中變數可以指向函式,函式名就是指向函式的變數。c.高階函式: 可以接受函式作為引數的函式,def add(x,y,f):ret
python進階(2)——re模組:正則表示式1
實驗結果輸出文件,包括多項引數(大約幾百個),想把所有的loss value對應的值提取出來,畫出曲線圖,這就需要用到正則表示式,基於此,開始學習正則表示式。 正則表示式:可匹配文字片段的模式 萬用字元:句點.(.ython與jpython與python與 ython都匹配,但不與ython
水波紋進度條(自定義View——進階篇1)
自定義控制元件——ProgressCircleView(水波紋進度條) 最近在很多群都有提到水波紋進度條,看起來蠻唬人的,但是我們要相信毛爺爺的話,一切看起來唬人的都是紙老唬,一言不合,還是先貼效果圖
android-進階(3)-自定義view(1)
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" xmlns:custom="http://sch
Python爬蟲(入門+進階)學習筆記 3-1 爬蟲工程師進階(七):HTTP請求分析
Chrome瀏覽器相對於其他的瀏覽器而言,DevTools(開發者工具)非常強大。這節課將為大家介紹怎麼利用Chrome瀏覽器的開發者工具進行HTTP請求分析Chrome瀏覽器講解Chrome 開發者工具是一套內置於Google Chrome中的Web開發和除錯工具,可用來對
mysql進階(二)之細談索引、分頁與慢日誌
連表 組合索引 rar 偏移量 最小值 num glob 要求 for 索引 1、數據庫索引 數據庫索引是一種數據結構,可以以額外的寫入和存儲空間為代價來提高數據庫表上的數據檢索操作的速度,以維護索引數據結構。索引用於快速定位數據,而無需在每次訪問數據庫表時搜索數據
python學習之函數學習進階(二)
python學習之函數進階二一、內置函數 zip函數: zip()是Python的一個內建函數,它接受一系列可叠代的對象作為參數,將對象中對應的 元素按順序組合成一個tuple,每個tuple中包含的是原有序列中對應序號位置的元素,然後返回由 這些tuples組成的list。若傳入參數的長度不等,則返回li
Python自動化開發課堂筆記【Day06】 - Python進階(類)
擴展性 程序 lex 類名 人物 優點 ini 參數 self. 類與對象 面向過程的程序設計: 優點:極大的降低了程序的復雜度 缺點:一套流水線或者流程就是用來解決一個問題,生產汽水的流水線無法生產汽車,即使能,也是得大改,改一個組件,牽一發而動全身面向對象的程序設計
mysql進階(一) mysql備份
mysql備份的目的: 實現災難恢復:誤操作、硬件故障、軟件故障、自然災害、黑客攻擊 註意的要點: 1、能夠容忍丟失多少數據 2、恢復數據所用的時間 3、備份需要的時間 4、是否對業務有影響 5、備份時服務器負載 備份類型 完全備份:備份整個
蘋果新的編程語言 Swift 語言進階(二)--基本數據類型
保持 popu 多條語句 常量 num access 對象 程序 進制 一 、 常量和變量 Swift語言 對常量和變量的聲明進行了明白的區分 Swift語言的常量類型比C 語言的co
mysql進階(三)MHA高可用集群
mysql mha簡介: 1、MHA目前在MySQL高可用方面是一個相對成熟的解決方案,是MySQL高可用環境下故障切換和主從提升的高可用軟件 2、MHA能在短時間內完成故障切換,並且在最大程度上保證數據的一致性,以達到真正意義上的高可用 3、MHA基於mysql協議,通過mysql主從或主主進行復制 4、
Python進階(3)_進程與線程中的lock(互斥鎖、遞歸鎖、信號量)
fun 我們 bsp 控制 支持 發生 class 線程 數據操作 1、同步鎖 (Lock) 當各個線程需要訪問一個公共資源時,會出現數據紊亂 例如: 1 import threading,time 2 def sub(): 3 global num
自己定義ViewGroup控件(二)----->流式布局進階(二)
avi ride sch get spec tracking htm out fst main.xml <?xml version="1.0" encoding="utf-8"?> <com.exa
函數進階(一)
並行 自己的 習題 文件 false 聲明 方式 關鍵字 true 1.命名空間 本質:存放名字與值的綁定關系 命名空間的分類:(1)全局命名空間(變量)->位於函數體外 (2)局部命名空間(變量)->