├── .gitignore ├── README.md ├── main.go └── test_binaries ├── x64 ├── CompileFlags-no-DYNAMICBASE.exe ├── CompileFlags-no-DYNAMICBASE.pdb ├── CompileFlags-no-GS.exe ├── CompileFlags-no-GS.pdb ├── CompileFlags-no-NXCOMPAT.exe └── CompileFlags-no-NXCOMPAT.pdb └── x86 ├── CompileFlags-no-DYNAMICBASE.exe ├── CompileFlags-no-DYNAMICBASE.pdb ├── CompileFlags-no-GS.exe ├── CompileFlags-no-GS.pdb ├── CompileFlags-no-NXCOMPAT.exe ├── CompileFlags-no-NXCOMPAT.pdb ├── CompileFlags-no-SAFESEH.exe └── CompileFlags-no-SAFESEH.pdb /.gitignore: -------------------------------------------------------------------------------- 1 | /binscope* 2 | *.exe 3 | .DS_Store 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # binscope 2 | 3 | `binscope` is a simple tool that checks a Windows PE file for some basic 4 | security issues, including: 5 | 6 | - No `/DYNAMICBASE` flag (i.e. no support for ASLR). 7 | - No `/NXCOMPAT` flag (i.e. no support for DEP). 8 | - No `/GS` flag (i.e. no stack cookies) - **note: not currently working** 9 | - No `/SAFESEH` flag in x86 binaries 10 | - Having any PE sections that are shared and are Read/Write. 11 | 12 | There are some test binaries in the `test_binaries` subfolder that demonstrate 13 | the output for various vulnerabilities. 14 | 15 | ## Example Output 16 | 17 | ``` 18 | $ ./binscope ./test_binaries/x86/*.exe 19 | ./test_binaries/x86/CompileFlags-no-DYNAMICBASE.exe:does not have DYNAMICBASE bit set 20 | ./test_binaries/x86/CompileFlags-no-NXCOMPAT.exe:does not have NXCOMPAT bit set 21 | ./test_binaries/x86/CompileFlags-no-SAFESEH.exe:does not use SAFESEH 22 | ``` 23 | 24 | ## Installation 25 | 26 | You can either compile the code manually: 27 | 28 | git clone https://github.com/andrew-d/binscope.git 29 | cd binscope 30 | go build -v . 31 | 32 | Or you can obtain a [pre-compiled release](https://github.com/andrew-d/binscope/releases). 33 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "debug/pe" 5 | "encoding/binary" 6 | "fmt" 7 | "io" 8 | "log" 9 | "os" 10 | 11 | flag "github.com/ogier/pflag" 12 | ) 13 | 14 | const ( 15 | IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE = 0x0040 16 | IMAGE_DLLCHARACTERISTICS_NX_COMPAT = 0x0100 17 | IMAGE_DLLCHARACTERISTICS_NO_SEH = 0x0400 18 | 19 | IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG = 10 20 | 21 | IMAGE_SUBSYSTEM_NATIVE = 1 22 | IMAGE_SUBSYSTEM_WINDOWS_GUI = 2 23 | IMAGE_SUBSYSTEM_WINDOWS_CUI = 3 24 | 25 | IMAGE_SCN_MEM_SHARED = 0x10000000 26 | IMAGE_SCN_MEM_READ = 0x40000000 27 | IMAGE_SCN_MEM_WRITE = 0x80000000 28 | ) 29 | 30 | type ImageLoadConfigDirectory32 struct { 31 | Size uint32 32 | TimeDateStamp uint32 33 | MajorVersion uint16 34 | MinorVersion uint16 35 | GlobalFlagsClear uint32 36 | GlobalFlagsSet uint32 37 | CriticalSectionDefaultTimeout uint32 38 | DeCommitFreeBlockThreshold uint32 39 | DeCommitTotalFreeThreshold uint32 40 | LockPrefixTable uint32 41 | MaximumAllocationSize uint32 42 | VirtualMemoryThreshold uint32 43 | ProcessHeapFlags uint32 44 | ProcessAffinityMask uint32 45 | CSDVersion uint16 46 | Reserved1 uint16 47 | EditList uint32 48 | SecurityCookie uint32 49 | SEHandlerTable uint32 50 | SEHandlerCount uint32 51 | } 52 | 53 | type ImageLoadConfigDirectory64 struct { 54 | Size uint32 55 | TimeDateStamp uint32 56 | MajorVersion uint16 57 | MinorVersion uint16 58 | GlobalFlagsClear uint32 59 | GlobalFlagsSet uint32 60 | CriticalSectionDefaultTimeout uint32 61 | DeCommitFreeBlockThreshold uint64 62 | DeCommitTotalFreeThreshold uint64 63 | LockPrefixTable uint64 64 | MaximumAllocationSize uint64 65 | VirtualMemoryThreshold uint64 66 | ProcessAffinityMask uint64 67 | ProcessHeapFlags uint32 68 | CSDVersion uint16 69 | Reserved1 uint16 70 | EditList uint64 71 | SecurityCookie uint64 72 | SEHandlerTable uint64 73 | SEHandlerCount uint64 74 | } 75 | 76 | var ( 77 | sizeofImageLoadConfigDirectory32 = uint16(binary.Size(ImageLoadConfigDirectory32{})) 78 | sizeofImageLoadConfigDirectory64 = uint16(binary.Size(ImageLoadConfigDirectory64{})) 79 | 80 | flagVerbose bool 81 | ) 82 | 83 | func init() { 84 | flag.BoolVarP(&flagVerbose, "verbose", "v", false, "be verbose") 85 | flag.Usage = func() { 86 | fmt.Fprintf(os.Stderr, "Usage: binscope [options] ...\n") 87 | flag.PrintDefaults() 88 | } 89 | } 90 | 91 | func main() { 92 | flag.Parse() 93 | 94 | for _, fname := range flag.Args() { 95 | if flagVerbose { 96 | log.Println("Checking file:", fname) 97 | } 98 | 99 | if err := check(fname); err != nil { 100 | log.Printf("Error checking file:", err) 101 | } 102 | } 103 | } 104 | 105 | func check(fname string) error { 106 | f, err := os.Open(fname) 107 | if err != nil { 108 | return err 109 | } 110 | defer f.Close() 111 | 112 | pefile, err := pe.NewFile(f) 113 | if err != nil { 114 | return err 115 | } 116 | 117 | dch := getDllCharacteristics(pefile) 118 | subsystem := getSubsystem(pefile) 119 | 120 | if flagVerbose { 121 | log.Printf(" Machine = 0x%04d\n", pefile.Machine) 122 | log.Printf(" Subsystem = %d\n", subsystem) 123 | log.Printf(" DllCharacteristics = 0x%04x\n", dch) 124 | } 125 | 126 | // Device drivers always have these flags set. 127 | if subsystem != IMAGE_SUBSYSTEM_NATIVE { 128 | if dch&IMAGE_DLLCHARACTERISTICS_NX_COMPAT == 0 { 129 | fmt.Printf("%s:does not have NXCOMPAT bit set\n", fname) 130 | } 131 | if dch&IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE == 0 { 132 | fmt.Printf("%s:does not have DYNAMICBASE bit set\n", fname) 133 | } 134 | } 135 | 136 | // Check for the /GS flag 137 | dir, err := readImageLoadConfigDirectory(pefile) 138 | if err != nil { 139 | return err 140 | } 141 | if dir != nil { 142 | var securityCookie uint64 143 | switch imdir := dir.(type) { 144 | case *ImageLoadConfigDirectory32: 145 | securityCookie = uint64(imdir.SecurityCookie) 146 | case *ImageLoadConfigDirectory64: 147 | securityCookie = imdir.SecurityCookie 148 | default: 149 | panic("bad load config directory type") 150 | } 151 | 152 | if flagVerbose { 153 | log.Printf(" SecurityCookie = 0x%x", securityCookie) 154 | } 155 | if securityCookie == 0 { 156 | fmt.Printf("%s:does not use security cookies\n", fname) 157 | } 158 | 159 | // Check for SAFESEH on Windows x86 only 160 | if pefile.Machine == pe.IMAGE_FILE_MACHINE_I386 { 161 | if dch&IMAGE_DLLCHARACTERISTICS_NO_SEH == 0 { 162 | handlerTable := dir.(*ImageLoadConfigDirectory32).SEHandlerTable 163 | handlerCount := dir.(*ImageLoadConfigDirectory32).SEHandlerTable 164 | 165 | if handlerTable == 0 { 166 | fmt.Printf("%s:does not use SAFESEH\n", fname) 167 | } 168 | 169 | if flagVerbose { 170 | log.Printf(" SEHandlerTable = 0x%x\n", handlerTable) 171 | log.Printf(" SEHandlerCount = 0x%x\n", handlerCount) 172 | } 173 | } else if flagVerbose { 174 | log.Println(" Skipping SAFESEH check because image has NO_SEH bit set") 175 | } 176 | } else if flagVerbose { 177 | log.Println(" Skipping SAFESEH check on non-x86 file") 178 | } 179 | } 180 | 181 | // Check for R/W shared image sections 182 | var rwSharedFlags uint32 = IMAGE_SCN_MEM_SHARED | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE 183 | for _, section := range pefile.Sections { 184 | if section.Characteristics&rwSharedFlags == rwSharedFlags { 185 | fmt.Printf("%s:has a R/W shared section named %s\n", fname, section.Name) 186 | } 187 | } 188 | 189 | return nil 190 | } 191 | 192 | func getDllCharacteristics(pefile *pe.File) uint16 { 193 | switch hdr := pefile.OptionalHeader.(type) { 194 | case *pe.OptionalHeader32: 195 | return hdr.DllCharacteristics 196 | case *pe.OptionalHeader64: 197 | return hdr.DllCharacteristics 198 | default: 199 | panic("unknown optional header") 200 | } 201 | } 202 | 203 | func getSubsystem(pefile *pe.File) uint16 { 204 | switch hdr := pefile.OptionalHeader.(type) { 205 | case *pe.OptionalHeader32: 206 | return hdr.Subsystem 207 | case *pe.OptionalHeader64: 208 | return hdr.Subsystem 209 | default: 210 | panic("unknown optional header") 211 | } 212 | } 213 | 214 | func getDataDirectory(pefile *pe.File, index int) pe.DataDirectory { 215 | if index > 15 || index < 0 { 216 | panic("bad index") 217 | } 218 | 219 | switch hdr := pefile.OptionalHeader.(type) { 220 | case *pe.OptionalHeader32: 221 | return hdr.DataDirectory[index] 222 | case *pe.OptionalHeader64: 223 | return hdr.DataDirectory[index] 224 | default: 225 | panic("unknown optional header") 226 | } 227 | } 228 | 229 | func readImageLoadConfigDirectory(pefile *pe.File) (interface{}, error) { 230 | dir := getDataDirectory(pefile, IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG) 231 | if dir.VirtualAddress == 0 { 232 | return nil, nil 233 | } 234 | 235 | // Allocate enough memory for the section. 236 | if pefile.Machine != pe.IMAGE_FILE_MACHINE_I386 && pefile.Machine != pe.IMAGE_FILE_MACHINE_AMD64 { 237 | return nil, fmt.Errorf("unknown machine type: %04x", pefile.Machine) 238 | } 239 | 240 | // Now, we have the directory's virtual address. To get the file offset, 241 | // we need to loop through all sections to find the containing one. 242 | var sr io.ReadSeeker 243 | for _, section := range pefile.Sections { 244 | if dir.VirtualAddress < section.VirtualAddress || 245 | dir.VirtualAddress >= (section.VirtualAddress+section.Size) { 246 | continue 247 | } 248 | 249 | sr = section.Open() 250 | 251 | // Seek to the right place 252 | offset := int64(dir.VirtualAddress - section.VirtualAddress) 253 | sr.Seek(offset, os.SEEK_SET) 254 | break 255 | } 256 | 257 | if sr == nil { 258 | return nil, fmt.Errorf("did not find data directory containing VA 0x%08x", dir.VirtualAddress) 259 | } 260 | 261 | // Convert into the appropriate format. 262 | if pefile.Machine == pe.IMAGE_FILE_MACHINE_I386 { 263 | var ret ImageLoadConfigDirectory32 264 | 265 | if err := binary.Read(sr, binary.LittleEndian, &ret); err != nil { 266 | return nil, err 267 | } 268 | 269 | return &ret, nil 270 | } else { 271 | var ret ImageLoadConfigDirectory64 272 | 273 | if err := binary.Read(sr, binary.LittleEndian, &ret); err != nil { 274 | return nil, err 275 | } 276 | 277 | return &ret, nil 278 | } 279 | } 280 | -------------------------------------------------------------------------------- /test_binaries/x64/CompileFlags-no-DYNAMICBASE.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andrew-d/binscope/ae736add878a8dfa8d46662ec95cacafd9ac66a7/test_binaries/x64/CompileFlags-no-DYNAMICBASE.exe -------------------------------------------------------------------------------- /test_binaries/x64/CompileFlags-no-DYNAMICBASE.pdb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andrew-d/binscope/ae736add878a8dfa8d46662ec95cacafd9ac66a7/test_binaries/x64/CompileFlags-no-DYNAMICBASE.pdb -------------------------------------------------------------------------------- /test_binaries/x64/CompileFlags-no-GS.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andrew-d/binscope/ae736add878a8dfa8d46662ec95cacafd9ac66a7/test_binaries/x64/CompileFlags-no-GS.exe -------------------------------------------------------------------------------- /test_binaries/x64/CompileFlags-no-GS.pdb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andrew-d/binscope/ae736add878a8dfa8d46662ec95cacafd9ac66a7/test_binaries/x64/CompileFlags-no-GS.pdb -------------------------------------------------------------------------------- /test_binaries/x64/CompileFlags-no-NXCOMPAT.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andrew-d/binscope/ae736add878a8dfa8d46662ec95cacafd9ac66a7/test_binaries/x64/CompileFlags-no-NXCOMPAT.exe -------------------------------------------------------------------------------- /test_binaries/x64/CompileFlags-no-NXCOMPAT.pdb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andrew-d/binscope/ae736add878a8dfa8d46662ec95cacafd9ac66a7/test_binaries/x64/CompileFlags-no-NXCOMPAT.pdb -------------------------------------------------------------------------------- /test_binaries/x86/CompileFlags-no-DYNAMICBASE.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andrew-d/binscope/ae736add878a8dfa8d46662ec95cacafd9ac66a7/test_binaries/x86/CompileFlags-no-DYNAMICBASE.exe -------------------------------------------------------------------------------- /test_binaries/x86/CompileFlags-no-DYNAMICBASE.pdb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andrew-d/binscope/ae736add878a8dfa8d46662ec95cacafd9ac66a7/test_binaries/x86/CompileFlags-no-DYNAMICBASE.pdb -------------------------------------------------------------------------------- /test_binaries/x86/CompileFlags-no-GS.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andrew-d/binscope/ae736add878a8dfa8d46662ec95cacafd9ac66a7/test_binaries/x86/CompileFlags-no-GS.exe -------------------------------------------------------------------------------- /test_binaries/x86/CompileFlags-no-GS.pdb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andrew-d/binscope/ae736add878a8dfa8d46662ec95cacafd9ac66a7/test_binaries/x86/CompileFlags-no-GS.pdb -------------------------------------------------------------------------------- /test_binaries/x86/CompileFlags-no-NXCOMPAT.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andrew-d/binscope/ae736add878a8dfa8d46662ec95cacafd9ac66a7/test_binaries/x86/CompileFlags-no-NXCOMPAT.exe -------------------------------------------------------------------------------- /test_binaries/x86/CompileFlags-no-NXCOMPAT.pdb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andrew-d/binscope/ae736add878a8dfa8d46662ec95cacafd9ac66a7/test_binaries/x86/CompileFlags-no-NXCOMPAT.pdb -------------------------------------------------------------------------------- /test_binaries/x86/CompileFlags-no-SAFESEH.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andrew-d/binscope/ae736add878a8dfa8d46662ec95cacafd9ac66a7/test_binaries/x86/CompileFlags-no-SAFESEH.exe -------------------------------------------------------------------------------- /test_binaries/x86/CompileFlags-no-SAFESEH.pdb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andrew-d/binscope/ae736add878a8dfa8d46662ec95cacafd9ac66a7/test_binaries/x86/CompileFlags-no-SAFESEH.pdb --------------------------------------------------------------------------------