如何编写Loader

来源:互联网 发布:人工智能将来发展方向 编辑:程序博客网 时间:2024/06/05 14:27

作者 : Detten    Detten@tiscali.be

来源 : http://biw.rult.at/

翻译 : nbw

 

1、什么是Loader,为什么需要它?

   所谓的Loader是一个用来加载其他程序的小程序。当然,只有被加载的内存的程序需要改动的时候我们才采用Loader。(内存补丁)

   Loader常用于让游戏玩家修改游戏。

   有很多原因导致我们选择Loader而不是一般的补丁程序。我们或许需要在程序CRC校验以后再进行修改,或者开始的时候修改内存数据,然后在程序中再恢复原来的数据.....

   我肯定你还可以找到其他一些用途。

 

2Loader是怎么工作的?

   OK,找到你的Win32.hlp然后坐下来:)

   首先,Loader必须创建一个进程启动目标程序。我们将用CreateProcess函数做这个(很明显嘛)。当目标程序被加载到内存,我们需要中断该进程,以便进行我们的修改。

   让我们查看一下win32.hlp对这个API函数的讲解:

   BOOL CreateProcess(

 

    LPCTSTR lpApplicationName,      // 可执行模块名称指针

    LPTSTR lpCommandLine,       // 命令行字符串指针

    LPSECURITY_ATTRIBUTES lpProcessAttributes,  // 进程安全属性指针

    LPSECURITY_ATTRIBUTES lpThreadAttributes,   // 线程安全属性指针

    BOOL bInheritHandles,       // 句柄继承标记

    DWORD dwCreationFlags,      // 创建标记

    LPVOID lpEnvironment,       // 新环境块指针

    LPCTSTR lpCurrentDirectory,     // 当前路径指针

    LPSTARTUPINFO lpStartupInfo,    // STARTUPINFO指针

    LPPROCESS_INFORMATION lpProcessInformation // PROCESS_INFORMATION指针

   );

   这里涉及到的API函数请查看win32.hlpl以了解详细内容,因为这里我只讲解重要的一些关键的API

 lpApplicationName 使目标程序的路径+名称. (例如 c:/somedir/crackme.exe)

 lpCommandLine 可以用来指定命令行参数,如果需要的话。

 dwCreationFlags 也很重要,因为我们需要随时中断被加载的进程,所以这里设置为CREATE_SUSPENDED  lpStartupInfo 指向一个代表启动信息的结构(查看win32.hlp看详细信息)。

 lpProcessInformation 指向一个空结构,该结构在进程被加载的时候载内存中被填充。结构中包含有进程句柄,线程句柄和进程/线程ID

 注意:这里建议采用进程句柄而不是线程句柄,因为如果采用进程句柄,你对整个进程体拥有PROCESS_ALL_ACCESS的操作权限。就是说你对整个进程拥有读写权限,但是如果采用线程ID,就需要再设置写权限。

   

   好了,现在目标程序被加载了。我们可以利用下面的API函数来运行或者停止进程:

 

DWORD ResumeThread(

    HANDLE hThread // identifies thread to restart

   );  恢复进程

 

 

DWORD SuspendThread(

    HANDLE hThread // handle to the thread

   );  挂起进程

   hThread 可以从LPPROCESS_INFORMATION 结构获得。

 

   最后,可以利用下面的函数读写进程:

BOOL WriteProcessMemory(

    HANDLE hProcess,    // 需要修改的进程的句柄

    LPVOID lpBaseAddress,   // 开始写入的地址

    LPVOID lpBuffer,    // 指向被写入的数据

    DWORD nSize,    // 写入字节数目

    LPDWORD lpNumberOfBytesWritten // 返回写入的数据长度

   );

   这是一个典型的信息自我返回(self-explanatory)。hProcess可以从LPPROCESS_INFORMATION结构获取。

   从进程读取数据:

BOOL ReadProcessMemory(

    HANDLE hProcess,    // handle of the process whose memory is read

    LPCVOID lpBaseAddress,  // address to start reading

    LPVOID lpBuffer,    // address of buffer to place read data

    DWORD nSize,    // number of bytes to read

    LPDWORD lpNumberOfBytesRead     // address of number of bytes read

   );

 

   看明白上面的信息,就可以看下面的内容了。

 

 

