使用汇编语言编写注入代码

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/SKI_12/article/details/82954890

这里主要借助OllyDbg的汇编功能,使用汇编语言编写注入代码即ThreadProc()函数。与上一篇文章的CodeInject.cpp代码类似,但区别在于THREAD_PARAM结构体不包含字符串成员,且使用指令字节数组替代了ThreadProc()函数(因为代码本身同时包含所需的字符串数据)。

汇编语言常用的开发工具(Assembler)有MASM、TASM和FASM等。

编写简单的待修改程序

编写一个并无啥功能的asmtest.exe程序以用于通过OllyDbg使用汇编编写注入代码。

asmtest.cpp

#include "stdio.h"

int test(){
    return 0;
}

int main(int argc, char* argv[]){
    return test();
}

编译生成asmtest.exe。

使用OllyDbg的Assemble功能修改汇编代码

使用OllyDbg打开asmtest.exe,划到代码区域的顶端位置,右键New origin here,将EIP更改为改地址401000:

注意,New origin here命令仅用来修改EIP寄存器值,与直接通过调用方式转到指定地址是不一样的,因为寄存器与栈中内容并未改变。

空格键即可弹出输入汇编命令的窗口,将其中的复选“Fill with NOP's”取消掉(若处于复选状态,输入代码长度短于已有代码时,剩余长度会填充为NOP指令,以整体对齐代码):

接下来使用汇编编写ThreadProc()函数。与C语言编写的不同在于,需要的Data(字符串)已包含在Code中。

自EP处输入代码至40102E地址的call指令处(后续再说明为啥call地址为40103E):

简单地说一下,就是先建立栈帧,401001地址处的MOV指令将ESI指向LoadLibraryA(),接下来3条PUSH指令为“User32.dll”的ASCII码(这种将字符串压入栈的方法仅适用于汇编编写的程序),接着PUSH ESP将上述字符串压入栈中,再CALL指令调用LoadLibraryA(‘User32.dll’);接着3条PUSH为“MessageBoxA”,压入栈后PUSH EAX将use32.dll地址压入栈,再CALL指令调用GetProcAddress();PUSH 0指令压入MessageBoxA()第四个参数uType值0,最后的CALL指令相当于PUSH+JMP指令,即将下列的字符串压入栈后再跳转到下一个位置执行。

接着在紧跟着的地址401033中输入字符串,Ctrl+E打开Edit窗口,在ASCII栏输入字符串“SKI12”,再到HEX栏最后输入00即NULL结束字符串,其为MessageBoxA()第三个参数lpCaption:

接着在40103E地址处填写call指令,可以看到之前的call指向现在的call指令,中间保存的是输入的‘SKI12’字符串,之所以显示奇怪的命令在于OllyDbg将字符串误认为IA-32指令,是由于输入者在Code位置输入字符串引起的:

接着在该call指令下面打开Edit窗口输入字符串‘Assemble Code Inject By SKI12’再输入NULL如紫框所示,其为MessageBoxA()第二个参数lpText,可以看到指令输入到了401060地址处,修改该处Hex,前面的00为之前字符串的NULL、后面修改为6A00即PUSH 0指令(压入MessageBoxA()第一个参数hWord)如绿框所示,接着通过Edit框输入Hex值的方式分别往下输入命令,call eax指令调用MessageBoxA(),XOR指令将线程函数的返回值设置为0,最后删除栈帧再return。完整的汇编代码如下图:

保存文件,右键》Copy to executable》All modifications》Copy all》Save file:

打开新保存的文件asmtest_ski12.exe,在Dump窗口中选中之前写入的代码部分,即从401000至40106A地址处的内容:

右键选中的内容》Copy》To file,保存为本地文件ski12.txt:

将该文件内容去掉多余的部分,保存中间的ASCII字节部分,在每个字节前面加上0x前缀,各个字节之间以逗号分隔,这就是用汇编语言编写的代码注入弹框payload:

编写代码注入程序

CodeInject_Assemble.cpp

// CodeInject_Assemble.cpp

#include "windows.h"
#include "stdio.h"

//不像之前那样包含字符串成员,因为注入代码中包含所需字符串数据
typedef struct _THREAD_PARAM {
    FARPROC pFunc[2];               // LoadLibraryA(), GetProcAddress()
} THREAD_PARAM, *PTHREAD_PARAM;

