Modern dump technology and protection measures (II)

Source: Internet
Author: User
Tags readfile unpack hex code
Modern dump technology and protection measures (II)

Dynamic unpacking

Another common method to deal with dump is dynamic packing. The idea is that Protector does not completely unpack the protected program, but is only part of the unpack. First, when the first unpack page is about to complete, Protector intercepts the exception and unpack the requested page, then it can delete the previous page from the memory. In this way, the image of the protected process has never been complete in the memory, so the general dumper cannot perform dump. This method is widely used in the Protector program Armadillo and called copymem. To intercept exceptions and decode them, the program creates an independent process that uses debugapi to debug the protected process.

Most cracker methods are not optimal for removing such protection. Their approach is to reverse and patch the Protector code to force it to work out the complete code. This is really troublesome. What is even more unacceptable is that in each new version, the authors modify the code of this protection mechanism, so that the old dump method is no longer effective, so that people don't hesitate to throw it into the museum. But fortunately, we still have a simpler approach-dumping processes internally. Therefore, we need to inject the dump code into the address space of the protected process and read its memory. In this process, an exception will occur. When an exception is handled, protector will present all the decrypted code to us. This method of removing copymem protection is already the easiest way to come up. Copymem is still in use, because this method is not widely used.

Get the dump of the kernel module

When you reverse the protector of Ring 0, you often need to dump PE files that have been loaded to the core memory. For example, if protector patches the system kernel, we can compare the kernel dump with the disk file to easily detect this situation. Or dump the driver of protector and study its decryption method. It is not easy to do this, because all current protectors have learned to delete references to their own drivers in all system structures to hide the drivers, so the most practical way to drive dump is to dump all the core memory, and then find what we need in this dump. But this method is often not very effective, because the protection program designers have stopped the driver pack (such as starforce 3), but switched to a more promising protection method, such as multi-state code and virtual machines.

At first glance, the dump kernel module only needs to copy with copymem, but it is not that simple, because there is a trap. To read the module memory from the beginning to the end, all you can get is a blue screen. If you want to create an MDL for the required memory segment and execute the MMP robeandlockpages command, an exception is thrown. If you call the mmbuildmdlfornonpagedpool, the MDL will be created, but the blue screen will be triggered again when you want to read it. In the native PE file, the Section can have the "discardable as needed" attribute, so that the section will be deleted immediately after the driver entry ends. Such a system can save nonpaged pool because nonpaged pool is scarce in the system. Therefore, an exception occurs when you try to access this address. This exception is not handled by Seh, causing a system crash. Therefore, before reading the memory, we need to use mmisaddressvalid to verify the validity of the address. The following code securely reads the core memory:

