1. 程式人生 > >SEH 進階(1)

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

返回ExceptionContinueSearch,然後函式依然向下搜尋,直到有異常處理函式聲明瞭可以處理它。但是,為什麼這裡顯示呼叫了兩次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)、理解硬件線程和軟件線程   硬件線程也稱為邏輯內核,一個物理內核可以

python1——模組:開箱即用

  一.開箱即用 之前總結的將模組作為函式匯入程式中: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

vue1 ---自定義元件

vue自定義元件 1、區域性元件,區域性元件必須要手動掛載,不然無法生效 2、全域性元件,全域性元件不需要手動掛載,但是不常用,儘量不要在全域性上掛載變數或者元件(可能會影響瀏覽器效能) 3、配合

python 1

python進階(1)參考慕課python教程1.函數語言程式設計1.1 函數語言程式設計簡介a.python不是純函數語言程式設計b.python中變數可以指向函式,函式名就是指向函式的變數。c.高階函式:  可以接受函式作為引數的函式,def add(x,y,f):ret

python2——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

mysqlMHA高可用集群

mysql mha簡介: 1、MHA目前在MySQL高可用方面是一個相對成熟的解決方案,是MySQL高可用環境下故障切換和主從提升的高可用軟件 2、MHA能在短時間內完成故障切換,並且在最大程度上保證數據的一致性,以達到真正意義上的高可用 3、MHA基於mysql協議,通過mysql主從或主主進行復制 4、

Python3_程與線程中的lock互斥鎖、遞歸鎖、信號量

fun 我們 bsp 控制 支持 發生 class 線程 數據操作 1、同步鎖 (Lock) 當各個線程需要訪問一個公共資源時,會出現數據紊亂 例如: 1 import threading,time 2 def sub(): 3 global num

自己定義ViewGroup控件-----&gt;流式布局

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)局部命名空間(變量)->