{"id":21070826,"url":"https://github.com/fafalone/checkbitness","last_synced_at":"2026-02-06T09:01:41.495Z","repository":{"id":200450690,"uuid":"705526458","full_name":"fafalone/CheckBitness","owner":"fafalone","description":"A simple utility to verify an executable is valid and check whether 32bit/64bit","archived":false,"fork":false,"pushed_at":"2024-03-03T16:21:37.000Z","size":3727,"stargazers_count":3,"open_issues_count":0,"forks_count":2,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-03-14T02:47:01.070Z","etag":null,"topics":["pe-file","pe-format","twinbasic","vb6"],"latest_commit_sha":null,"homepage":"","language":"Visual Basic 6.0","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/fafalone.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2023-10-16T07:28:34.000Z","updated_at":"2023-12-31T18:41:06.000Z","dependencies_parsed_at":null,"dependency_job_id":"945b561a-632f-4f2f-a615-8c51528232a0","html_url":"https://github.com/fafalone/CheckBitness","commit_stats":null,"previous_names":["fafalone/checkbitness"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/fafalone/CheckBitness","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fafalone%2FCheckBitness","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fafalone%2FCheckBitness/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fafalone%2FCheckBitness/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fafalone%2FCheckBitness/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/fafalone","download_url":"https://codeload.github.com/fafalone/CheckBitness/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fafalone%2FCheckBitness/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":267294177,"owners_count":24065343,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-07-27T02:00:11.917Z","response_time":82,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["pe-file","pe-format","twinbasic","vb6"],"created_at":"2024-11-19T18:48:29.896Z","updated_at":"2026-02-06T09:01:41.348Z","avatar_url":"https://github.com/fafalone.png","language":"Visual Basic 6.0","readme":"# CheckBitness\nA simple utility to verify an executable is valid and check whether 32bit/64bit\n\nUpdate (03 Mar 2024): .twinproj has been updated to use a more recent version of WinDevLib (formerly tbShellLib) due to errors in the package tB did not raise at the time this project was released.\n\n![image](https://github.com/fafalone/CheckBitness/assets/7834493/a2193090-8835-45b4-bdbb-23df50a26218)\n\n\nSince twinBASIC supports 64bit, there's more of a reason than ever to check whether an exe or dll is 32bit or 64bit. I was playing around with PE file headers the past few days and decide to turn this into a little demo. There's not much to it, just pick a file, and it tells you if it's 32bit, 64bit AMD64, 64bit IA64, or 64bit ARM64 (for newer Windows on ARM devices); or lets you know if there's a different signature or it couldn't process the file at all.\n\n## How it works\n\n(Overview only, needs full code to run)\n\nThe key is a couple headers at the start of all Windows executable (PE format) files\n\n```vb6\nPublic Type IMAGE_DOS_HEADER ' DOS .EXE header\n    e_magic As Integer ' Magic number\n    e_cblp As Integer ' Bytes on last page of file\n    e_cp As Integer ' Pages in file\n    e_crlc As Integer ' Relocations\n    e_cparhdr As Integer ' Size of header in paragraphs\n    e_minalloc As Integer ' Minimum extra paragraphs needed\n    e_maxalloc As Integer ' Maximum extra paragraphs needed\n    e_ss As Integer ' Initial (relative) SS value\n    e_sp As Integer ' Initial SP value\n    e_csum As Integer ' Checksum\n    e_ip As Integer ' Initial IP value\n    e_cs As Integer ' Initial (relative) CS value\n    e_lfarlc As Integer ' File address of relocation table\n    e_ovno As Integer ' Overlay number\n    e_res(0 To 3) As Integer ' Reserved words\n    e_oemid As Integer ' OEM identifier (for e_oeminfo)\n    e_oeminfo As Integer ' OEM information; e_oemid specific\n    e_res2(0 To 9) As Integer ' Reserved words\n    e_lfanew As Long ' File address of new exe header\nEnd Type\n\nPublic Type IMAGE_FILE_HEADER\n    Machine As Integer\n    NumberOfSections As Integer\n    TimeDateStamp As Long\n    PointerToSymbolTable As Long\n    NumberOfSymbols As Long\n    SizeOfOptionalHeader As Integer\n    Characteristics As Integer\nEnd Type\n\nPublic Type IMAGE_NT_HEADERS\n    Signature As Long\n    FileHeader As IMAGE_FILE_HEADER\n    OptionalHeader As IMAGE_OPTIONAL_HEADER\nEnd Type\n```\n\n\u003e[!NOTE]\n\u003eThere's different `IMAGE_OPTIONAL_HEADER` types for 32 and 64bit because of pointer size differences, and subsequently different `IMAGE_NT_HEADERS`, but we don't worry about that for this check because we only need the `IMAGE_FILE_HEADER` and `IMAGE_DOS_HEADER`, which are the same on both 32bit and 64bit.\n\nAll of those definitions, and the omitted enums and constants, are included in tbShellLib. With those defs, here's what we do:\n\n1. Load the file with `CreateFile` and map it into memory with `CreateFileMapping/MapViewOfFile`\n2. The mapping gives us a base address, from which we copy the `IMAGE_DOS_HEADER`\n3. We check the magic number: It should be 'MZ', i.e. `IMAGE_DOS_SIGNATURE  = \u0026H5A4D`\n4. If it's correct, we proceed to add the value of `e_lfnew` to the base address; this is a relative address that tells us where the `IMAGE_NT_HEADERS` data is.\n5. Since the optional header differs between bitnesses and we don't need it, we copy only the `Signature` and `IMAGE_FILE_HEADER`\n6. The `Machine` member of the `IMAGE_FILE_HEADER` tells us what we want to know:\n\n   ```vb6\n   IMAGE_FILE_MACHINE_I386  = \u0026H014c  ' Intel 386.\n   IMAGE_FILE_MACHINE_AMD64  = \u0026H8664\u0026  ' AMD64 (K8)\n   IMAGE_FILE_MACHINE_IA64  = \u0026H0200  ' Intel 64\n   IMAGE_FILE_MACHINE_ARM64  = \u0026HAA64\u0026  ' ARM64 Little-Endian\n   ```\n   Those are the ones we're interested in; there's many others, but they're either not supported on Windows at all, or were only on very old limited release or unreleased versions. The program will provide the raw value if it's not one of the above.\n\n   The core function:\n\n   ```vb6\n       Private Function GetPEMachine(sFile As String) As Integer\n        Dim lpBaseAddress As LongPtr\n        Dim hFile As LongPtr\n        Dim hMapping As LongPtr\n    \n        hFile = CreateFileW(StrPtr(sFile), GENERIC_READ, FILE_SHARE_READ, vbNullPtr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0)\n\n        If hFile = INVALID_HANDLE_VALUE Then\n            AppendLog \"Failed to open file \" \u0026 sFile\n            Return -1\n        End If\n\n        hMapping = CreateFileMappingW(hFile, vbNullPtr, PAGE_READONLY, 0, 0, 0)\n\n        If hMapping = 0 Then\n            AppendLog \"Failed to map file \" \u0026 sFile\n            CloseHandle hFile\n            Return -1\n        End If\n\n        lpBaseAddress = MapViewOfFile(hMapping, FILE_MAP_READ, 0, 0, 0)\n    \n        If lpBaseAddress = 0 Then\n            AppendLog \"Failed to map file \" \u0026 sFile\n            CloseHandle hMapping\n            CloseHandle hFile\n            Return -1\n        End If\n        Dim tDOS As IMAGE_DOS_HEADER\n        Dim tNT As IMAGE_NT_HEADERS\n    \n        'All EXEs and DLLs start with an IMAGE_DOS_HEADER structure\n        CopyMemory tDOS, ByVal lpBaseAddress, LenB(tDOS)\n        If tDOS.e_magic = IMAGE_DOS_SIGNATURE Then 'The magic number, 'MZ', is a good sign our address gave us a valid executable\n            'e_lfanew points to the IMAGE_NT_HEADERS structure\n            'We only copy the Signature and IMAGE_FILE_HEADER, because the optional header is different depending\n            'on x86 or x64, and we don't it for this purpose.\n            CopyMemory tNT, ByVal PointerAdd(lpBaseAddress, tDOS.e_lfanew), LenB(tNT.FileHeader) + 4\n            UnmapViewOfFile lpBaseAddress\n            CloseHandle hMapping\n            CloseHandle hFile\n            Return tNT.FileHeader.Machine\n        Else\n            AppendLog \"Signature check failed, not a valid executable.\"\n        End If\n        UnmapViewOfFile lpBaseAddress\n        CloseHandle hMapping\n        CloseHandle hFile\n        Return -1\n    End Function\n   ```\n\n   I discovered something interesting while testing. While native Windows ARM64 executables have the ARM64 signature here, they don't appear to have it in memory when loaded for execution. The machine is AMD64 and you have to check a flag much deeper into the headers to tell them apart.\n\n   That's about it, I hope you enjoyed this little foray into EXE/DLL internal structure!\n   \n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffafalone%2Fcheckbitness","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ffafalone%2Fcheckbitness","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffafalone%2Fcheckbitness/lists"}