Ntstatus dumpkernelmemory (pvoid srcaddr, pvoid dstaddr, ulong size)
{
Pmdl psrcmdl, pdstmdl;
Puchar paddress, pdstdddress;
Ntstatus ST = status_unsuccessful;
Ulong R;

Psrcmdl = ioallocatemdl (srcaddr, size, false, false, null );

If (psrcmdl)
{
Mmbuildmdlfornonpagedpool (psrcmdl );

Paddress = mmgetsystemaddressformdlsafe (psrcmdl, normalpagepriority );

If (paddress)
{
Pdstmdl = ioallocatemdl (dstaddr, size, false, false, null );

If (pdstmdl)
{
_ Try
{
MMP robeandlockpages (pdstmdl, usermode, iowriteaccess );

Pdstdddress = mmgetsystemaddressformdlsafe (
Pdstmdl, normalpagepriority );

If (pdstdddress)
{
Memset (pdstdddress, 0, size );

For (r = 1; r <size; r ++)
{
If (mmisaddressvalid (paddress) * pdstdddress = * paddress;
Paddress ++;
Pdstdddress ++;
}

St = STATUS_SUCCESS;
}

Mmunlockpages (pdstmdl );
}

_ Handler T (exception_execute_handler)
{
}

Iofreemdl (pdstmdl );
}
}

Iofreemdl (psrcmdl );
}

Return st;
}

In addition to memory reading, we also need to obtain the module information to know the read address. The following describes how to obtain this information.

Make the dump process more convenient

Writing your dumper is definitely a laborious task, so I decided to use PE tools because it considers the details of many PE files and can easily and quickly dump. So I wrote a plug-in called extreme dumper (opposite to extreme protector), which can implement dump driver and dump with dllinjection. For this reason, I intercepted zwopenprocess and zwreadvirtualmemory functions in PE tools and replaced them with my own functions. They are all written in the method described above. For example, here is the code of the zwreadvirtualmemory handler:

Function newzwreadvirtualmemory (processhandle: DWORD; baseaddress: pointer;
Buffer: pointer; bufferlength: DWORD;
Returnlength: pdword): ntstatus; stdcall;
VaR
Hpipe, bytes, Len: DWORD;
Req: txdumprequest;
Pipename: array [0 .. 128] of char;
Spid: array [0 .. 8] of char;
Processid: DWORD;
Query: tdriverquery;
Begin
If drivermethod then
Begin
Result: = DWORD (-1 );
Len: = bufferlength;
Query. querytype: = 2;
Query. param1: = processhandle;
Query. param2: = DWORD (@ Len );
Query. param3: = DWORD (baseaddress );
Query. param4: = DWORD (buffer );
Writefile (hdriver, query, sizeof (tdriverquery), bytes, nil );
If returnlength <> nil then returnlength ^: = Len;
If Len> 0 then result: = STATUS_SUCCESS;
End else
Begin
Processid: = getpid (processhandle );
Strcpy (pipename, bpipename );
Tohex (processid, 8, spid );
Strcat (pipename, spid );
Hpipe: = createfile (@ pipename,
Generic_write or generic_read,
0, nil, open_existing, 0, 0 );
If hpipe <> invalid_handle_value then
Begin
Req. Address: = baseaddress;
Req. Length: = bufferlength;
Writefile (hpipe, req, sizeof (txdumprequest), bytes, nil );
Readfile (hpipe, Len, sizeof (DWORD), bytes, nil );
Readfile (hpipe, buffer ^, Len, bytes, nil );
If returnlength <> nil then returnlength ^: = Len;
Result: = 0;
Closehandle (hpipe );
End else result: = truezwreadvirtualmemory (processhandle,
Baseaddress, buffer,
Bufferlength, returnlength );
End;
End;

We can see that in order for dumper to interact with the injected DLL to use Named Pipes in protected processes, the name is the hex code of the string extremedumper and process ID. Requests for reading memory are sent to the dump process, where the server reads the requested memory and returns the data and the number of bytes read. The dumper server code is as follows:

Library xdump;

Uses
Windows,
Advapihook,
Nativeapi;

{$ Include string. inc}

Type
Pxdumprequest = ^ txdumprequest;
Txdumprequest = packed record
Reqtype: DWORD;
Address: pointer;
Length: DWORD;
End;

Const
Bpipename = '//./pipe/extremedumper' #0;

Function safereadmemory (ADDR, Buff: pointer; Size: DWORD): DWORD;
ASM
Push EBX
Push edX
Push ECx
Push ESI
Push EDI
Push EBP
Push offset @ Handler
Push FS: [0]
MoV FS: [0], ESP
MoV ESI, eax
MoV EDI, EDX
Rep movsb
MoV eax, ECx
Pop FS: [0]
Add ESP, 4
Pop EBP
Pop EDI
Pop ESI
Pop ECx
Pop edX
Pop EBX
RET
@ Handler:
MoV ECx, [esp + $ 0C]
Add [ECx + $ B8], 2
RET
End;

Function pipethread (hpipe: DWORD): DWORD; stdcall;
VaR
Req: txdumprequest;
Bytes, Len: DWORD;
Pbuff: pointer;
Begin
Readfile (hpipe, req, sizeof (txdumprequest), bytes, nil );
Getmem (pbuff, req. Length );
Len: = Req. Length-safereadmemory (req. Address, pbuff, req. Length );
Writefile (hpipe, Len, sizeof (DWORD), bytes, nil );
Writefile (hpipe, pbuff ^, req. length, bytes, nil );
Freemem (pbuff );
Closehandle (hpipe );
End;

VaR
TRID: DWORD;

Procedure pipeserverthread ();
VaR
Hpipe: DWORD;
Pipename: array [0 .. 128] of char;
Spid: array [0 .. 8] of char;
Begin
Strcpy (pipename, bpipename );
Tohex (getcurrentprocessid (), 8, spid );
Strcat (pipename, spid );
Repeat
Hpipe: = createnamedpipe (pipename,
Pipe_access_duplex or write_dac,
Pipe_type_message or pipe_readmode_message or pipe_wait,
Pipe_unlimited_instances, 1024,102 4, 5000, nil );
If hpipe = invalid_handle_value then exit;
If connectnamedpipe (hpipe, nil) then
Createthread (nil, 0, @ pipethread, pointer (hpipe), 0, TRID );
Until false;
End;

Begin
Createthread (nil, 0, @ pipeserverthread, nil, 0, TRID );
End.

When the client connects to pipe, a thread is created. The thread reads the request data within the seh processing program, and the data is then routed to the client. The only drawback of this Code is its low efficiency because each request is served by a separate thread. However, in fact, too high efficiency is unnecessary, because it is important to get dump. Although some people may not be satisfied, they will write a better version, but I am so stupid here :).

