Notes on counterfeit return address bypassing CallStack detection and counterfeit return address detection

Source: Internet
Author: User

Source: Security Focus

Author: [CISRG] KiSSinGGer
E-mail: kissingger@gmail.com
MSN: kyller_clemens@hotmail.com

Questions... Anti-CallStack Check and Anti-CallStack Check ...(;--)

We found that the two articles on MJ0011 and gyzy's "stack-based fingerprint-based buffer overflow detection ideas" are similar.
Both of them are done by detecting the return address in CallStack.
I have recently studied some AntiRootkit technologies, which have to attract my attention.

According to the logic of MJ0011, The CallStack is detected from the Hook point of the Rootkit Detector.
But there are some DWORDs in CallStack. How can we determine where is a parameter and where is the return address?
I have two Goo jobs... I usually use EBP backtracking.
Consider most of the _ stdcall format:
Mov edi
Push ebp
Mov ebp esp
...
...
We can obtain the EBP of the previous call from dword ptr [EBP], dword ptr [EBP + 4] to obtain the return address to be detected, and then EBP = dword ptr [EBP], continue searching. locate the stack base address.
Check whether the returned address is in a valid module.

However, according to the <write Shellcode that bypasses Kabbah's active defense> In gyzy's article, we can see that the following method can invalidate this detection method.

1. Find a C3 (ret Opcode) byte in the valid system module (e.g. ntoskrnl.exe), and its pointer is K.
2. Use the following Hook Function

HookedZwXxx (...)
{
//
// Some parameter processing operations
//

Jmp _ pushrealretaddr
_ Trickstage:

Push Arg [N]
Push Arg [N-1]
...
Push Arg [0]

Push K
Jmp ZwXxx; // call the original function

_ Pushrealretaddr:
Call _ trickstage

Realretaddr:

//
// Other result processing operations
//
}

In this way, check the call stack in the depth of ZwXxx. dword ptr [EBP + 4] is an address in the valid module K.

I wrote the following ring3 sample program.

Define the following functions:
Int _ stdcall Call_C (int a, int B)
{
Check_callstack ();
Return a + B;
}

Int _ stdcall Call_ B (int a, int B)
{
Return Call_C (a, B );
}

Int _ stdcall Call_A (int a, int B)
{
Return Call_ B (a, B );
}

The call order is A-> B-> C, where C executes check_callstack () to check whether an invalid return address exists.

Void
_ Stdcall
Check_callstack (void)
{
Int saved_ebp;
Int retaddr;

Printf ("Check Call Stack Methord 1 :");
_ Asm
{
Mov eax, dword ptr [ebp + 4]
Mov retaddr, eax
Mov eax, dword ptr [ebp]
Mov saved_ebp, eax
}
Printf ("retaddr = 0x % 08X", retaddr );

While (saved_ebp <StackBase & saved_ebp> 0)
{
If (saved_ebp! = 0)
{
Retaddr = * (int *) (saved_ebp + 4 );
Printf ("retaddr = 0x % 08X", retaddr );
Saved_ebp = * (int *) saved_ebp;
}
}
}

If no Hook exists, we execute Call_A () and return 3 as normal.

Check_callstack output:
Retaddr = 0x00401008
Retaddr = 0x00401030
Retaddr = 0x00401050
Retaddr = 0x00401126
Retaddr = 0x0040149D
Retaddr = 0x7C816FD7

Now we use the Hooked_Call_ B function to Hook Call_ B in Call_A.
The Hook Call_ B only changes the return value to 4.

_ Declspec (naked)
Int Hooked_Call_ B (int a, int B)
{
_ Asm
{
Push ebp
Mov ebp, esp
Jmp _

_ Trickstage:

Mov eax, B
Push eax
Mov eax,
Push eax
// To facilitate the use of an OD hard code: P
Push 0x004011AD // This address points to a C3
Jmp Call_ B

_:
Call _ trickstage
Mov eax, 4 // here, change the return value so that the result of 1 + 2 is 4.
Pop ebp
Ret 8
}
}

Used to rewrite the Call_A function. This function causes exceptions in the EXE compiled in 2003.
Because the. text Segment has no write permission, I used StudPE to change the segment attribute in the actual test.
If you want to modify the attributes of the Code section...

Int _ stdcall SetHook (int Hook_Call)
{
Int Original_Call = 0;
Int hook_pos = (int) Call_A;

//
// The following ugly code finds the location of the "call Call_ B" command in Call_A
//
_ Asm
{
_ Again:
Mov eax, hook_pos
Xor ecx, ecx
Mov cl, byte ptr ds: [eax]
Cmp cl, 0xE8
Je _ finish
Mov edx, hook_pos
Add edx, 1
Mov hook_pos, edx
Jmp _ again
}
_ Finish:

//
// Use Hook_Call patch to delete the address after the call
//

Hook_Call = Hook_Call-hook_pos-5;
_ Asm
{
Mov eax, Hook_Call
Mov edi, hook_pos
Mov dword ptr [edi + 1], eax
}
Return hook_pos;
}