3Loader举例

   下面的事例我将启动一个Crackme(译者:我也没有)并且把窗口标题改成“Detten's Caption”。我将把进程启动5秒钟,然后挂起之。

   这里我们修补的字符串,当然用这种方法也可以修补字节或者字:)。作为事情准备,我们需要指导字符串在进程中的地址。所以,开启你喜欢的反汇编软件,找到地址为:004050FCh

 

<-------------Code Snippet----------------->

.386

.model flat,stdcall

option casemap:none

 

include /masm32/include/windows.inc

include /masm32/include/user32.inc

include /masm32/include/kernel32.inc

includelib /masm32/lib/user32.lib

includelib /masm32/lib/kernel32.lib

 

.data

FileName db "C:/somedir/crackme.exe",0

notloaded db "It did not work :-(",0

Letsgo db "The process is started",13,10,

      "Let's change smthg and run it now :-)",0

NewText db "Dettens Caption",0

 

Startup STARTUPINFO <>

processinfo PROCESS_INFORMATION <>

 

.data?

hInstance HINSTANCE ?

byteswritten dd ?

uExitCode dd ?

 

.code

start:

 

    invoke GetModuleHandleA, NULL

    mov    hInstance,eax

    ;创建新进程,载入crackme,并且迅速挂起该线程

    invoke CreateProcess, ADDR FileName, NULL, NULL, NULL, NULL, CREATE_SUSPENDED,

                  NULL, NULL, ADDR Startup, ADDR processinfo

    .IF eax == NULL ; 进程创建失败?

        invoke MessageBox, NULL, ADDR notloaded, NULL, MB_ICONEXCLAMATION

    .ELSE

            invoke MessageBox, NULL, ADDR Letsgo, NULL, MB_OK ; Display Message

        ;修改字符串(004050FCh

                invoke WriteProcessMemory, processinfo.hProcess, 004050FCh, ADDR NewText,

                               13, byteswritten

            ; 恢复进程 ;)

            invoke ResumeThread, processinfo.hThread

            ;让进程运行5秒,然后杀掉它

            invoke Sleep, 5000

            invoke TerminateProcess, processinfo.hProcess, uExitCode

    .ENDIF

    invoke ExitProcess,eax

end start

    <-------------End Code Snippet------------->

 

   这就是写Loader的整个步骤。

   再下一篇中我将讨论更多的Loader技术。比如读取和修改进程环境(所有的寄存器和标记)

 

 

 

 

 

 

What is a loader, and why do we need it? 

A loader is a little standalone program that is used to load another program. Of course we will only use a loader if we want to change something in the program after it is loaded in the memory. (patching in memory)

A well known example of a loader is a trainer used to cheat in games.

The reasons why we choose for a loader instead of a regular patch can be various. We might only want to change something after the CRC is done, or we might want to change something and later in the program restore the original bytes,...

I'm sure you can find some use for it :)

 

 

How does a loader work? 

Ok, grab your win32.hlp and fasten your seatbelt :)

First of all, the loader has to create a new process and start the target. We will use the CreateProcess API for that (pretty obvious ;) When the target is loaded in memory we want to pause the process, so we can change the things we want.

Let's check out what win32.hlp can tell us about this API :

 

BOOL CreateProcess(

 

    LPCTSTR lpApplicationName,      // pointer to name of executable module

    LPTSTR lpCommandLine,       // pointer to command line string

    LPSECURITY_ATTRIBUTES lpProcessAttributes,  // pointer to process security attributes

    LPSECURITY_ATTRIBUTES lpThreadAttributes,   // pointer to thread security attributes

    BOOL bInheritHandles,       // handle inheritance flag

    DWORD dwCreationFlags,      // creation flags

    LPVOID lpEnvironment,       // pointer to new environment block

    LPCTSTR lpCurrentDirectory,     // pointer to current directory name

    LPSTARTUPINFO lpStartupInfo,    // pointer to STARTUPINFO

    LPPROCESS_INFORMATION lpProcessInformation // pointer to PROCESS_INFORMATION

   );

 

Check all the API's I mention here in your win32.hlp document, because I will only discuss the things that are important for our loader.

 

lpApplicationName is the path + name of our target program. (eg c:/somedir/crackme.exe)

lpCommandLine can be used if you want to add some commandline parameters to the target.

 

dwCreationFlags is important for us, because we want to pause the process as soon as it is loaded.

To accomplish that, we use CREATE_SUSPENDED here.

 

