Skip to content

Commit

Permalink
initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
Nanite Factory committed Oct 6, 2018
0 parents commit 5aae6c7
Show file tree
Hide file tree
Showing 9 changed files with 320 additions and 0 deletions.
19 changes: 19 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
OUT := my
FUNC := Test
VERSION := $(shell git describe --always --long)

all: run

build:
go build -i -v -o ${OUT}.dll -buildmode=c-shared -ldflags "-w -s -X main.version=${VERSION}"

debug:
go build -i -v -o ${OUT}.dll -buildmode=c-shared

run: debug
rundll32.exe ${OUT}.dll ${FUNC}

clean:
-@rm ${OUT}.dll ${OUT}.h #${OUT}-*

.PHONY: build debug run clean
31 changes: 31 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# hookwin10calc

- Runtime module (DLL) that hooks functionality of `Calculator.exe`, with help of [gominhook](https://github.com/NaniteFactory/gominhook).

- Reverse engineering UWP `Calculator.exe` in Windows 10.

- - -

### Build

```Bash
$ make build
```

### Clean-up

```Bash
$ make clean
```

- - -

## Demo

![1](./sampleshots/1.jpg)

![1](./sampleshots/1.PNG)

![2](./sampleshots/2.PNG)

![3](./sampleshots/3.PNG)
4 changes: 4 additions & 0 deletions dllmain.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package main

//#include "dllmain.h"
import "C"
47 changes: 47 additions & 0 deletions dllmain.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
#include <Windows.h>

void OnProcessAttach(HINSTANCE, DWORD, LPVOID);

typedef struct {
HINSTANCE hinstDLL; // handle to DLL module
DWORD fdwReason; // reason for calling function // reserved
LPVOID lpReserved; // reserved
} MyThreadParams;

DWORD WINAPI MyThreadFunction(LPVOID lpParam) {
MyThreadParams params = *((MyThreadParams*)lpParam);
OnProcessAttach(params.hinstDLL, params.fdwReason, params.lpReserved);
free(lpParam);
return 0;
}

BOOL WINAPI DllMain(
HINSTANCE _hinstDLL, // handle to DLL module
DWORD _fdwReason, // reason for calling function
LPVOID _lpReserved) // reserved
{
switch (_fdwReason) {
case DLL_PROCESS_ATTACH:
// Initialize once for each new process.
// Return FALSE to fail DLL load.
{
MyThreadParams* lpThrdParam = (MyThreadParams*)malloc(sizeof(MyThreadParams));
lpThrdParam->hinstDLL = _hinstDLL;
lpThrdParam->fdwReason = _fdwReason;
lpThrdParam->lpReserved = _lpReserved;
HANDLE hThread = CreateThread(NULL, 0, MyThreadFunction, lpThrdParam, 0, NULL);
// CreateThread() because otherwise DllMain() is highly likely to deadlock.
}
break;
case DLL_PROCESS_DETACH:
// Perform any necessary cleanup.
break;
case DLL_THREAD_DETACH:
// Do thread-specific cleanup.
break;
case DLL_THREAD_ATTACH:
// Do thread-specific initialization.
break;
}
return TRUE; // Successful.
}
219 changes: 219 additions & 0 deletions main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,219 @@
package main

import (
"fmt"
"log"
"strings"
"syscall"
"unicode/utf16"
"unsafe"

"github.com/nanitefactory/gominhook"
"github.com/nanitefactory/winmb"
"github.com/zetamatta/go-outputdebug"
)

// ----------------------------------------------------------------------------

/*
#include <string.h>
#include <Windows.h>
// Due to lack of my knowledge in reversing I literally have no idea what the return type of these functions would be though.
// Arguments could be guessed; 64-bit integers because they always pass in R8, RDX, RCX in order.
typedef DWORD64 (*ProtoOnDisplayUpdate)(DWORD64, DWORD64, DWORD64);
DWORD64 OnDisplayUpdate(DWORD64, DWORD64, DWORD64);
typedef DWORD64 (*ProtoOnNumberUpdate)(DWORD64, DWORD64, DWORD64);
DWORD64 OnNumberUpdate(DWORD64, DWORD64, DWORD64);
*/
import "C"

var isInMiddleOfOnDisplayUpdate bool

var fpDisplayUpdate C.ProtoOnDisplayUpdate
var fpNumberUpdate C.ProtoOnNumberUpdate

//export OnDisplayUpdate
func OnDisplayUpdate(arg1, arg2, arg3 uintptr) (ret uintptr) {
isInMiddleOfOnDisplayUpdate = true
ret, _, _ = syscall.Syscall6(uintptr(unsafe.Pointer(fpDisplayUpdate)), 3, arg1, arg2, arg3, 0, 0, 0)
return
}

//export OnNumberUpdate
func OnNumberUpdate(arg1, arg2, arg3 uintptr) (ret uintptr) {
// See if it's hooked well. // arg3 points to the stack where our string is stored.
outputdebug.String("OnNumberUpdate(): " + fmt.Sprintf("Arguments passed: 0x%X 0x%X 0x%X", arg1, arg2, arg3))

// If this OnNumberUpdate() is not called from OnDisplayUpdate(), don't do nothing and fallthrough.
if !isInMiddleOfOnDisplayUpdate {
ret, _, _ = syscall.Syscall6(uintptr(unsafe.Pointer(fpNumberUpdate)), 3, arg1, arg2, arg3, 0, 0, 0)
return
}
isInMiddleOfOnDisplayUpdate = false

// Convert our UTF16 string (WSTR) to which our arg3 points to a plain Go string.
strArg3 := lpwstrToString((C.LPCWSTR)(unsafe.Pointer(arg3)))
outputdebug.String("original text: " + strArg3)

// Make some changes to this copied string.
strArg3 = strings.Replace(strArg3, "0", "空", -1)
strArg3 = strings.Replace(strArg3, "1", "一", -1)
strArg3 = strings.Replace(strArg3, "2", "二", -1)
strArg3 = strings.Replace(strArg3, "3", "三", -1)
strArg3 = strings.Replace(strArg3, "4", "四", -1)
strArg3 = strings.Replace(strArg3, "5", "五", -1)
strArg3 = strings.Replace(strArg3, "6", "六", -1)
strArg3 = strings.Replace(strArg3, "7", "七", -1)
strArg3 = strings.Replace(strArg3, "8", "八", -1)
strArg3 = strings.Replace(strArg3, "9", "九", -1)
// strArg3 = "야옹, 멍멍, 귀여워! <" + strArg3 + "> by 코코넛 xD 냠냠"
outputdebug.String("modified text: " + strArg3)

// Get another copy of that modified string with a ptr to it.
newArg3 := syscall.StringToUTF16Ptr(strArg3)
sizeNewArg3 := len(syscall.StringToUTF16(strArg3)) * 2 // size as byte array
outputdebug.String("arg3: " + fmt.Sprintf("0x%X -> 0x%X -> 0x%X", arg3, strArg3, newArg3))

// ----------------------------------------------------------------------------
// Copy this new string to where arg3 points to.

// Going to read & write memory with kernel32 API.
kernel32 := syscall.NewLazyDLL("kernel32.dll")

hProcess, _, _ := syscall.Syscall(
kernel32.NewProc("GetCurrentProcess").Addr(),
0, 0, 0, 0,
)

var oldProtect C.DWORD
ret, _, err := syscall.Syscall6(
kernel32.NewProc("VirtualProtectEx").Addr(),
5, hProcess, arg3, uintptr(sizeNewArg3), C.PAGE_EXECUTE_READWRITE, uintptr(unsafe.Pointer(&oldProtect)), 0,
)
outputdebug.String(fmt.Sprint(ret, err)) // ret: return code. err: return detail.

ret, _, err = syscall.Syscall6(
kernel32.NewProc("WriteProcessMemory").Addr(),
4, hProcess, arg3, uintptr(unsafe.Pointer(newArg3)), uintptr(sizeNewArg3), 0, 0,
)
outputdebug.String(fmt.Sprint(ret, err)) // ret: return code. err: return detail.
// ----------------------------------------------------------------------------

// Call the original function.
ret, _, _ = syscall.Syscall6(uintptr(unsafe.Pointer(fpNumberUpdate)), 3, arg1, arg2, arg3, 0, 0, 0)
return
}

func lpwstrToString(cwstr C.LPCWSTR) string {
const maxRunes = 1<<30 - 1
ptr := unsafe.Pointer(cwstr)
sz := C.wcslen((*C.wchar_t)(ptr))
wstr := (*[maxRunes]uint16)(ptr)[:sz:sz]
return string(utf16.Decode(wstr))
}

// ----------------------------------------------------------------------------

// GetProcessModuleHandle returns the base address of .exe module.
//export GetProcessModuleHandle
func GetProcessModuleHandle() (hModule uintptr) {
hModule, _, _ = syscall.Syscall( // (HANDLE)GetModuleHandle(NULL);
syscall.NewLazyDLL("kernel32.dll").NewProc("GetModuleHandleW").Addr(),
1, 0, 0, 0,
)
outputdebug.String(fmt.Sprintf("GetProcessModuleHandle(): 0x%X", hModule))
return
}

// GetHookPoint in our process.
//export GetHookPoint
func GetHookPoint(offset uintptr) (hookPoint uintptr) {
base := GetProcessModuleHandle()
hookPoint = base + offset
outputdebug.String(fmt.Sprintf("GetHookPoint(): 0x%X = 0x%X + 0x%X", hookPoint, base, offset))
return
}

// OnProcessAttach is an async callback (hook).
//export OnProcessAttach
func OnProcessAttach(
hinstDLL unsafe.Pointer, // handle to DLL module
fdwReason uint32, // reason for calling function
lpReserved unsafe.Pointer, // reserved
) {
// Initialize minhook
err := gominhook.Initialize()
if err != nil {
outputdebug.String(err.Error())
log.Fatalln(err)
}

// Clean-up minhook
defer func() {
// Unhook
err := gominhook.DisableHook(gominhook.AllHooks)
if err != nil {
outputdebug.String(err.Error())
log.Println(err)
}
// Uninitialize
err = gominhook.Uninitialize()
if err != nil {
outputdebug.String(err.Error())
log.Println(err)
}
}()

// ----------------------------------------------------------------------------

// Create a hook for OnDisplayUpdate(). // Calculator.exe+2BBC8 - 48 89 5C 24 08 - mov [rsp+08],rbx
err = gominhook.CreateHook(GetHookPoint(0x2BBC8), uintptr(C.OnDisplayUpdate), uintptr(unsafe.Pointer(&fpDisplayUpdate)))
if err != nil {
outputdebug.String(err.Error())
log.Fatalln(err)
}

// Create a hook for OnNumberUpdate(). // Calculator.exe+2BC94 - 48 89 5C 24 08 - mov [rsp+08],rbx
err = gominhook.CreateHook(GetHookPoint(0x2BC94), uintptr(C.OnNumberUpdate), uintptr(unsafe.Pointer(&fpNumberUpdate)))
if err != nil {
outputdebug.String(err.Error())
log.Fatalln(err)
}

// Enable the hook.
err = gominhook.EnableHook(gominhook.AllHooks)
if err != nil {
outputdebug.String(err.Error())
log.Fatalln(err)
}

// ----------------------------------------------------------------------------

// Block this routine.
ch := make(chan int)
<-ch
outputdebug.String("OnProcessAttach(): Exit")
}

// ----------------------------------------------------------------------------

//export MessageBoxTest
func MessageBoxTest() {
winmb.MessageBoxPlain("export Test", "export Test")
}

//export Test
func Test() {
}

const title = "TITLE"

var version = "undefined"

func main() {
// nothing really. xD
}
Binary file added sampleshots/1.PNG
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added sampleshots/1.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added sampleshots/2.PNG
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added sampleshots/3.PNG
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit 5aae6c7

Please sign in to comment.