Now let's get back to the kernel module information. To achieve this purpose, I will use the zwquerysysteminformation function and the class is systemmoduleinformation. In this way, the function will return the following types of struct arrays to me:

Psystem_module_information = ^ system_module_information;
System_module_information = packed record // Information Class 11
Reserved: array [0 .. 1] of ulong;
Base: pvoid;
Size: ulong;
Flags: ulong;
Index: ushort;
Unknown: ushort;
Loadcount: ushort;
Modulenameoffset: ushort;
Imagename: array [0 .. 255] of char;
End;

Psystem_module_information_ex = ^ system_module_information_ex;
System_module_information_ex = packed record
Modulescount: DWORD;
Modules: array [0 .. 0] of system_module_information;
End;

The fields we are interested in are imagename, base, and size. I found that in kernelmode, this information can also be obtained through the linked list psloadedmoduleslist, but it makes no sense to use it here.

To enumerate the modules loaded by a process, PE tools uses the getmodulefirst/getmodulenext function in procs32.dll. To map our own list, we will intercept and replace it with our version in the system process Zhong. The code for processing these functions is as follows:

Function newgetmodulenext (dwpid: DWORD; mentry: pmodule_entry): bool; stdcall;
Begin
If dwpid = systempid then
Begin
Result: = false;
Lstrcpy (mentry ^. lpfilename, modules ^. modules [currentmodule]. imagename );
Mentry ^. dwimagebase: = DWORD (modules ^. modules [currentmodule]. Base );
Mentry ^. dwimagesize: = modules ^. modules [currentmodule]. size;
Mentry ^. bsystemprocess: = true;
INC (currentmodule );
If currentmodule> modules ^. modulescount then
Releasemodulesinfo () else result: = true;
End else result: = truegetmodulenext (dwpid, mentry );
End;

Function newgetmodulefirst (dwpid: DWORD; mentry: pmodule_entry): bool; stdcall;
Begin
If dwpid = systempid then
Begin
If currentmodule> 0 then releasemodulesinfo ();
Modules: = getinfotable (systemmoduleinformation );
Result: = newgetmodulenext (dwpid, mentry );
End else result: = truegetmodulefirst (dwpid, mentry );
End;

To perform dump, PE tools uses the dumpprocess function in ndump. dll. We need to intercept this function and send the Dump Request to our own driver. The processing code is as follows:

Function newdumpprocess (dwprocessid: DWORD; pstartaddr: pointer;
Dwcbytes: DWORD; pdumpedbytes: pointer
): Bool; stdcall;
VaR
Query: tdriverquery;
Bytes: DWORD;
Begin
If dwprocessid = systempid then
Begin
Query. querytype: = ioctl_dump_kernel_mem;
Query. param1: = DWORD (pstartaddr );
Query. param2: = dwcbytes;
Query. param3: = DWORD (pdumpedbytes );
Result: = writefile (hdriver, query, sizeof (tdriverquery), bytes, nil );
End else result: = truedumpprocess (dwprocessid, pstartaddr,
Dwcbytes, pdumpedbytes );
End;

--------------------------------------------------------------------------------

Extreme dumper can be divided into the following parts:

Two dump methods (DLL-injection method and driver method)
Openprocessex)
Prevent PE tools from being affected by other processes (protect PE tools)
Dump (Enable kernel modules dumping) of the kernel module)

In short, this plug-in is undoubtedly very useful.

--------------------------------------------------------------------------------

I would like to remind the authors of articles such as program cracking should not conduct any activity in violation of Russian federal regulations. The content of this article can only be used for teaching purposes. The author shall not be liable for any illegal behavior caused by this (for any violation of the laws and regulations of the People's Republic of China, the translator shall not be liable ).

Dong Yan Translation
Http://greatdong.blog.edu.cn

 

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.