We will call SetHook (Hooked_Call_ B) to change "call Call_ B" in Call_A.

The address of Hooked_Call_ B is [0x004010B0, 0x004010D2] In the debugger.
If the callback CallStack method is effective based on EBP, A retaddr belonging to [0x004010B0, 0x004010D2] should be successfully found after Hooked_Call_ B takes effect.

Unfortunately, no...

Check_callstack output:
Retaddr = 0x00401008
Retaddr = 0x00401030
Retaddr = 0x004011AD <-- note this
Retaddr = 0x00401050
Retaddr = 0x0040114D
Retaddr = 0x0040149D
Retaddr = 0x7C816FD7

We can see that our normal return address is replaced by a seemingly legal 0x004011AD.

So here we conclude that, according to the tracing of EBP, this method (called Detour Ret? : P) fooled.

Another question.

Let's look at the actual stack in OD. This is the time to stop in Call_C.

0012FEA0 0012FEB4 <-- current EBP
0012FEA4 004011AD <-- Counterfeit return address, pointing to C3
0012FEA8 00000001 <-
0012 FEAC 00000002 <-Two Parameters
0012FEB0 004010CC <-- real return address!
0012FEB4/0012FEC4
0012FEB8 | 00401050
0012 FEBC | 00000001
0012FEC0 | 00000002

When Call_C exits, run:
Pop ebp
Ret 8
Register status afterwards:
Ebp = 0012FEB4
Esp = 0012FEB0
Eip = 004011AD

At this time, the code is executed to. The ret at ad will make the eip = dword ptr [esp], so that it will return to CC smoothly.

Er? In this case, the malicious return address of CC exists in CallStack. The key is how to determine it.
EBP does not support backtracking. Maybe ESP does not support backtracking... I am confused about this specific method. MJ0011 means using ESP backtracking. In this way, we have to consider the number of parameters for each call.

In this way, I have a thought:
Determine whether to point to a C3.
If yes, retaddr = the first parameter location + number of parameters * 4
If not, retaddr = dword ptr [EBP + 4]

Modify check_callstack:

Void
_ Stdcall
Check_callstack (void)
{
Int saved_ebp;
Int retaddr;

// [Number of parameters] x4. For kernel routines, the parameters are usually fixed.
Int stack_fix = 0x8;

Printf ("Check Call Stack Methord 2 :");

_ Asm
{
Mov eax, dword ptr [ebp + 4]
Mov retaddr, eax
Mov eax, dword ptr [ebp]
Mov saved_ebp, eax
}
Printf ("retaddr = 0x % 08X", retaddr );

While (saved_ebp <StackBase & saved_ebp> 0)
{
If (saved_ebp! = 0)
{
Retaddr = * (int *) (saved_ebp + 4 );
Printf ("retaddr = 0x % 08X", retaddr );

If (retaddr! = 0)
{
If (* (unsigned char *) retaddr = 0xC3)
{
//
// If the returned command points to C3, we need to check the return address after the push parameter.
// Sorry for my ugly expression :(

Retaddr = * (int *) (saved_ebp + 8 + stack_fix );
Printf ("Suspicious retaddr found: 0x % 08x", retaddr );
}
}
Saved_ebp = * (int *) saved_ebp;
}
}
}

Let's run the program to verify:

NO Hook:

Retaddr = 0x0040100D
Retaddr = 0x00401030
Retaddr = 0x00401050
Retaddr = 0x00401126
Retaddr = 0x0040149D
Retaddr = 0x7C816FD7

Hook:

Retaddr = 0x0040100D
Retaddr = 0x00401030
Retaddr = 0x004011AD
Suspecious retaddr found: 0x004010cc
Retaddr = 0x00401050
Retaddr = 0x0040114D
Retaddr = 0x0040149D
Retaddr = 0x7C816FD7

Find the 0x004010cc that belongs to [0x004010B0, 0x004010D2]

Can we determine whether this stack tracing check is effective?
Still cannot draw conclusions...

If the spoofed return address points to a "C2 XXXX "?
For example, we write this in Hooked_Call_ B:
Push xxx // here, push two items at will, and balance the stack with ret 8
Push xxx
Mov eax, B
Push eax
Mov eax,
Push eax

Push K // This address points to a C2 08 00 (ret 8)
Jmp Call_ B

Then, we have to check whether the returned address is C2 and obtain a WORD after C2, this WORD is used to determine the true return address of the 3rd DWORD following the Arg [N] stack position.

Furthermore, if the forged return address K points to the following command sequence:
Pop eax
Pop ebx
Pop ebp
Ret 8

We have to perform Semantic Analysis on the returned address (pop + ret) to determine the real return address... it is located at the third DWORD behind the Arg [N] stack position...

And
If the returned address contains the add, sub, and so on for esp, you will need to perform more checks.

Although I have implemented a simple C3

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.