⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 29a-7.006

📁 从29A上收集的病毒源码
💻 006
📖 第 1 页 / 共 2 页
字号:

                            How to break the rules
                           with the class libraries
                               roy g biv / 29A



About the author:

Former  DOS/Win16  virus writer, author of several virus  families,  including
Ginger  (see Coderz #1 zine for terrible buggy example, contact me for  better
sources  ;),  and Virus Bulletin 9/95 for a description of what   they  called
Rainbow.   Co-author  of  world's first virus using circular  partition  trick
(Orsam, coded with Prototype in 1993).  Designer of world's first XMS swapping
virus  (John Galt, coded by RT Fishel in 1995, only 30 bytes stub, the rest is
swapped  out).   Author of world's first virus using Thread Local Storage  for
replication  (Shrug, see Virus Bulletin 6/02 for a description, but they  call
it Chiton), world's first virus using Visual Basic 5/6 language extensions for
replication  (OU812), world's first Native executable virus (Chthon),  world's
first  virus  using process co-operation to prevent termination  (Gemini,  see
Virus  Bulletin 9/02 for a description), world's first virus using polymorphic
SMTP  headers  (Junkmail,  see Virus Bulletin 11/02 for  a  description),  and
world's  first  viruses that can convert any data files to infectable  objects
(Pretext).   Author  of  various retrovirus articles (eg see Vlad #7  for  the
strings  that make your code invisible to TBScan).  Went to sleep for a number
of years.  This is my first virus for .NET.  It is the world's first 32/64-bit
parasitic EPO .NET virus. :)


Object-oriented language programming

At  the first look, object-oriented languages in the .NET framework (C#,  C++,
JScript,  and Visual Basic) seem to be very strict.  There are no pointers  in
JScript and Visual Basic, and they are limited in C# and C++.  Type conversion
is also restricted.


BitConverter class

Imagine  that we have an array of bytes (because only a byte array is  allowed
for the buffer when accessing files in the .NET framework).  We have read some
bytes  from the file and now we want to check for the 'MZ' signature (assuming
little-endian machine).  In C, we could use:

    if (*((WORD *) &array[0]) == 0x5a4d)

Can  we do that in JScript or Visual Basic?  No.  Can we do that in C++?  Yes,
but  only for unmanaged arrays, which cannot be used with the class libraries.
Can  we  do it in C#?  No (really yes, but it requires "Unsafe coding",  which
causes other problems).  We can form the word from the array like this:

    if (((array[1] << 8) + array[0]) == 0x5a4d)        //C#, C++, JScript
    if (((array(1) * &h100) + array(0)) = &h5a4d) then 'Visual Basic

but  that  requires lots of bytes of code each time to do that.   Fortunately,
the class libraries contain a class that exposes methods for exactly this kind
of  data access.  The class is called BitConverter, and the method we can  use
here  is called ToInt16().  It takes two parameters: the array and the offset.
So now our code can be replaced by:

    if (BitConverter.ToInt16(array, 0) == 0x5a4d)     //C#, JScript
    if (BitConverter::ToInt16(array, 0) == 0x5a4d)    //C++
    if (BitConverter.ToInt16(array, 0) = &h5a4d) then 'Visual Basic

Nice.   The class exposes other methods, too, such as ToInt32() and ToInt64(),
and also versions for unsigned numbers (ToUInt16(), etc).


Marshal class

Now we want to write large values into a byte array.  In C, we could use

    *((DWORD *) &array[0]) = dword;

In C#, C++, and JScript, we can store a dword in the array like this:

    array[0] = (Byte) (dword);
    array[1] = (Byte) (dword >> 8);
    array[2] = (Byte) (dword >> 0x10);
    array[3] = (Byte) (dword >> 0x18);

In Visual Basic, we can store a dword in the array like this:

    array(0) = dword mod &h100
    array(1) = (dword \ &h100) mod &h100
    array(2) = (dword \ &h10000) mod &h100
    array(3) = (dword \ &h1000000) mod &h100

but  that  requires  too many instructions and too many bytes  of  code.   The
better  way  is to use the Marshal class.  It allows us to copy  data  between
managed  data  types  (.NET base types) and unmanaged data types  (eg  process
memory).   It can also copy from one managed type to another managed type, but
this  use  is  not  documented.  The method that we can  use  here  is  called
WriteInt32().   It takes three parameters: the array, the offset in the array,
and the value.  So now our code can be replaced by:

    Marshal.WriteInt32(array, 0, dword);  //C#, JScript
    Marshal::WriteInt32(array, 0, dword); //C++
    Marshal.WriteInt32(array, 0, dword)   'Visual Basic

Much better.  Accessing process memory is easy, too.  The Process class exists
for that purpose.  Let's get our module handle:

    IntPtr ourbase = Process.GetCurrentProcess().MainModule.BaseAddress;                //C#
    IntPtr ourbase = Process::GetCurrentProcess()->get_MainModule()->get_BaseAddress(); //C++
    var ourbase : IntPtr = Process.GetCurrentProcess().MainModule.BaseAddress           //JScript
    dim ourbase as IntPtr : Process.GetCurrentProcess().MainModule.BaseAddress          'Visual Basic

