flat assembler
Message board for the users of flat assembler.

Index > Windows > Simple JMP Hook.

Goto page 1, 2  Next
Author
Thread Post new topic Reply to topic
Overflowz



Joined: 03 Sep 2010
Posts: 1046
Overflowz
Hi there, long time after, I'm still going to learn low-level things. I've been learning HLL(job, duh!).
So, I'm trying to understand hooks deeper. I saw detours example but it crashes sometimes. I'm playing with simple JMP hook code, but I fail because I'm applying local variable, which not exists anymore whenever I call the function.
Here's what I mean.
Code:
proc hookit, origFunction, hookFunction
     pushad

        invoke VirtualProtect, [origFunction], 7, PAGE_EXECUTE_READWRITE, oldProtect
        invoke ReadProcessMemory, -1, [origFunction], original_bytes, 7, rpbytes
        invoke WriteProcessMemory, -1, [origFunction], .hook_stub, 7, wpbytes

     popad
     ret
.hook_stub:
     mov eax,[hookFunction] ;this will fail after exiting this procedure.
     jmp eax
endp    

How can I fix this problem? I can use global variable, but if I have to hook many procedures ?
Thanks.
Post 15 Dec 2012, 10:02
View user's profile Send private message Reply with quote
Overflowz



Joined: 03 Sep 2010
Posts: 1046
Overflowz
Anyone?..
Post 15 Dec 2012, 14:04
View user's profile Send private message Reply with quote
l_inc



Joined: 23 Oct 2009
Posts: 881
l_inc
Overflowz
First, you don't have to change page permissions with VirtualProtect. WriteProcessMemory is able to handle that for you.

Second, mov eax,mem32 + jmp eax is kinda overkill in terms of size. A single jmp imm32 is enough to hook. Besides the longer the hooking byte sequence, the larger the probability, you will disturb the processor at the moment it will be trying to execute the original bytes, and the hook attempt will fail.

Third, if you hook a standard WinAPI function, then consider taking advantage of the hotpatch prologue, because most of the WinAPI functions are hotpatchable, which means, that they start with mov edi,edi (in rare case a different 2-bytes instruction), which is a place for a short 2-bytes jmp back for 7 bytes and are preceded by at least 5 bytes of padding, which is a place for a near 5-bytes jmp to your hook function.

And finally...
Quote:
How can I fix this problem?

Easily. Just modify your hooking code before applying it to the hooked function. In your case it would mean to write to the instruction mov eax,imm32 (mov eax,mem32 is a bad idea anyway) first and then write the result to the beginning of the function. But again, it's better to use the previously mentioned short and/or near jmp's.
Post 15 Dec 2012, 14:17
View user's profile Send private message Reply with quote
Overflowz



Joined: 03 Sep 2010
Posts: 1046
Overflowz
Hi, I tried something like this:
Code:
mov eax,[hookFunction]
mov [hook_stub+1],eax ;$E9 + jmp location
hook_stub:
db $E9 ;JMP
db 0xFFFFFFFF ;imm32    

but it jumps to wrong location. How to calculate location ? Neutral
--
But wait. now I it like this:
Code:
1) MessageBoxA called, JMP to my proc.
2) change text and caption, restore original bytes, jmp there. (unhook?)    

this will require code to be hooked again. What I'm missing?
Post 15 Dec 2012, 15:00
View user's profile Send private message Reply with quote
baldr



Joined: 19 Mar 2008
Posts: 1651
baldr
Overflowz,

0xE9 is opcode for jmp rel32, not imm32 (i.e. it's relative jump, not direct). You must calculate displacement beforehand (simply subtract address just after jmp from target address, and you've got it).

To re-hook function you may patch return address in stack.

Completely different approach involves debug API and breakpoints.
Post 15 Dec 2012, 15:26
View user's profile Send private message Reply with quote
Overflowz



Joined: 03 Sep 2010
Posts: 1046
Overflowz
I honestly don't get it.
Code:
        mov eax,[hookFunction]
        sub eax,hook_stub+5    
Post 15 Dec 2012, 15:41
View user's profile Send private message Reply with quote
Overflowz



Joined: 03 Sep 2010
Posts: 1046
Overflowz
Bump.. I'm trying to write static library that will redirect to hooked procedure, modify things, then return modified arguments to API. I'm just having headaches after moving from HLL to LLL. Neutral
Post 15 Dec 2012, 16:36
View user's profile Send private message Reply with quote
l_inc