//汇编语言编写的弹框payload
BYTE g_InjectionCode[] = {
        0x55, 0x8B, 0xEC, 0x8B, 0x75, 0x08, 0x68, 0x6C, 0x6C, 0x00, 0x00, 0x68, 0x33, 0x32, 0x2E, 0x64, 
	0x68, 0x75, 0x73, 0x65, 0x72, 0x54, 0xFF, 0x16, 0x68, 0x6F, 0x78, 0x41, 0x00, 0x68, 0x61, 0x67, 
	0x65, 0x42, 0x68, 0x4D, 0x65, 0x73, 0x73, 0x54, 0x50, 0xFF, 0x56, 0x04, 0x6A, 0x00, 0xE8, 0x0B, 
	0x00, 0x00, 0x00, 0x53, 0x4B, 0x49, 0x31, 0x32, 0x00, 0x90, 0x90, 0x90, 0x90, 0x90, 0xE8, 0x1E, 
	0x00, 0x00, 0x00, 0x41, 0x73, 0x73, 0x65, 0x6D, 0x62, 0x6C, 0x65, 0x20, 0x43, 0x6F, 0x64, 0x65, 
	0x20, 0x49, 0x6E, 0x6A, 0x65, 0x63, 0x74, 0x20, 0x42, 0x79, 0x20, 0x53, 0x4B, 0x49, 0x31, 0x32, 
	0x00, 0x6A, 0x00, 0xFF, 0xD0, 0x33, 0xC0, 0x8B, 0xE5, 0x5D, 0xC3
};

BOOL SetPrivilege(LPCTSTR lpszPrivilege, BOOL bEnablePrivilege) {
    TOKEN_PRIVILEGES tp;
    HANDLE hToken;
    LUID luid;

    if( !OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken) ){
        printf("OpenProcessToken error: %u\n", GetLastError());
        return FALSE;
    }

    if( !LookupPrivilegeValue(NULL,           // lookup privilege on local system
                              lpszPrivilege,  // privilege to lookup 
                              &luid) )        // receives LUID of privilege
    {
        printf("LookupPrivilegeValue error: %u\n", GetLastError() ); 
        return FALSE; 
    }

    tp.PrivilegeCount = 1;
    tp.Privileges[0].Luid = luid;
    if( bEnablePrivilege )
        tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
    else
        tp.Privileges[0].Attributes = 0;

    // Enable the privilege or disable all privileges.
    if( !AdjustTokenPrivileges(hToken, 
                               FALSE, 
                               &tp, 
                               sizeof(TOKEN_PRIVILEGES), 
                               (PTOKEN_PRIVILEGES) NULL, 
                               (PDWORD) NULL) )
    {
        printf("AdjustTokenPrivileges error: %u\n", GetLastError() ); 
        return FALSE; 
    } 

    if( GetLastError() == ERROR_NOT_ALL_ASSIGNED ){
        printf("The token does not have the specified privilege. \n");
        return FALSE;
    } 

    return TRUE;
}

