topameng's profileQuake3 启示录PhotosBlogListsMore Tools Help

Blog


    May 21

    检测调试器是否存在

    BOOL IsDebuggerPresent()
    {
        _asm
        {
            mov        eax, dword ptr fs:[0x18]
            mov        eax,[eax+0x30]
            movzx    eax,byte ptr[eax+0x2]
        }
    }

    January 04

    获取cpu时间戳

    在Pentium以上的CPU中,提供了一条机器指令RDTSC(Read Time Stamp Counter)来读取这个时间戳的数字,并将其保存在EDX:EAX寄存器对中。由于EDX:EAX寄存器对恰好是Win32平台下C++语言保存函数返回值的寄存器,所以我们可以把这条指令看成是一个普通的函数调用。vc2003像这样:
    inline unsigned __int64 GetTimeStampCount()
    {
    __asm RDTSC
    }
    对于vc6或者其他编译器可能不行,因为RDTSC不被C++的内嵌汇编器直接支持,所以我们要用_emit伪指令直接嵌入该指令的机器码形式0X0F、0X31,如下:
    inline unsigned __int64 GetTimeStampCount()
    {
    __asm _emit 0x0F
    __asm _emit 0x31
    }

    September 27

    使用远程线程守护进程

      远程线程指把当前进程部分代码注入到其他进程做为线程执行,虽然钩子程序能挂钩其他程序的消息,但钩子程序退出,注入的dll也就退出了,而远程线程不会 随着本地进程退出而结束。而且可以处理更多的事情,而不局限于消息。由于98不支持所以只能在nt内核上运行,下面是制作远程线程需要使用的api。
    获取进程句柄方法之一是使用GetWindowThreadProcessId函数,这个函数可以从一个窗口句柄获得创建窗口进程的id,而获得一个窗口句柄可以用FindWindow轻易得到。
    HWND FindWindow(LPCTSTR lpClassName, LPCTSTR lpWindowName);
    lpClassName, // 窗口类名称,可以指定为NULL,光指定窗口名称即可
    lpWindowName // 窗口名称。
    如果两个参数都为0,则获得最顶层窗口的句柄。
    DWORD GetWindowThreadProcessId(HWND hWnd,LPDWORD lpdwProcessId);
    Hwnd 进程拥有的窗口句柄
    LpdwProcessID 指向用来存放进程ID的变量
    得到进程ID 之后,可以使用 OpenProcess函数来获得进程句柄
    HANDLE OpenProcess(DWORD dwDesiredAccess,BOOL bInheritHandle,DWORD dwProcessId);
    DwDesiredAccess,对打开的进程的访问权限,可以是下列值的组合:
    PROCESS_ALL_ACCESS--------------------等于下面所有权限的组合
    PROCESS_CREATE_THREAD-----------------允许创建远程线程
    PROCESS_DUP_HANDLE--------------------允许进程句柄被复制
    PROCESS_QUERY_INFORMATION-------------允许使用GetExitCodeProcess函数查询和GetProrityClass 查询进程信息
    PROCESS_SET_INFORMATION---------------允许使用SetPriorityClass函数设置进程的优先级
    PROCESS_TERMINATE----------------------允许结束进程
    PROCESS_VM_OPERATION-------------------允许使用WriteProcessMemory函数或者VirtualProtectEx来修改进程的地址空间
    PROCESS_VM_READ------------------------允许对读取进程地址空间
    PROCESS_VM_WRITE-----------------------允许使用写入进程地址空间
    BInheritHandle参数,指定返回的进程句柄是否可以被当前进程的子进程继承
    DwProcessId 参数指定目标进程的进程ID

    还有一种方法是使用CreateToolhelp32Snapshot(快照)函数来获得进程句柄,上面的方法进程必须要有窗口,而快照函数不需要进程拥有窗口,暂不介绍

    下面是读写进程数据的两个api函数:
    BOOL ReadProcessMemory(
    HANDLE hProcess, // 进程句柄
    LPCVOID lpBaseAddress, // 要读取的目标进程起始内存
    LPVOID lpBuffer, // 本地进程用来存放读取内容的数据缓冲区
    SIZE_T nSize, // 要从目标进程读取得数据长度
    SIZE_T * lpNumberOfBytesRead // 要读出的到本地的数量,为NULL则忽略这个参数

    BOOL WriteProcessMemory(
    HANDLE hProcess, // handle to process
    LPVOID lpBaseAddress, // base of memory area
    LPVOID lpBuffer, // data buffer
    SIZE_T nSize, // count of bytes to write
    SIZE_T * lpNumberOfBytesWritten // count of bytes written
    );

    要注入远程线程,必须要在目标进程中开辟一段空间,来存放远程线程代码。
    LPVOID VirtualAllocEx(
    HANDLE hProcess, // 要开辟内存的进程
    LPVOID lpAddress, // 从进程那个地址开始分配,为NULL,则系统决定
    SIZE_T dwSize, // 要分配的空间大小
    DWORD flAllocationType, // 分配的类型,一般用 MEM_COMMIT即可
    DWORD flProtect // 这段内存访问的权限,PAGE_EXECUTE_READWRITE,远程线程所处空间必须可读可执行
    );

    下面是注入远程线程的需要使用的函数:
    HANDLE CreateRemoteThread(
    HANDLE hProcess, // 要写入远程线程进程句柄
    LPSECURITY_ATTRIBUTES lpThreadAttributes, // 线程安全属性
    DWORD dwStackSize, // 初始化堆栈大小
    LPTHREAD_START_ROUTINE lpStartAddress, // 远程线程函数
    LPVOID lpParameter, // 远程线程参数
    DWORD dwCreationFlags, // 标志,可以创建挂起的线程等等
    LPDWORD lpThreadId // 用来返回线程ID的指针
    );
    代码重定位
    有 了这些函数就可以把一段代码插入到目标进程,这段代码将作为目标进程中一个独立的线程运行。但是代码编译时,全局变量、Api函数等等,将被编译为地址形 式,这是地址对于本地进程是可读可执行的,对于目标进程,读取这些地址是非法的,windows这样做,可以保证每个进程都拥有自己独立的4GB空间,而 不互相干扰(处于ring3的进程互相访问是非法的)。对于所有的高级语言,包括C语言,根本不能解决重定位问题,程序只能先写一个dll文件,然后用 CreateRemoteThread 把LoadLibrary 函数注入到目标进程中。LoadLibrary 函数调用dll 文件,执行自己想要的功能,不过这样用一些进程工具可以看到目标进程多了一个dll。重定位是汇编语言的拿手好戏。
    Call @F
    @@:
    pop ebx
    sub ebx,offset @B
    现在 ebx 即得到了代码的实际地址和汇编地址之间的偏差,所以在需要重定位的代码上加上这个偏移值即可。

    远程线程小例子
    ;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
    ;使用远程线程注入到explorer中,避免出现在win2k任务管理器中,并实现看护win2k进程,发现进程退出
    ;马上启动同样的另一个进程
    ;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
    ; 使用 nmake 或下列命令进行编译和链接:
    ; ml /c /coff RemoteThread.asm
    ; rc RemoteThread.rc
    ; Link /subsystem:windows RemoteThread.obj RemoteThread.res
    ;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
    .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
    include macro.inc
    ;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
    .data
    ;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
    KnlOpenProcessStr db 'OpenProcess',0
    KnlWaitForObjectStr db 'WaitForSingleObject',0
    KnlWinExecStr db 'WinExec',0
    KnlGetModuleHandleStr db 'GetModuleHandleA',0
    KnlGetProcAddressStr db 'GetProcAddress',0
    FileName db 'nodead.exe',0
    szDllKernel db 'Kernel32.dll',0
    ;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
    ;下面两个变量是explorer.exe 进程窗口的类名(RegisterClassA参数中设定的)和进程标题名字
    ;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
    szDesktopClass db 'Progman',0
    szDesktopWindow db 'Program Manager',0
    ;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
    .data?
    ;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
    KnlOpenProcess dd ?
    KnlWaitForSingleObject dd ?
    KnlWinExec dd ?
    KnlGetModuleHandle dd ?
    KnlGetProcAddress dd ?
    dwProcessID dd ?
    dwThreadID dd ?
    ;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
    .code
    ;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
    include RemoteCode.asm
    Start:
    invoke GetModuleHandle,addr szDllKernel
    mov ebx,eax
    invoke GetProcAddress,ebx,offset KnlOpenProcessStr
    mov KnlOpenProcess,eax
    invoke GetProcAddress,ebx,offset KnlWaitForObjectStr
    mov KnlWaitForSingleObject,eax
    invoke GetProcAddress,ebx,offset KnlWinExecStr
    mov KnlWinExec,eax
    invoke GetProcAddress,ebx,offset KnlGetProcAddressStr
    mov KnlGetProcAddress,eax
    invoke GetProcAddress,ebx,offset KnlGetModuleHandleStr
    mov KnlGetModuleHandle,eax

    invoke FindWindow,addr szDesktopClass,addr szDesktopWindow
    invoke GetWindowThreadProcessId,eax,offset dwProcessID
    mov dwThreadID,eax
    invoke OpenProcess,PROCESS_ALL_ACCESS,FALSE,dwProcessID
    test eax,eax
    jz OpenProcessError
    mov ebx,eax
    invoke VirtualAllocEx,ebx,NULL,REMOTE_CODE_LENGTH,MEM_COMMIT,PAGE_EXECUTE_READWRITE
    or eax,eax
    jz OpenProcessError
    mov edi,eax
    push eax
    invoke WriteProcessMemory,ebx,edi,offset REMOTE_CODE_START,REMOTE_CODE_LENGTH,NULL
    invoke WriteProcessMemory,ebx,edi,offset KnlOpenProcess,sizeof dword * 5,NULL
    add edi,offset Protect2kProc - offset REMOTE_CODE_START
    invoke CreateRemoteThread,ebx,NULL,0,edi,0,0,NULL
    invoke CloseHandle,ebx
    invoke Sleep,100h
    invoke MessageBoxA,0,offset FileName,offset FileName,0
    OpenProcessError:
    invoke ExitProcess,0
    end Start
    macro.inc
    ;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
    ; 将参数列表的顺序翻转
    ;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
    reverseArgs macro arglist:VARARG
    local txt,count

    txt TEXTEQU <>
    count = 0
    for i,
    count = count + 1
    txt TEXTEQU @CatStr(i,,<%txt>)
    endm
    if count GT 0
    txt SUBSTR txt,1,@SizeStr(%txt)-1
    endif
    exitm txt
    endm
    ;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
    ; 建立一个类似于 invoke 的 Macro
    ;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
    _invoke macro _Proc,args:VARARG
    local count

    count = 0
    % for i,< reverseArgs( args ) >
    count = count + 1
    push i
    endm
    call dword ptr _Proc

    endm

    RemoteThread.asm
    ;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
    ; 注入远程进程执行的代码
    ;equ this [:类型],则变量包含的段和偏移地址都和下一句相同,类型指变量类型
    ;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
    REMOTE_CODE_START equ this byte
    _KnlOpenProcess dd ?
    _KnlWaitForSingleObject dd ?
    _KnlWinExec dd ?
    _KnlGetModuleHandle dd ?
    _KnlGetProcAddress dd ?

    _KnlSleep dd ?
    _Error2 db 'overflow2',0;循环把这个字符串覆盖了?
    _FileName db 'c:\wap32.exe',0; 要看护的进程路径
    _WinName db 'Our First Dialog Box',0;要看护的进程窗口名字
    _hInstance dd ?
    _KnlFindWindow dd ?
    _KnlMessageBox dd ?
    _ErrorMsg db 'overflow',0 ;循环把这个字符串覆盖了?
    ;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
    ; 要从User32 中提取使用的api函数名称
    ;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
    _szDllUser db 'user32.dll',0
    _szDllKernel db 'kernel32.dll',0
    szFindWindow db 'FindWindowA',0
    szMessageBox db 'MessageBoxA',0,0 ;多一个0用于结束循环

    szSleep db 'Sleep',0,0

    Protect2kProc proc uses ebx edi esi
    local hModuleUser
    local hModuleKernel
    call @F
    @@:
    pop ebx
    sub ebx,offset @B
    _invoke [ebx+ _KnlGetModuleHandle],NULL
    test eax,eax
    jz ExitProtectProc
    mov [ebx+ _hInstance],eax
    lea eax,[ebx+ offset _szDllUser]
    _invoke [ebx+_KnlGetModuleHandle],eax
    mov hModuleUser,eax
    lea esi,[ebx+offset szFindWindow]
    lea edi,[ebx+offset _KnlFindWindow]
    ;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
    ; 从User32.dll中取api函数地址的循环
    ;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
    .while TRUE
    _invoke [ebx+_KnlGetProcAddress],hModuleUser,esi
    mov [edi],eax
    add edi,4
    @@:
    lodsb
    or al,al
    jnz @B
    .break .if ! byte ptr [esi+1]
    .endw
    ;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
    ; 从Kernel32.dll中取api函数地址的循环
    ;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
    lea eax,[ebx+ offset _szDllKernel]
    _invoke [ebx+_KnlGetModuleHandle],eax
    mov hModuleKernel,eax
    lea esi,[ebx+offset szSleep]
    lea edi,[ebx+offset _KnlSleep]
    .while TRUE
    _invoke [ebx+_KnlGetProcAddress],hModuleKernel,esi
    mov [edi],eax
    add edi,4
    @@:
    lodsb
    or al,al
    jnz @B
    .break .if ! byte ptr [esi+1]
    .endw
    call _WinMain
    ExitProtectProc:
    ret
    Protect2kProc endp

    _WinMain proc uses ebx esi edi
    call @F
    @@:
    pop ebx
    sub ebx,@B
    lea edi,[ebx+ offset _ErrorMsg]
    ;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
    ; 看护循环,发现进程退出马上重启一个
    ;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
    .while TRUE
    lea esi,[ebx+offset _WinName]
    _invoke [ebx+_KnlFindWindow],NULL,esi
    .if ! eax
    lea esi,[ebx+ offset _FileName]
    _invoke [ebx+_KnlWinExec],esi,SW_SHOWNORMAL
    .endif
    _invoke [ebx+_KnlSleep],110h
    .endw
    ret
    _WinMain endp

    REMOTE_CODE_END equ this byte
    ;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
    ;REMOTE_CODE_LENGTH 获取整个插入代码的长度,在nodead.asm中,VirtualAllocEx 中制定远程线程
    ;长度可以使用此参数
    ;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
    REMOTE_CODE_LENGTH equ offset REMOTE_CODE_END - offset REMOTE_CODE_START