Joined: 23 Oct 2009
Posts: 881
l_inc
baldr
Quote:
0xE9 is opcode for jmp rel32, not imm32 (i.e. it's relative jump, not direct).

Actually, imm32 wouldn't mean, it's a direct jump. imm32 means only, that it's an immediate value (encoded directly in the instruction) and that it's length is 32 bits, which is true. However as long as Intel provides a separate notation for relative values, rel32 would surely be more correct.
Overflowz
Jump source is not your "hook_stub". Jump source is the the beginning of the hooked function (+ 5 bytes). Thus destination-source would look like this:
Code:
mov eax,[hookFunction]
sub eax,[origFunction]
sub eax,5 ;size of the jmp rel32 instruction
mov [hook_stub+1],eax ;$E9 + jmp location    

Quote:
this will require code to be hooked again. What I'm missing?

Never unhook a function, until you don't need the hook anymore. If you don't wanna make use of the hotpatchability, then in general case you'd need more or less advanced code mutation engines, able to move the overwritten code from the original function, so that it remains functional at another location, where your hook function would execute it directly. The hotpatchable prologue (as described before) makes the things much easier.
Post 15 Dec 2012, 16:55
View user's profile Send private message Reply with quote
Overflowz



Joined: 03 Sep 2010
Posts: 1046
Overflowz
Oh, my bad, I guess it now.
Will continue writing..
Here's what I'm trying to do.
Code:
1) hook function (MessageBoxA), first 5 bytes would be JMP XXXXXX.
2) Whenever I call MessageBoxA, it will jump to hooked function, like his:
proc hMessageBoxA, HWND, szText, szCap, UINT
      mov [szText],"Hello"
      mov [szCap],"World!"
ret
endp

3) after RET, it will return to the original MessageBoxA and execute it.    


I have to work very hard I guess Very Happy
Post 15 Dec 2012, 17:23
View user's profile Send private message Reply with quote
typedef



Joined: 25 Jul 2010
Posts: 2913
Location: 0x77760000
typedef
Here. Study this.

Code:
;
; typedef @fasm boards
;
format pe gui 4.0

include 'win32ax.inc'

entry WinMain

section '.text' code readable executable

;-----------------------------------------------------------------------------------
; Returns absolute original function pointer.
;
; FARPROC WINAPI HookFunc(LPSTR szLibrary, LPSTR szFunctionName, LPVOID newHook);
;-----------------------------------------------------------------------------------
proc HookFunc  szLibrary, szFunctionName, newHook

               push    ebx
               push    esi
               push    edi

               local oldProtect:DWORD

               push    dword [szLibrary]
               call    [GetModuleHandleA]

               push    dword [szFunctionName]
               push    eax
               call    [GetProcAddress]
               
               mov     ebx, eax
               mov     esi, ebx
               add     esi, -5 ; calculate address

               lea     eax, dword[oldProtect]
               push    eax
               push    0x40             ; PAGE_EXECUTE_READWRITE
               push    7
               push    esi
               call    [VirtualProtect]
               
               cmp     eax, 0
               jne     no_error

               jmp     finish
             ; begin patch. This is simplified so you understand and not memorize.
no_error:
                cld
                mov    al,     0xE9
                
                mov    edi,    esi
                stosb                                   ; Install Jump opcode at first 'NOP' op-code

                mov    eax,    dword [newHook]
                sub    eax,    ebx
                stosd                                   ; Install address. DWORD

                mov    edi,    ebx                      ; at LEA EDI, EDI
                mov    ax,     word [jmp_code]
                stosw                                   ; Install jump code + address(WORD) at entry point(LEA EDI,EDI)

                lea    eax, [ebx+2]                     ; Absolute function pointer (starts at PUSH EBP skipping -
                jmp    finish                           ;   the relative two byte backward jump; JMP SHORT XX)
jmp_code:
                jmp $ - 5
finish:
                pop    edi
                pop    esi
                pop    ebx
                mov    esp, ebp
                pop    ebp
                ret 4 * 3
endp
; to unhook just put NOPs on it and remember to set PAGE protection back to its normal state.

;
; TEST
;
proc msgBox, hwnd, msg, title, flags

        ; push them again or use JMP(not good IMO)
        push   dword [flags]
        push   dword [title]
        push   myText
        push   dword [hwnd]
        call   dword [absMsgBoxPtr]
        leave
        ret    4 * 4        ; remove original arguments
endp