BOOL InjectCode(DWORD dwPID){
    HMODULE         hMod            = NULL;
    THREAD_PARAM    param           = {0,};
    HANDLE          hProcess        = NULL;
    HANDLE          hThread         = NULL;
    LPVOID          pRemoteBuf[2]   = {0,};

    hMod = GetModuleHandleA("kernel32.dll");

    // set THREAD_PARAM
    param.pFunc[0] = GetProcAddress(hMod, "LoadLibraryA");
    param.pFunc[1] = GetProcAddress(hMod, "GetProcAddress");

    // Open Process
    if ( !(hProcess = OpenProcess(PROCESS_ALL_ACCESS,               // dwDesiredAccess
                                  FALSE,                            // bInheritHandle
                                  dwPID)) )                         // dwProcessId
    {
        printf("OpenProcess() fail : err_code = %d\n", GetLastError());
        return FALSE;
    }

    // Allocation for THREAD_PARAM
    if( !(pRemoteBuf[0] = VirtualAllocEx(hProcess,                  // hProcess
                                         NULL,                      // lpAddress
                                         sizeof(THREAD_PARAM),      // dwSize
                                         MEM_COMMIT,                // flAllocationType
                                         PAGE_READWRITE)) )         // flProtect
    {
        printf("VirtualAllocEx() fail : err_code = %d\n", GetLastError());
        return FALSE;
    }

    if( !WriteProcessMemory(hProcess,                               // hProcess
                            pRemoteBuf[0],                          // lpBaseAddress
                            (LPVOID)&param,                         // lpBuffer
                            sizeof(THREAD_PARAM),                   // nSize
                            NULL) )                                 // [out] lpNumberOfBytesWritten
    {
        printf("WriteProcessMemory() fail : err_code = %d\n", GetLastError());
        return FALSE;
    }

    // Allocation for ThreadProc()
    if( !(pRemoteBuf[1] = VirtualAllocEx(hProcess,                  // hProcess
                                         NULL,                      // lpAddress
                                         sizeof(g_InjectionCode),   // dwSize
                                         MEM_COMMIT,                // flAllocationType
                                         PAGE_EXECUTE_READWRITE)) ) // flProtect
    {
        printf("VirtualAllocEx() fail : err_code = %d\n", GetLastError());
        return FALSE;
    }
	//g_InjectionCode替换了之前的ThreadProc()
    if( !WriteProcessMemory(hProcess,                               // hProcess
                            pRemoteBuf[1],                          // lpBaseAddress
                            (LPVOID)&g_InjectionCode,               // lpBuffer
                            sizeof(g_InjectionCode),                // nSize
                            NULL) )                                 // [out] lpNumberOfBytesWritten
    {
        printf("WriteProcessMemory() fail : err_code = %d\n", GetLastError());
        return FALSE;
    }

    if( !(hThread = CreateRemoteThread(hProcess,                    // hProcess
                                       NULL,                        // lpThreadAttributes
                                       0,                           // dwStackSize
                                       (LPTHREAD_START_ROUTINE)pRemoteBuf[1],
                                       pRemoteBuf[0],               // lpParameter
                                       0,                           // dwCreationFlags
                                       NULL)) )                     // lpThreadId
    {
        printf("CreateRemoteThread() fail : err_code = %d\n", GetLastError());
        return FALSE;
    }

    WaitForSingleObject(hThread, INFINITE);	

    CloseHandle(hThread);
    CloseHandle(hProcess);

    return TRUE;
}

int main(int argc, char *argv[]){
    DWORD dwPID     = 0;

	if( argc != 2 ){
	    printf("\n USAGE  : %s <pid>\n", argv[0]);
		return 1;
	}

	// change privilege
	if( !SetPrivilege(SE_DEBUG_NAME, TRUE) )
        return 1;

    // code injection
    dwPID = (DWORD)atol(argv[1]);
    InjectCode(dwPID);

	return 0;
}

随意注入的cmd进程:

跟踪调试分析

调试分析,使用OllyDbg打开notepad.exe运行,设置生成新进程时进行断点,查看进程PID并进行代码注入,可看到调试器暂停在线程代码起始点,先是生成栈帧,[EBP+8]是传入函数的第一个参数,这里指THREAD_PARAM结构体指针,执行完该MOV指令后ESI存储的地址是CodeInject_Assemble.exe进程为THREAD_PARAM结构体在notepad.exe进程内存空间中分配的内存缓冲区地址;可以看到该地址存储着2个4字节的值,即LoadLibraryA()和GetProcAddress()的起始地址:

接着将“user32.dll”字符串以ASCII码逆序压入栈中,PUSH ESP中为该字符串的起始地址,最后CALL指令调用LoadLibraryA(‘user32.dll’),函数的返回地址保存在EAX中:

使用ALT+E查看加载到进程内存中的所有DLL:

同样,将“MessageBoxA”字符串压入栈作为GetProcAddress()函数的第二个参数值,接着PUSH EAX指令将上述的user32.dll返回地址压入栈作为GetProcAddress()函数的第一个参数hMod的值,再CALL指令调用GetProcAddress(hMod, ‘MessageBoxA’):

接着是依次压入MessageBoxA()函数的第四个参数uType值0、第三个参数lpCaption值“SKI12”、第二个参数lpText值“Assemble Code Inject By SKI12”和第一个参数hWnd值0,最后调用MessageBoxA():

使用CALL指令将包含在代码间的字符串数据地址压入栈:

仅适用于使用汇编语言编写的程序。简单地说,CALL相当于PUSH+JMP。执行1A002E地址处的CALL指令后,函数(1A003E)终止并将返回地址(1A0033)压入栈中,再JMP到相应的函数地址(1A003E)。这里1A003E实际并不是函数,不具有RETN指令返回的形态。此处的CALL指令只是用来将紧接其后的“SKI12”字符串地址压入栈,然后转到下一条代码指令。

执行完call指令后便弹框显示注入内容。

之后便是设置ThreadProc()函数的返回值为0(使用XOR EAX,EAX指令比使用MOV EAX,0指令更快捷),再删除栈帧及函数返回:

猜你喜欢

转载自blog.csdn.net/SKI_12/article/details/82954890
今日推荐