Great!  But what's an IntPtr?


Marshal class again

The  IntPtr  type is a special managed type whose size  is  platform-specific.
That means that it is 32 bits large on a 32-bit platform, and 64-bits large on
a 64-bit platform.  The Marshal methods require an IntPtr as the object to use
for  reading  and writing unmanaged memory.  IntPtrs do not support  operators
such  as  + and - (they are like void * in C and C++), but since  the  Marshal
methods require an offset, this is usually okay.  For example, we can read the
lfanew field without trouble:

    Int32 lfanew = Marshal.ReadInt32(ourbase, 0x3c);       //C#
    Int32 lfanew = Marshal::ReadInt32(ourbase, 0x3c);      //C++
    var lfanew : Int32 = Marshal.ReadInt32(ourbase, 0x3c)  //JScript
    dim lfanew as Int32 : Marshal.ReadInt32(ourbase, &h3c) 'Visual Basic

and then read bytes in the PE header:

    Int32 tlsrva = Marshal.ReadInt32(ourbase, lfanew + 0xc0);                //C#
    Int32 tlsrva = Marshal::ReadInt32(ourbase, lfanew + 0xc0);               //C++
    var tlsrva : Int32 = Marshal.ReadInt32(ourbase, lfanew + 0xc0)           //JScript
    dim tlsrva as Int32 : tlsrva = Marshal.ReadInt32(ourbase, lfanew + &hc0) 'Visual Basic

But  what  if we want to copy any number of bytes?  There is a Copy()  method,
and  it  takes four parameters: the source array, the destination  array,  the
offset  in  destination array, and the number of bytes to copy.  It  does  not
support  an offset in the source array, so we need to find a way to apply  the
+  operator  to  a IntPtr.  Fortunately, the Marshal class  exposes  a  method
called  ReadIntPtr(), which can convert a Int32 to a IntPtr, but this usage is
also  not  documented.  To make our pointer, we convert our old IntPtr into  a
Int32  by  using the ToInt32() method, then add the value that we  need,  then
pass the result to the ReadIntPtr() method, using an offset of 0:

    IntPtr tlsva = Marshal.ReadIntPtr(ourbase.ToInt32() + tlsdata, 0);               //C#
    IntPtr tlsva = Marshal::ReadIntPtr(__box(ourbase.ToInt32() + tlsdata), 0);       //C++
    var tlsva : IntPtr = Marshal.ReadIntPtr(ourbase.ToInt32() + tlsdata, 0)          //JScript
    dim tlsva as IntPtr : tlsva = Marshal.ReadIntPtr(ourbase.ToInt32() + tlsdata, 0) 'Visual Basic

For 64-bit platforms, there is a ToInt64() method, and the ReadIntPtr() method
can  also  convert  an Int64 to an IntPtr.  Notice the use of __box  for  C++.
This  is required to convert a value into a managed object, which can then  be
passed to the class libraries.

Now  we can use the Copy() method to copy bytes from the process memory to our
array.  Let's copy 0x29a bytes ;) to offset 6:

    Marshal.Copy(tlsva, array, 6, 0x29a);  //C#, JScript
    Marshal::Copy(tlsva, array, 6, 0x29a); //C++
    Marshal.Copy(tlsva, array, 6, &h29a);  'Visual Basic


TLS and MSIL

I  thought  to make an interesting W32/MSIL hybrid by using TLS to  write  the
MSIL  RVA in the PE header at runtime, but when the MSIL code is run, it fails
the StrongName signature verification.  Then I tried storing a hard-coded fake
RVA  in the file, and setting it correctly at runtime, but the MSIL code won't
run in that case.  Finally, I stored the correct RVA and size in the file, but
if  TLS  code runs on exit, then one of the MSIL dlls waits infinitely for  an
object  that never signals and the application won't terminate.  So don't  use
TLS in MSIL files unless you can solve one of these problems. ;)


GetProcAddress()

.NET  files import functions in a way that is simlar to Windows files.   There
are  AssemblyRefs  which  are  the DLL names, and  MemberRefs  which  are  the
function  names (there  are also TypeRefs, and they hold the class names,  but
they  have  no equivalent).  Let us imagine that we want to display our  image
base, using only standard library functions (mscorlib).  The image base is the
BaseAddress  property of the ProcessModule class.  The ProcessModule class  is
returned  by the MainModule property of the Process class.  The Process  class
is  in the System.Diagnostics  namespace.  The System.Diagnostics namespace is
exported  by system.dll.  Since system.dll is not a standard library, we  need
to  find  how  to load system.dll and get access to its methods,  but  without
using  any  function from system.dll to do that.  We also have to rely on  the
GetType()  method, instead of casting the result, because casts use references
to  the  dll  that exports the class.  The .NET  equivalent  of  LoadLibrary()
requires  the full path of the file to load, if the file is not in the current
directory  (this  is  similar to LoadLibrary()).  System.dll is  in  the  .NET
system  directory (which is not the same as the Windows system directory),  so
we  need  to find the .NET system directory.  We can get the system  directory

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -