Skip to content

Commit be4add0

Browse files
committed
add initial project
1 parent d37526a commit be4add0

File tree

11 files changed

+5046
-0
lines changed

11 files changed

+5046
-0
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
/node_modules

README.md

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
# windows-binary-architecture
2+
3+
Find the target CPU architecture of Windows binaries (DLLs, EXEs and others).
4+
5+
The module exposes a single function, `getTargetArch(path, callback(err,
6+
archName, archCode))` which opens an executable file and determines what CPU
7+
architecture the file was built for, by calling your callback with a name for
8+
the architecture and its code, according to the table on [the Windows
9+
documentation](https://docs.microsoft.com/en-us/windows/win32/debug/pe-format#machine-types),
10+
reproduced here.
11+
12+
|Name|Value|Description|
13+
|--|--|--|
14+
|UNKNOWN| 0x0| The contents of this field are assumed to be applicable to any machine type|
15+
|AM33| 0x1d3| Matsushita AM33|
16+
|AMD64| 0x8664| x64|
17+
|ARM| 0x1c0| ARM little endian|
18+
|ARM64| 0xaa64| ARM64 little endian|
19+
|ARMNT| 0x1c4| ARM Thumb-2 little endian|
20+
|EBC| 0xebc| EFI byte code|
21+
|I386| 0x14c| Intel 386 or later processors and compatible processors|
22+
|IA64| 0x200| Intel Itanium processor family|
23+
|M32R| 0x9041| Mitsubishi M32R little endian|
24+
|MIPS16| 0x266| MIPS16|
25+
|MIPSFPU| 0x366| MIPS with FPU|
26+
|MIPSFPU16| 0x466| MIPS16 with FPU|
27+
|POWERPC| 0x1f0| Power PC little endian|
28+
|POWERPCFP| 0x1f1| Power PC with floating point support|
29+
|R4000| 0x166| MIPS little endian|
30+
|RISCV32| 0x5032| RISC-V 32-bit address space|
31+
|RISCV64| 0x5064| RISC-V 64-bit address space|
32+
|RISCV128| 0x5128| RISC-V 128-bit address space|
33+
|SH3| 0x1a2| Hitachi SH3|
34+
|SH3DSP| 0x1a3| Hitachi SH3 DSP|
35+
|SH4| 0x1a6| Hitachi SH4|
36+
|SH5| 0x1a8| Hitachi SH5|
37+
|THUMB| 0x1c2| Thumb|
38+
|WCEMIPSV2| 0x169| MIPS little-endian WCE v2 |
39+
|`null`|| Unknown architecture, name is `null` and the architecture code is sent to the callback as-is|

index.js

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
'use strict'
2+
3+
var fs = require('fs')
4+
var path = require('path')
5+
6+
module.exports = getTargetArch
7+
8+
// DWORD for offset of PE signature; see
9+
// https://docs.microsoft.com/en-us/windows/win32/debug/pe-format#ms-dos-stub-image-only
10+
var SIGNATURE_OFFSET_OFFSET = 0x3c
11+
12+
function getTargetArch(path, callback) {
13+
fs.open(path, 'r', function(err, fd) {
14+
if (err) return callback(err)
15+
readMagic(fd, function(err, archName, archCode) {
16+
fs.close(fd)
17+
callback(err, archName, archCode)
18+
})
19+
})
20+
}
21+
22+
function readMagic(fd, callback) {
23+
var buffer = Buffer.alloc(4)
24+
fs.read(fd, buffer, 0, 2, 0, function(err, bytesRead) {
25+
if (err) return callback(err)
26+
if (bytesRead !== 2) return callback(new Error('failed to read MZ magic'))
27+
var magic = buffer.readInt16BE()
28+
var bigEndian = null
29+
if (magic === 0x5a4d)
30+
bigEndian = true
31+
else if (magic === 0x4d5a)
32+
bigEndian = false
33+
else
34+
return callback(new Error('not a DOS-MZ file'))
35+
readTargetArch(fd, bigEndian, callback)
36+
})
37+
}
38+
39+
function readTargetArch(fd, bigEndian, callback) {
40+
var buffer = Buffer.alloc(4)
41+
fs.read(fd, buffer, 0, 4, SIGNATURE_OFFSET_OFFSET, function(err, bytesRead) {
42+
if (err) return callback(err)
43+
if (bytesRead !== 4) return callback(new Error('failed to read PE signature offset'))
44+
var signatureOffset = bigEndian ? buffer.readUInt32BE() : buffer.readUInt32LE()
45+
var targetArchOffset = signatureOffset + 4
46+
fs.read(fd, buffer, 0, 2, targetArchOffset, function(err, bytesRead) {
47+
if (err) return callback(err)
48+
if (bytesRead !== 2) return callback(new Error('failed to read PE architecture field'))
49+
var targetArchCode = bigEndian ? buffer.readUInt16BE() : buffer.readUInt16LE()
50+
var targetArchName = architectureNameByCode(targetArchCode)
51+
callback(null, targetArchName, targetArchCode)
52+
})
53+
})
54+
}
55+
56+
// see https://docs.microsoft.com/en-us/windows/win32/debug/pe-format#machine-types
57+
function architectureNameByCode(code) {
58+
switch (code) {
59+
case 0x0:
60+
return 'UNKNOWN'
61+
case 0x1d3:
62+
return 'AM33'
63+
case 0x8664:
64+
return 'AMD64'
65+
case 0x1c0:
66+
return 'ARM'
67+
case 0xaa64:
68+
return 'ARM64'
69+
case 0x1c4:
70+
return 'ARMNT'
71+
case 0xebc:
72+
return 'EBC'
73+
case 0x14c:
74+
return 'I386'
75+
case 0x200:
76+
return 'IA64'
77+
case 0x9041:
78+
return 'M32R'
79+
case 0x266:
80+
return 'MIPS16'
81+
case 0x366:
82+
return 'MIPSFPU'
83+
case 0x466:
84+
return 'MIPSFPU16'
85+
case 0x1f0:
86+
return 'POWERPC'
87+
case 0x1f1:
88+
return 'POWERPCFP'
89+
case 0x166:
90+
return 'R4000'
91+
case 0x5032:
92+
return 'RISCV32'
93+
case 0x5064:
94+
return 'RISCV64'
95+
case 0x5128:
96+
return 'RISCV128'
97+
case 0x1a2:
98+
return 'SH3'
99+
case 0x1a3:
100+
return 'SH3DSP'
101+
case 0x1a6:
102+
return 'SH4'
103+
case 0x1a8:
104+
return 'SH5'
105+
case 0x1c2:
106+
return 'THUMB'
107+
case 0x169:
108+
return 'WCEMIPSV2'
109+
default:
110+
return null
111+
}
112+
}

index.test.js

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
var getTargetArch = require('./index.js')
2+
3+
describe(getTargetArch, function() {
4+
it.each([
5+
['I386', 0x14c],
6+
['AMD64', 0x8664],
7+
])('detects %s', function(expectedArchName, expectedArchCode, done) {
8+
getTargetArch('./test.' + expectedArchName + '.dll', function(err, actualArchName, actualArchCode) {
9+
expect(err).toBeNull()
10+
expect(actualArchCode).toBe(expectedArchCode)
11+
expect(actualArchName).toBe(expectedArchName)
12+
done()
13+
})
14+
})
15+
16+
it('handles big-endian', function(done) {
17+
getTargetArch('./test.big-endian.dll', function(err, architectureName, architectureCode) {
18+
expect(err).toBeNull()
19+
expect(architectureName).toBe('MIPS16')
20+
expect(architectureCode).toBe(0x266)
21+
done()
22+
})
23+
})
24+
25+
it('errors when file does not exist', function(done) {
26+
getTargetArch('./nothere', function(err) {
27+
expect(err).not.toBeNull()
28+
done()
29+
})
30+
})
31+
32+
it('errors when MZ magic cannot be read', function(done) {
33+
getTargetArch('./test.no-mz.dll', function(err) {
34+
expect(String(err)).toBe('Error: failed to read MZ magic')
35+
done()
36+
})
37+
})
38+
39+
it('errors when MZ magic is incorrect', function(done) {
40+
getTargetArch('./test.bad-mz.dll', function(err) {
41+
expect(String(err)).toBe('Error: not a DOS-MZ file')
42+
done()
43+
})
44+
})
45+
})

0 commit comments

Comments
 (0)