Skip to content

Commit 69dedcf

Browse files
Konstantin-Kretovww898
authored andcommitted
NP-1964 [java] The PE hash is calculated in chunks of 1Mb
1 parent 7119ea3 commit 69dedcf

File tree

1 file changed

+40
-27
lines changed
  • jvm/src/main/kotlin/com/jetbrains/signatureverifier

1 file changed

+40
-27
lines changed

jvm/src/main/kotlin/com/jetbrains/signatureverifier/PeFile.kt

Lines changed: 40 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,6 @@ class PeFile {
1414
private val _signData: DataInfo
1515
private val _dotnetMetadata: DataInfo
1616

17-
private val RawPeData: ByteArray by lazy { rawPeData() }
18-
1917
val ImageDirectoryEntrySecurityOffset: Int
2018
get() = _imageDirectoryEntrySecurity.Offset
2119

@@ -108,48 +106,63 @@ class PeFile {
108106
* @param algName Name of the hashing algorithm
109107
* */
110108
fun ComputeHash(@NotNull algName: String): ByteArray {
111-
val data = RawPeData
112109
val hash = MessageDigest.getInstance(algName)
113110

114-
//hash from start to checksum field
111+
fun hashRange(startOffset: Long, length: Long) {
112+
if (length <= 0L) return
113+
val fileSize = _stream.size()
114+
val safeStart = startOffset.coerceAtLeast(0L).coerceAtMost(fileSize)
115+
val maxLen = (fileSize - safeStart).coerceAtLeast(0L)
116+
val safeLen = length.coerceAtMost(maxLen)
117+
if (safeLen <= 0L) return
118+
119+
val buffer = ByteArray(1024 * 1024) // 1MB chunks
120+
var remaining = safeLen
121+
_stream.Seek(safeStart, SeekOrigin.Begin)
122+
while (remaining > 0) {
123+
val toRead = if (remaining < buffer.size) remaining.toInt() else buffer.size
124+
val bytesRead = _stream.read(java.nio.ByteBuffer.wrap(buffer, 0, toRead))
125+
if (bytesRead <= 0) break
126+
hash.update(buffer, 0, bytesRead)
127+
remaining -= bytesRead
128+
}
129+
}
130+
131+
val fileSize = _stream.size().toInt()
132+
133+
// 1) Hash from start to checksum field (exclusive)
115134
var offset = 0
116135
var count = _checkSum.Offset
117-
hash.update(data, offset, count)
136+
hashRange(offset.toLong(), count.toLong())
118137

119-
//jump over checksum and hash to IMAGE_DIRECTORY_ENTRY_SECURITY
138+
// 2) Skip checksum field, hash up to IMAGE_DIRECTORY_ENTRY_SECURITY (exclusive)
120139
offset = count + _checkSum.Size
121140
count = _imageDirectoryEntrySecurity.Offset - offset
122-
hash.update(data, offset, count)
141+
hashRange(offset.toLong(), count.toLong())
123142

124-
//jump over IMAGE_DIRECTORY_ENTRY_SECURITY
143+
// 3) Skip IMAGE_DIRECTORY_ENTRY_SECURITY itself (8 bytes)
125144
offset = _imageDirectoryEntrySecurity.Offset + _imageDirectoryEntrySecurity.Size
126145

127-
if (_signData.IsEmpty) // PE is not signed
128-
{
129-
//hash to EOF
130-
count = data.count() - offset
131-
hash.update(data, offset, count)
146+
if (_signData.IsEmpty) {
147+
// 4a) Not signed: hash to EOF
148+
count = fileSize - offset
149+
hashRange(offset.toLong(), count.toLong())
132150
} else {
133-
//PE is signed
151+
// 4b) Signed: hash up to the start of signature data
134152
count = _signData.Offset - offset
153+
if (offset + count <= fileSize) {
154+
hashRange(offset.toLong(), count.toLong())
155+
}
135156

136-
//hash to start the signature data
137-
if ((offset + count) <= data.count())
138-
hash.update(data, offset, count)
139-
140-
//jump over the signature data and hash all the rest
157+
// 5) Jump over signature data and hash the rest to EOF
141158
offset = _signData.Offset + _signData.Size
142-
count = data.count() - offset
143-
144-
if (count > 0)
145-
hash.update(data, offset, count)
159+
count = fileSize - offset
160+
if (count > 0) {
161+
hashRange(offset.toLong(), count.toLong())
162+
}
146163
}
147164

148165
return hash.digest()
149166
}
150-
151-
private fun rawPeData(): ByteArray {
152-
return _stream.Rewind().ReadToEnd()
153-
}
154167
}
155168

0 commit comments

Comments
 (0)