;
; scanexe (CC0), by fliermate (aka Mat Quasar), 2025/12
;

format PE console
include 'win32a.inc'

FILELIMIT = 8192
PE32 = 0x10B
PE64 = 0x20B
IMAGE_SUBSYSTEM_WINDOWS_GUI = 2
IMAGE_SUBSYSTEM_WINDOWS_CUI = 3
OFFSET_MAGIC = 0x18
OFFSET_SUBSYSTEM = 0x5C

section '.code' code readable executable

  entry $
        invoke  GetStdHandle, STD_OUTPUT_HANDLE
        mov     dword [stdout],eax
        invoke  lstrcat, buf, filepath
        call    find_files
        invoke  ExitProcess, 0
    
proc    find_files
    locals
        hfind  dd ?
    endl
        invoke FindFirstFile,buf,wfd
        cmp    eax,INVALID_HANDLE_VALUE
        je     .exit
        mov    [hfind], eax
.check:
        test   [wfd.dwFileAttributes],FILE_ATTRIBUTE_DIRECTORY
        jz     .found
        jmp    .next
.found:
        invoke  WriteFile, [stdout], wfd.cFileName, 16, 0, 0
        invoke  CreateFile, wfd.cFileName, GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0
        mov     dword [fd], eax
        cmp     eax, INVALID_HANDLE_VALUE
        je      .quit
        
        invoke  ReadFile, dword [fd], MZ_ID_read, 2, dummy, 0
        movzx   ebx, word [MZ_ID_read]
        cmp     bx, word [MZ_ID]
        jnz     .err4
        invoke  SetFilePointer, dword [fd], 0x3C, 0, FILE_BEGIN
        invoke  ReadFile, dword [fd], PE_start, 4, dummy, 0
        invoke  SetFilePointer, dword [fd], dword [PE_start], 0, FILE_BEGIN
        invoke  ReadFile, dword [fd], PE_ID_read, 4, dummy, 0
        mov     ebx, dword [PE_ID_read]
        cmp     ebx, dword [PE_ID]
        jnz     .err5
        mov     ebx, dword [PE_start]
        add     ebx, OFFSET_MAGIC
        mov     [_offset], ebx
        invoke  SetFilePointer, dword [fd], dword [_offset] , 0, FILE_BEGIN
        invoke  ReadFile, dword [fd], _magic, 2, dummy, 0
        movzx   ebx, word [_magic]
        cmp     ebx, PE32
        jz      .isPE32
        invoke  WriteFile, dword [stdout], _msg7, _msglen7, dummy, 0
        jmp     .continue
  .isPE32:
        invoke  WriteFile, dword [stdout], _msg6, _msglen6, dummy, 0
  .continue:
        mov     ebx, dword [PE_start]
        add     ebx, OFFSET_SUBSYSTEM
        mov     [_offset], ebx
        invoke  SetFilePointer, dword [fd], dword [_offset] , 0, FILE_BEGIN
        invoke  ReadFile, dword [fd], _subsystem, 2, dummy, 0
        movzx   ebx, word [_subsystem]
        cmp     ebx, IMAGE_SUBSYSTEM_WINDOWS_CUI
        jz      .isCUI
        cmp     ebx, IMAGE_SUBSYSTEM_WINDOWS_GUI
        jz      .isGUI
        invoke  WriteFile, dword [stdout], _msg10, _msglen10, dummy, 0
        jmp     .done
  .isCUI:
        invoke  WriteFile, dword [stdout], _msg8, _msglen8, dummy, 0
        jmp     .done
  .isGUI:
        invoke  WriteFile, dword [stdout], _msg9, _msglen9, dummy, 0
  .done:
        jmp     .close

  .err1:
        invoke  WriteFile, dword [stdout], _msg1, _msglen1, dummy, 0
        jmp     .quit
  .err2:
        invoke  WriteFile, dword [stdout], _msg2, _msglen2, dummy, 0
        jmp     .close
  .err4:
        invoke  WriteFile, dword [stdout], _msg4, _msglen4, dummy, 0
        jmp     .close
  .err5:
        invoke  WriteFile, dword [stdout], _msg5, _msglen5, dummy, 0

.close:
        invoke  CloseHandle, dword [fd]
        
.quit:
        add    [filecount],1
        cmp    [filecount],FILELIMIT
        jae    .exit
.next:
        invoke FindNextFile,[hfind],wfd
        test   eax,eax
        jne    .check
        invoke FindClose, [hfind]
.exit:
        ret
endp

section '.data' data readable writable

  _msg1 db ' could not be opened.',13,10
  _msglen1  = $ - _msg1
  _msg2 db ' could not be read.',13,10
  _msglen2  = $ - _msg2
  _msg4 db ' is not a valid EXE.',13,10
  _msglen4  = $ - _msg4
  _msg5 db ' is not a valid PE.',13,10
  _msglen5  = $ - _msg5
  _msg6 db ' is a 32-bit '
  _msglen6 = $ - _msg6
  _msg7 db ' is a 64-bit '
  _msglen7 = $ - _msg7
  _msg8 db 'Console app.',13,10
  _msglen8 = $ - _msg8
  _msg9 db 'GUI app.',13,10
  _msglen9 = $ - _msg9
  _msg10 db 'PE file.',13,10
  _msglen10 = $ - _msg10

  _magic      dw ?
  _subsystem  dd ?
  _key        dd ?
  _offset     dd 0
  PE_start    dd ?
  PE_ID_read  rb 4
  PE_ID       db 'P','E',0,0
  MZ_ID_read  rb 2
  MZ_ID       db 'M','Z'
  buf         rb MAX_PATH
  filepath    db '.\*.exe',0
  wfd         WIN32_FIND_DATA
  stdout      dd ?
  filecount   dw ?
  dummy       dd ?
  fd          dd ?

section '.idata' import readable writable

 library kernel32, 'KERNEL32.DLL'

 import kernel32,\
        GetStdHandle, 'GetStdHandle', \
        WriteConsole, 'WriteConsoleA', \
        ReadConsole, 'ReadConsoleA', \
        CreateFile, 'CreateFileA', \
        ReadFile, 'ReadFile', \
        WriteFile, 'WriteFile', \
        CloseHandle, 'CloseHandle', \
        GetCommandLine, 'GetCommandLineA', \
        SetFilePointer, 'SetFilePointer', \
        lstrcat, 'lstrcatA',\
        FindFirstFile, 'FindFirstFileA',\
        FindNextFile, 'FindNextFileA',\
        FindClose, 'FindClose',\
        ExitProcess, 'ExitProcess'