WinMain:
        stdcall   HookFunc, szLib, szProc, msgBox
        mov       [absMsgBoxPtr], eax
        invoke    MessageBoxA,0,'Hello world','Here I am',MB_OK
        invoke    ExitProcess,0x0


section '.data' data readable writeable

absMsgBoxPtr dd 0
myText       db 'Hello Again Nuggah!',0
szProc       db 'MessageBoxA',0
szLib        db 'user32.dll',0

section '.idata' import data readable

library user32,'user32.dll',\
        kernel32,'kernel32.dll'

include 'api/kernel32.inc'
include 'api/user32.inc'
    
Post 15 Dec 2012, 18:26
View user's profile Send private message Reply with quote
Overflowz



Joined: 03 Sep 2010
Posts: 1046
Overflowz
Thanks for sample, will study it right now!
Post 15 Dec 2012, 18:31
View user's profile Send private message Reply with quote
typedef



Joined: 25 Jul 2010
Posts: 2913
Location: 0x77760000
typedef
No problemo. I also suggest you step through it with Olly so you know what's actually going on. Wink
Post 15 Dec 2012, 18:33
View user's profile Send private message Reply with quote
Overflowz



Joined: 03 Sep 2010
Posts: 1046
Overflowz
I understood what's going on there. it's hooking MOV EDI,EDI to jmp -5 relative, where are 5 nops patched with JMP to HookedFunction and returns original function w/o MOV EDI,EDI, just another next bytes. Is all WinAPI like that ? I'm going to try more advanced one, which will save bytes, restore, etc.. Very Happy
Post 15 Dec 2012, 19:06
View user's profile Send private message Reply with quote
typedef



Joined: 25 Jul 2010
Posts: 2913
Location: 0x77760000
typedef
Yes. Most if not all WINAPIs are like that.
Post 15 Dec 2012, 19:11
View user's profile Send private message Reply with quote
Overflowz



Joined: 03 Sep 2010
Posts: 1046
Overflowz
Alright, got it now how this should work, will work with it now Wink Thank you guys.
Post 15 Dec 2012, 19:17
View user's profile Send private message Reply with quote
l_inc



Joined: 23 Oct 2009
Posts: 881
l_inc
Overflowz
Quote:
I'm going to try more advanced one, which will save bytes, restore, etc.

As I already said, to restore is not more advanced. It's more faulty. Especially, in multithreaded environment. If you wanna do the patch more advanced, then try to execute the original instructions from where you backed up them and then transfer control to the continuation of the original function.
Quote:
Is all WinAPI like that ?

As I already said, most of the WinAPI functions are hotpatchable. But not all of them. You should always check that and in the simplest case refrain from patching an inappropriate function
Post 15 Dec 2012, 20:20
View user's profile Send private message Reply with quote
Overflowz



Joined: 03 Sep 2010
Posts: 1046
Overflowz
l_inc
Thanks in advice, but where can I save original bytes for each function and access them for each procedure? I know most of them will be same(MOV EDI,EDI) but if there's different one? Thanks.
Post 15 Dec 2012, 21:06
View user's profile Send private message Reply with quote
typedef



Joined: 25 Jul 2010
Posts: 2913
Location: 0x77760000
typedef
Overflowz wrote:
l_inc
Thanks in advice, but where can I save original bytes for each function and access them for each procedure? I know most of them will be same(MOV EDI,EDI) but if there's different one? Thanks.


You probably don't want to do that because then you'll have to allocate memory for each function you hook. Maybe you could make a function that finds a code cave in the process space then use linked lists.

But all of this for what? It's too overkill IMO.

Now, for those APIs that are not patchable like this, you can still overwrite the first 5 bytes with a JMP to a hook address that contains the hardcoded patched bytes. That is to say if you're that desperate. In which case you;d have to set up pointers/flags again. Otherwise you may want to use IAT hooks at this point.
Post 15 Dec 2012, 21:25
View user's profile Send private message Reply with quote
Overflowz



Joined: 03 Sep 2010
Posts: 1046
Overflowz
I guess you're right. Thanks for tips Wink
Post 16 Dec 2012, 11:39
View user's profile Send private message Reply with quote
yoshimitsu



Joined: 07 Jul 2011
Posts: 96
yoshimitsu
l_inc wrote:
First, you don't have to change page permissions with VirtualProtect. WriteProcessMemory is able to handle that for you.
msdn wrote:
lpBaseAddress [in]
A pointer to the base address in the specified process to which data is written. Before data transfer occurs, the system verifies that all data in the base address and memory of the specified size is accessible for write access, and if it is not accessible, the function fails.