lpStartupInfo points to a struct with startup information (again check win32.hlp for more info)

 

lpProcessInformation points to an empty struct that will be filled when the target is loaded in memory.

This struct contains the process handle, thread handle and process/thread ID.

NOTE : The advantage of using the process handle instead of the thread handle, is that when using the process handle you have PROCESS_ALL_ACCESS access to the process object. Meaning that you have read/write access for the entire process. When using the thread handle, you will need to enable write access manualy.

 

Ok, now that the target is loaded, we can easily let the thread run/pause with the following API's :

 

DWORD ResumeThread(

 

    HANDLE hThread // identifies thread to restart

   );

 

to let it run, and

DWORD SuspendThread(

 

    HANDLE hThread // handle to the thread

   );

 

to pause it again.

The hThread handle can be found in the LPPROCESS_INFORMATION struct.

 

Finally we can read and write from/to the process using these API's :

BOOL WriteProcessMemory(

 

    HANDLE hProcess,    // handle to process whose memory is written to

    LPVOID lpBaseAddress,   // address to start writing to

    LPVOID lpBuffer,    // pointer to buffer to write data to

    DWORD nSize,    // number of bytes to write

    LPDWORD lpNumberOfBytesWritten // actual number of bytes written

   );

 

This is pretty self-explanatory. The hProcess handle is the one from the LPPROCESS_INFORMATION struct.

To read from the process :

 

BOOL ReadProcessMemory(

 

    HANDLE hProcess,    // handle of the process whose memory is read

    LPCVOID lpBaseAddress,  // address to start reading

    LPVOID lpBuffer,    // address of buffer to place read data

    DWORD nSize,    // number of bytes to read

    LPDWORD lpNumberOfBytesRead     // address of number of bytes read

   );

 

 

 

This information should be enough to understand the following example.

 

 

A loader example 

In the example below I will open a crackme (in target.zip) and change the caption text to "Detten's Caption".

I will show the process for 5 seconds, and then terminate the process.

So here we patch a simple string, but with the same method we can also patch code bytes or dwords of course ;) As preparation for the loader we need to know the address where the caption string is saved in the target. So fire up your favorite disassembler, and find this address : 004050FCh

 

 

    <-------------Code Snippet----------------->

.386

.model flat,stdcall

option casemap:none

 

include /masm32/include/windows.inc

include /masm32/include/user32.inc

include /masm32/include/kernel32.inc

includelib /masm32/lib/user32.lib

includelib /masm32/lib/kernel32.lib

 

 

.data

FileName db "C:/somedir/crackme.exe",0

notloaded db "It did not work :-(",0

Letsgo db "The process is started",13,10,

      "Let's change smthg and run it now :-)",0

NewText db "Dettens Caption",0

 

Startup STARTUPINFO <>

processinfo PROCESS_INFORMATION <>

 

.data?

hInstance HINSTANCE ?

byteswritten dd ?

uExitCode dd ?

 

.code

start:

 

    invoke GetModuleHandleA, NULL

    mov    hInstance,eax

    ; Create a process and load the crackme in it, and

    ; immediatly suspend the thread (pause it)

    invoke CreateProcess, ADDR FileName, NULL, NULL, NULL, NULL, CREATE_SUSPENDED,

                  NULL, NULL, ADDR Startup, ADDR processinfo

    .IF eax == NULL ; Creation of new process failed?

        invoke MessageBox, NULL, ADDR notloaded, NULL, MB_ICONEXCLAMATION

    .ELSE

            invoke MessageBox, NULL, ADDR Letsgo, NULL, MB_OK ; Display Message

            ; I will change the text string in the crackme used in

            ; the captionbar (004050FCh)

                invoke WriteProcessMemory, processinfo.hProcess, 004050FCh, ADDR NewText,

                               13, byteswritten

            ; Let the process run happily ;)

            invoke ResumeThread, processinfo.hThread

            ;Let the process run for 5 sec. and then terminate it

            invoke Sleep, 5000

            invoke TerminateProcess, processinfo.hProcess, uExitCode

    .ENDIF

    invoke ExitProcess,eax

end start

    <-------------End Code Snippet------------->

 

That's all it takes to create a loader ;)

In the next loader tutorial I will discuss some more advanced loader techniques, like reading and changing the process context (all registers and flags).

 

If you have questions, remarks, or suggestions contact me at detten@tiscali.be

 

Detten

原创粉丝点击