Windows 10 시간 동기화 실패 문제…(feat Golang)

윈도우10 시간 동기화 실패

몇일동안 인터넷 검색해서 나온 해결법 다 해보고도 해결 못한 문제가 “윈도우 시간 동기화 실패” 문제입니다. 이사하기 전 KT 쓸때는 아무 문제 없었는데 이사후 지역 케이블 인터넷으로 바뀐뒤 부터 안되는걸 보니 제가 뭘해도 안될거 같아서 포기하고 윈도우 시간 동기화 하는 프로그램을 하나 만들었습니다.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
func IsAdmin() bool {
_, err := os.Open("\\.\PHYSICALDRIVE0")
if err != nil {
//관리자 권한이 없음
return false
}
//관리자 권한이 있음.
return true
}
func IsAdmin() bool { _, err := os.Open("\\.\PHYSICALDRIVE0") if err != nil { //관리자 권한이 없음 return false } //관리자 권한이 있음. return true }
func IsAdmin() bool {
    _, err := os.Open("\\.\PHYSICALDRIVE0")
    if err != nil {
        //관리자 권한이 없음
        return false
    }
    //관리자 권한이 있음.
    return true
}

윈도우에서 시간값 변경을 할려면 관리자 권한이 필요합니다. 위 코드는 단순하게 권한이 있는지 없는지 유무만 체크합니다.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
func runMeElevated() {
verb := "runas"
exe, _ := os.Executable()
cwd, _ := os.Getwd()
args := strings.Join(os.Args[1:], " ")
verbPtr, _ := syscall.UTF16PtrFromString(verb)
exePtr, _ := syscall.UTF16PtrFromString(exe)
cwdPtr, _ := syscall.UTF16PtrFromString(cwd)
argPtr, _ := syscall.UTF16PtrFromString(args)
// var showCmd int32 = 0 //SW_HIDE
var showCmd int32 = 5 //SW_SHOW
err := windows.ShellExecute(0, verbPtr, exePtr, argPtr, cwdPtr, showCmd)
if err != nil {
panic(err)
}
}
func runMeElevated() { verb := "runas" exe, _ := os.Executable() cwd, _ := os.Getwd() args := strings.Join(os.Args[1:], " ") verbPtr, _ := syscall.UTF16PtrFromString(verb) exePtr, _ := syscall.UTF16PtrFromString(exe) cwdPtr, _ := syscall.UTF16PtrFromString(cwd) argPtr, _ := syscall.UTF16PtrFromString(args) // var showCmd int32 = 0 //SW_HIDE var showCmd int32 = 5 //SW_SHOW err := windows.ShellExecute(0, verbPtr, exePtr, argPtr, cwdPtr, showCmd) if err != nil { panic(err) } }
func runMeElevated() {
    verb := "runas"
    exe, _ := os.Executable()
    cwd, _ := os.Getwd()
    args := strings.Join(os.Args[1:], " ")

    verbPtr, _ := syscall.UTF16PtrFromString(verb)
    exePtr, _ := syscall.UTF16PtrFromString(exe)
    cwdPtr, _ := syscall.UTF16PtrFromString(cwd)
    argPtr, _ := syscall.UTF16PtrFromString(args)

    // var showCmd int32 = 0 //SW_HIDE
    var showCmd int32 = 5 //SW_SHOW

    err := windows.ShellExecute(0, verbPtr, exePtr, argPtr, cwdPtr, showCmd)
    if err != nil {
        panic(err)
    }
}

이 코드는 현재 실행파일을 관리자 권한으로 다시 실행 하는 코드입니다.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
if IsAdmin() {
//관리자 권한으로 실행시킬 코드들
} else {
//관리자가 아니면 관리자 권한으로 다시 실행함
runMeElevated()
}
if IsAdmin() { //관리자 권한으로 실행시킬 코드들 } else { //관리자가 아니면 관리자 권한으로 다시 실행함 runMeElevated() }
if IsAdmin() {
    //관리자 권한으로 실행시킬 코드들
} else {
    //관리자가 아니면 관리자 권한으로 다시 실행함
    runMeElevated()
}

위 함수 두개를 조합해서 이렇게 사용합니다.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
var (
Kernel32dll = windows.NewLazyDLL("Kernel32.dll")
procSystemParamInfo = Kernel32dll.NewProc("SetLocalTime")
)
var ( Kernel32dll = windows.NewLazyDLL("Kernel32.dll") procSystemParamInfo = Kernel32dll.NewProc("SetLocalTime") )
var (
    Kernel32dll = windows.NewLazyDLL("Kernel32.dll")
    procSystemParamInfo = Kernel32dll.NewProc("SetLocalTime")
)

시스템 시간을 변경할려면 WinAPI중 SetLocalTime 함수를 사용해야 합니다.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
type SYSTEMTIME struct {
Year uint16
Month uint16
DayOfWeek uint16
Day uint16
Hour uint16
Minute uint16
Second uint16
Milliseconds uint16
}
type SYSTEMTIME struct { Year uint16 Month uint16 DayOfWeek uint16 Day uint16 Hour uint16 Minute uint16 Second uint16 Milliseconds uint16 }
type SYSTEMTIME struct {
    Year         uint16
    Month        uint16
    DayOfWeek    uint16
    Day          uint16
    Hour         uint16
    Minute       uint16
    Second       uint16
    Milliseconds uint16
}

SetLocalTime에서 사용할 SYSTEMTIME 구조체를 선언해줍니다.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
ntptime, err := ntp.Time("time.google.com")
ntptime, err := ntp.Time("time.google.com")
ntptime, err := ntp.Time("time.google.com")

NTP 라이브러리로 github.com/beevik/ntp 를 사용했습니다.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
st := SYSTEMTIME{}
st.Year = uint16(ntptime.Year())
st.Month = uint16(ntptime.Month())
st.Day = uint16(ntptime.Day())
st.Hour = uint16(ntptime.Hour())
st.Minute = uint16(ntptime.Minute())
st.Second = uint16(ntptime.Second())
st.Milliseconds = uint16(ntptime.Nanosecond() / 1000000)
procSystemParamInfo.Call(uintptr(unsafe.Pointer(&st)))
st := SYSTEMTIME{} st.Year = uint16(ntptime.Year()) st.Month = uint16(ntptime.Month()) st.Day = uint16(ntptime.Day()) st.Hour = uint16(ntptime.Hour()) st.Minute = uint16(ntptime.Minute()) st.Second = uint16(ntptime.Second()) st.Milliseconds = uint16(ntptime.Nanosecond() / 1000000) procSystemParamInfo.Call(uintptr(unsafe.Pointer(&st)))
st := SYSTEMTIME{}
st.Year = uint16(ntptime.Year())
st.Month = uint16(ntptime.Month())
st.Day = uint16(ntptime.Day())
st.Hour = uint16(ntptime.Hour())
st.Minute = uint16(ntptime.Minute())
st.Second = uint16(ntptime.Second())
st.Milliseconds = uint16(ntptime.Nanosecond() / 1000000)

procSystemParamInfo.Call(uintptr(unsafe.Pointer(&st)))

구한 ntptime을 구조체에 채운뒤 SetLocalTime를 호출하면 됩니다.

이하 전체 코드입니다.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
package main
import (
"encoding/json"
"fmt"
"time"
"unsafe"
"os"
"strings"
"syscall"
"github.com/beevik/ntp"
"golang.org/x/sys/windows"
)
// https://anubissec.github.io/How-To-Call-Windows-APIs-In-Golang/#
var (
Kernel32dll = windows.NewLazyDLL("Kernel32.dll")
//procSystemParamInfo = Kernel32dll.NewProc("SetSystemTime")
procSystemParamInfo = Kernel32dll.NewProc("SetLocalTime")
)
type SYSTEMTIME struct {
Year uint16
Month uint16
DayOfWeek uint16
Day uint16
Hour uint16
Minute uint16
Second uint16
Milliseconds uint16
}
// ---------------------------------------------------------------------------------------------
// 관리자 권한 체크
func IsAdmin() bool {
_, err := os.Open("\\.\PHYSICALDRIVE0")
if err != nil {
//관리자 권한이 없음
return false
}
//관리자 권한이 있음.
return true
}
// ---------------------------------------------------------------------------------------------
func runMeElevated() {
verb := "runas"
exe, _ := os.Executable()
cwd, _ := os.Getwd()
args := strings.Join(os.Args[1:], " ")
verbPtr, _ := syscall.UTF16PtrFromString(verb)
exePtr, _ := syscall.UTF16PtrFromString(exe)
cwdPtr, _ := syscall.UTF16PtrFromString(cwd)
argPtr, _ := syscall.UTF16PtrFromString(args)
// var showCmd int32 = 0 //SW_HIDE
var showCmd int32 = 5 //SW_SHOW
err := windows.ShellExecute(0, verbPtr, exePtr, argPtr, cwdPtr, showCmd)
if err != nil {
panic(err)
}
}
// ---------------------------------------------------------------------------------------------
func checkErr(err error) {
if err != nil {
panic(err)
}
}
type Config struct {
//DB관련 설정값
NTP_URL_LIST string `json:"ntp_url_list"`
}
// ---------------------------------------------------------------------------------------------
// LoadConfig 는 설정파일을 읽어들인다.
func LoadConfig(filePath string, result any) bool {
bytes, err := os.ReadFile(filePath)
if err != nil {
fmt.Println(err)
fo, err := os.Create(filePath)
checkErr(err)
defer fo.Close()
jsonBytes, err := json.MarshalIndent(result, "", " ")
checkErr(err)
_, err = fo.Write(jsonBytes)
checkErr(err)
fmt.Println(filePath, "파일을 생성했습니다.")
fmt.Println("json 파일을 열어서 각 항목을 수정하세요")
return false
}
err = json.Unmarshal(bytes, &result)
checkErr(err)
return true
}
// ---------------------------------------------------------------------------------------------
func main() {
//설정파일 값 읽기
cf := Config{}
if !LoadConfig("./timesync.json", &cf) {
//설정파일 읽기 실패시 종료.
os.Exit(0)
}
//관리자 권한 체크
if IsAdmin() {
//관리자 권한일 경우
URLS := strings.Split(cf.NTP_URL_LIST, ",")
for _, url := range URLS {
url = strings.Replace(url, " ", "", -1)
ntptime, err := ntp.Time(url)
if err != nil {
fmt.Println(err)
continue
}
st := SYSTEMTIME{}
st.Year = uint16(ntptime.Year())
st.Month = uint16(ntptime.Month())
st.Day = uint16(ntptime.Day())
st.Hour = uint16(ntptime.Hour())
st.Minute = uint16(ntptime.Minute())
st.Second = uint16(ntptime.Second())
st.Milliseconds = uint16(ntptime.Nanosecond() / 1000000)
procSystemParamInfo.Call(uintptr(unsafe.Pointer(&st)))
t := time.Now()
t2 := t.Sub(ntptime)
fmt.Printf("변경전 %s 서버와 로컬 시간 차이 : %s n", url, t2)
time.Sleep(time.Millisecond * 100)
ntptime, err = ntp.Time(url)
if err != nil {
fmt.Println(err)
continue
}
t = time.Now()
t2 = t.Sub(ntptime)
fmt.Printf("변경후 %s 서버와 로컬 시간 차이 : %s n", url, t2)
break
}
//------------------------------------------------------------------
fmt.Println("엔터키 누르면 창이 닫힙니다.")
fmt.Scanln()
} else {
//관리자가 아니면 관리자 권한으로 다시 실행함
runMeElevated()
}
}
package main import ( "encoding/json" "fmt" "time" "unsafe" "os" "strings" "syscall" "github.com/beevik/ntp" "golang.org/x/sys/windows" ) // https://anubissec.github.io/How-To-Call-Windows-APIs-In-Golang/# var ( Kernel32dll = windows.NewLazyDLL("Kernel32.dll") //procSystemParamInfo = Kernel32dll.NewProc("SetSystemTime") procSystemParamInfo = Kernel32dll.NewProc("SetLocalTime") ) type SYSTEMTIME struct { Year uint16 Month uint16 DayOfWeek uint16 Day uint16 Hour uint16 Minute uint16 Second uint16 Milliseconds uint16 } // --------------------------------------------------------------------------------------------- // 관리자 권한 체크 func IsAdmin() bool { _, err := os.Open("\\.\PHYSICALDRIVE0") if err != nil { //관리자 권한이 없음 return false } //관리자 권한이 있음. return true } // --------------------------------------------------------------------------------------------- func runMeElevated() { verb := "runas" exe, _ := os.Executable() cwd, _ := os.Getwd() args := strings.Join(os.Args[1:], " ") verbPtr, _ := syscall.UTF16PtrFromString(verb) exePtr, _ := syscall.UTF16PtrFromString(exe) cwdPtr, _ := syscall.UTF16PtrFromString(cwd) argPtr, _ := syscall.UTF16PtrFromString(args) // var showCmd int32 = 0 //SW_HIDE var showCmd int32 = 5 //SW_SHOW err := windows.ShellExecute(0, verbPtr, exePtr, argPtr, cwdPtr, showCmd) if err != nil { panic(err) } } // --------------------------------------------------------------------------------------------- func checkErr(err error) { if err != nil { panic(err) } } type Config struct { //DB관련 설정값 NTP_URL_LIST string `json:"ntp_url_list"` } // --------------------------------------------------------------------------------------------- // LoadConfig 는 설정파일을 읽어들인다. func LoadConfig(filePath string, result any) bool { bytes, err := os.ReadFile(filePath) if err != nil { fmt.Println(err) fo, err := os.Create(filePath) checkErr(err) defer fo.Close() jsonBytes, err := json.MarshalIndent(result, "", " ") checkErr(err) _, err = fo.Write(jsonBytes) checkErr(err) fmt.Println(filePath, "파일을 생성했습니다.") fmt.Println("json 파일을 열어서 각 항목을 수정하세요") return false } err = json.Unmarshal(bytes, &result) checkErr(err) return true } // --------------------------------------------------------------------------------------------- func main() { //설정파일 값 읽기 cf := Config{} if !LoadConfig("./timesync.json", &cf) { //설정파일 읽기 실패시 종료. os.Exit(0) } //관리자 권한 체크 if IsAdmin() { //관리자 권한일 경우 URLS := strings.Split(cf.NTP_URL_LIST, ",") for _, url := range URLS { url = strings.Replace(url, " ", "", -1) ntptime, err := ntp.Time(url) if err != nil { fmt.Println(err) continue } st := SYSTEMTIME{} st.Year = uint16(ntptime.Year()) st.Month = uint16(ntptime.Month()) st.Day = uint16(ntptime.Day()) st.Hour = uint16(ntptime.Hour()) st.Minute = uint16(ntptime.Minute()) st.Second = uint16(ntptime.Second()) st.Milliseconds = uint16(ntptime.Nanosecond() / 1000000) procSystemParamInfo.Call(uintptr(unsafe.Pointer(&st))) t := time.Now() t2 := t.Sub(ntptime) fmt.Printf("변경전 %s 서버와 로컬 시간 차이 : %s n", url, t2) time.Sleep(time.Millisecond * 100) ntptime, err = ntp.Time(url) if err != nil { fmt.Println(err) continue } t = time.Now() t2 = t.Sub(ntptime) fmt.Printf("변경후 %s 서버와 로컬 시간 차이 : %s n", url, t2) break } //------------------------------------------------------------------ fmt.Println("엔터키 누르면 창이 닫힙니다.") fmt.Scanln() } else { //관리자가 아니면 관리자 권한으로 다시 실행함 runMeElevated() } }
package main

import (
    "encoding/json"
    "fmt"
    "time"
    "unsafe"

    "os"
    "strings"
    "syscall"

    "github.com/beevik/ntp"
    "golang.org/x/sys/windows"
)

// https://anubissec.github.io/How-To-Call-Windows-APIs-In-Golang/#
var (
    Kernel32dll = windows.NewLazyDLL("Kernel32.dll")
    //procSystemParamInfo = Kernel32dll.NewProc("SetSystemTime")
    procSystemParamInfo = Kernel32dll.NewProc("SetLocalTime")
)

type SYSTEMTIME struct {
    Year         uint16
    Month        uint16
    DayOfWeek    uint16
    Day          uint16
    Hour         uint16
    Minute       uint16
    Second       uint16
    Milliseconds uint16
}

// ---------------------------------------------------------------------------------------------
// 관리자 권한 체크
func IsAdmin() bool {
    _, err := os.Open("\\.\PHYSICALDRIVE0")
    if err != nil {
        //관리자 권한이 없음
        return false
    }
    //관리자 권한이 있음.
    return true
}

// ---------------------------------------------------------------------------------------------
func runMeElevated() {
    verb := "runas"
    exe, _ := os.Executable()
    cwd, _ := os.Getwd()
    args := strings.Join(os.Args[1:], " ")

    verbPtr, _ := syscall.UTF16PtrFromString(verb)
    exePtr, _ := syscall.UTF16PtrFromString(exe)
    cwdPtr, _ := syscall.UTF16PtrFromString(cwd)
    argPtr, _ := syscall.UTF16PtrFromString(args)

    // var showCmd int32 = 0 //SW_HIDE
    var showCmd int32 = 5 //SW_SHOW

    err := windows.ShellExecute(0, verbPtr, exePtr, argPtr, cwdPtr, showCmd)
    if err != nil {
        panic(err)
    }
}

// ---------------------------------------------------------------------------------------------
func checkErr(err error) {
    if err != nil {
        panic(err)
    }
}

type Config struct {
    //DB관련 설정값
    NTP_URL_LIST string `json:"ntp_url_list"`
}

// ---------------------------------------------------------------------------------------------
// LoadConfig 는 설정파일을 읽어들인다.
func LoadConfig(filePath string, result any) bool {

    bytes, err := os.ReadFile(filePath)
    if err != nil {
        fmt.Println(err)

        fo, err := os.Create(filePath)
        checkErr(err)
        defer fo.Close()

        jsonBytes, err := json.MarshalIndent(result, "", "	")
        checkErr(err)

        _, err = fo.Write(jsonBytes)
        checkErr(err)

        fmt.Println(filePath, "파일을 생성했습니다.")
        fmt.Println("json 파일을 열어서 각 항목을 수정하세요")
        return false
    }
    err = json.Unmarshal(bytes, &result)
    checkErr(err)

    return true
}

// ---------------------------------------------------------------------------------------------
func main() {

    //설정파일 값 읽기
    cf := Config{}
    if !LoadConfig("./timesync.json", &cf) {
        //설정파일 읽기 실패시 종료.
        os.Exit(0)
    }

    //관리자 권한 체크
    if IsAdmin() {
        //관리자 권한일 경우
        URLS := strings.Split(cf.NTP_URL_LIST, ",")

        for _, url := range URLS {
            url = strings.Replace(url, " ", "", -1)

            ntptime, err := ntp.Time(url)
            if err != nil {
                fmt.Println(err)
                continue
            }

            st := SYSTEMTIME{}
            st.Year = uint16(ntptime.Year())
            st.Month = uint16(ntptime.Month())
            st.Day = uint16(ntptime.Day())
            st.Hour = uint16(ntptime.Hour())
            st.Minute = uint16(ntptime.Minute())
            st.Second = uint16(ntptime.Second())
            st.Milliseconds = uint16(ntptime.Nanosecond() / 1000000)
            procSystemParamInfo.Call(uintptr(unsafe.Pointer(&st)))

            t := time.Now()
            t2 := t.Sub(ntptime)
            fmt.Printf("변경전 %s 서버와 로컬 시간 차이 : %s n", url, t2)

            time.Sleep(time.Millisecond * 100)

            ntptime, err = ntp.Time(url)
            if err != nil {
                fmt.Println(err)
                continue
            }
            t = time.Now()
            t2 = t.Sub(ntptime)
            fmt.Printf("변경후 %s 서버와 로컬 시간 차이 : %s n", url, t2)
            break
        }
        //------------------------------------------------------------------
        fmt.Println("엔터키 누르면 창이 닫힙니다.")
        fmt.Scanln()

    } else {
        //관리자가 아니면 관리자 권한으로 다시 실행함
        runMeElevated()
    }
}

Comments

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다