typedef wrote:
Yes. Most if not all WINAPIs are like that.

Functions from kernel32.dll (win7 x86 dll), which do not support hotpatching:
Quote:
AreFileApisANSI
BaseDumpAppcompatCache
BaseFlushAppcompatCache
BaseInitAppcompatCacheSupport
CancelDeviceWakeupRequest
CheckForReadOnlyResource
CloseConsoleHandle
CloseProfileUserMapping
CmdBatNotification
CommConfigDialogA
CommConfigDialogW
ConsoleMenuControl
ConvertFiberToThread
CopyFileExW
CopyFileTransactedA
CopyFileTransactedW
CreateConsoleScreenBuffer
CreateDirectoryTransactedW
CreateFileTransactedW
CreateHardLinkTransactedW
CreateHardLinkW
CreateProcessInternalA
CreateProcessInternalW
CreateSocketHandle
CreateSymbolicLinkTransactedW
CreateThreadpoolCleanupGroup
CreateTimerQueue
CreateToolhelp32Snapshot
CtrlRoutine
DebugBreak
DeleteFiber
DeleteFileTransactedW
DosPathToSessionPathA
DosPathToSessionPathW
DuplicateConsoleHandle
EnumSystemFirmwareTables
ExitProcess
FindFirstFileNameTransactedW
FindFirstFileNameW
FindFirstFileTransactedA
FindFirstFileTransactedW
FindFirstStreamTransactedW
FindFirstStreamW
FindFirstVolumeA
FindFirstVolumeMountPointA
FindNextVolumeA
FindNextVolumeMountPointA
FindResourceExA
FlushConsoleInputBuffer
GenerateConsoleCtrlEvent
GetACP
GetBinaryTypeW
GetCalendarInfoEx
GetCalendarInfoW
GetCommandLineA
GetCommandLineW
GetCompressedFileSizeTransactedW
GetConsoleAliasExesLengthA
GetConsoleAliasExesLengthW
GetConsoleCP
GetConsoleCharType
GetConsoleCursorInfo
GetConsoleCursorMode
GetConsoleDisplayMode
GetConsoleFontInfo
GetConsoleFontSize
GetConsoleHardwareState
GetConsoleHistoryInfo
GetConsoleInputExeNameW
GetConsoleInputWaitHandle
GetConsoleMode
GetConsoleNlsMode
GetConsoleOutputCP
GetConsoleProcessList
GetConsoleScreenBufferInfo
GetConsoleScreenBufferInfoEx
GetConsoleSelectionInfo
GetConsoleWindow
GetCurrentConsoleFont
GetCurrentConsoleFontEx
GetCurrentProcess
GetCurrentProcessId
GetCurrentThread
GetCurrentThreadId
GetDefaultCommConfigA
GetDefaultCommConfigW
GetEnabledXStateFeatures
GetEnvironmentStrings
GetEnvironmentStringsA
GetEnvironmentStringsW
GetErrorMode
GetFileAttributesTransactedW
GetFullPathNameTransactedA
GetFullPathNameTransactedW
GetHandleContext
GetLargePageMinimum
GetLargestConsoleWindowSize
GetLastError
GetLogicalDrives
GetLongPathNameTransactedA
GetLongPathNameTransactedW
GetNamedPipeClientComputerNameA
GetNextVDMCommand
GetNumberOfConsoleFonts
GetNumberOfConsoleInputEvents
GetNumberOfConsoleMouseButtons
GetOEMCP
GetProcessHeap
GetShortPathNameA
GetSystemDefaultLCID
GetSystemDefaultLangID
GetSystemDefaultUILanguage
GetSystemFirmwareTable
GetThreadErrorMode
GetThreadLocale
GetThreadUILanguage
GetTickCount
GetTickCount64
GetUserDefaultLCID
GetUserDefaultLangID
GetUserDefaultUILanguage
GetVDMCurrentDirectories
GetVersion
GetVolumeInformationA
GetVolumeNameForVolumeMountPointA
GetVolumePathNameA
GetVolumePathNamesForVolumeNameA
GlobalCompact
GlobalFlags
GlobalHandle
GlobalLock
GlobalReAlloc
GlobalSize
GlobalUnlock
InvalidateConsoleDIBits
IsBadReadPtr
IsBadStringPtrA
IsBadStringPtrW
IsBadWritePtr
IsDebuggerPresent
IsSystemResumeAutomatic
IsThreadAFiber
IsTimeZoneRedirectionEnabled
K32EnumDeviceDrivers
K32EnumProcessModulesEx
K32EnumProcesses
K32GetModuleInformation
LZDone
LZStart
LoadModule
LocalCompact
LocalFlags
LocalHandle
LocalShrink
LocalSize
MoveFileTransactedA
MoveFileTransactedW
MulDiv
NlsCheckPolicy
NlsGetCacheUpdateCount
NlsUpdateLocale
NlsUpdateSystemLocale
OpenConsoleW
OpenFile
OpenProfileUserMapping
PrivCopyFileExW
PrivMoveFileIdentityW
ReadFile
RegCloseKey
RegDeleteValueA
RegDeleteValueW
RegDisablePredefinedCacheEx
RegEnumKeyExA
RegEnumKeyExW
RegEnumValueA
RegEnumValueW
RegFlushKey
RegGetKeySecurity
RegKrnGetGlobalState
RegLoadKeyA
RegLoadKeyW
RegLoadMUIStringA
RegQueryInfoKeyA
RegQueryInfoKeyW
RegQueryValueExA
RegQueryValueExW
RegRestoreKeyA
RegRestoreKeyW
RegSaveKeyExA
RegSaveKeyExW
RegSetKeySecurity
RegSetValueExA
RegSetValueExW
RegUnLoadKeyA
RegUnLoadKeyW
RegisterConsoleIME
RegisterConsoleOS2
RegisterConsoleVDM
RemoveDirectoryTransactedW
ReplaceFile
ReplaceFileW
RequestDeviceWakeup
RequestWakeupLatency
SetConsoleActiveScreenBuffer
SetConsoleCP
SetConsoleCursor
SetConsoleCursorInfo
SetConsoleCursorMode
SetConsoleCursorPosition
SetConsoleDisplayMode
SetConsoleFont
SetConsoleHardwareState
SetConsoleHistoryInfo
SetConsoleIcon
SetConsoleInputExeNameW
SetConsoleKeyShortcuts
SetConsoleLocalEUDC
SetConsoleMaximumWindowSize
SetConsoleMenuClose
SetConsoleMode
SetConsoleNlsMode
SetConsoleOS2OemFormat
SetConsoleScreenBufferInfoEx
SetConsoleScreenBufferSize
SetConsoleTextAttribute
SetConsoleTitleA
SetConsoleTitleW
SetConsoleWindowInfo
SetCurrentConsoleFontEx
SetDefaultCommConfigA
SetDefaultCommConfigW
SetFileApisToANSI
SetFileApisToOEM
SetFileAttributesTransactedW
SetHandleContext
SetLastConsoleEventActive
SetLastError
SetMessageWaitingIndicator
SetVDMCurrentDirectories
SetVolumeLabelW
ShowConsoleCursor
SignalObjectAndWait
SwitchToFiber
SwitchToThread
TermsrvAppInstallMode
TlsAlloc
UTUnRegister
UnhandledExceptionFilter
UnregisterApplicationRecoveryCallback
UnregisterApplicationRestart
UnregisterConsoleIME
UpdateResourceA
VerifyConsoleIoHandle
WTSGetActiveConsoleSessionId
WerpCleanupMessageMapping
WerpInitiateRemoteRecovery
Wow64GetThreadSelectorEntry
lstrcat
lstrcatA
lstrcatW
lstrcpy
lstrcpyA
lstrcpyW


The list for user32.dll is even longer..

Instead of checking for the availability of hotpatching (the 5x nops could also be 5x int3, and if l_inc is correct, the mov edi,edi might differ as well, making it kinda complicated to check whether hotpatching is possible) you should probably just write a universal hook function which uses a length disassembler engine to securely save the instructions which need to get overwritten by the hook-jmp and save them to a different location and append a jmp back to the hooked function but behind the hook-jmp, creating a gate to call the original function without invoking the hook.
For cleaning up just move the instructions back to their original place and free the location, where you saved them previously, afterwards if it was allocated.
Post 17 Dec 2012, 00:56
View user's profile Send private message Reply with quote
Display posts from previous:
Post new topic Reply to topic

Jump to:  
Goto page 1, 2  Next

< Last Thread | Next Thread >
Forum Rules:
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum
You cannot attach files in this forum
You can download files in this forum


Copyright © 1999-2020, Tomasz Grysztar. Also on GitHub, YouTube, Twitter.

Website powered by rwasa.