/** * Copyright 2012 Emmanuel Bourg * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package net.jsign.pe; import java.util.zip.Checksum; /** * Compute the checksum of a portable executable similarly to the checksum * function implemented into IMAGHELP.DLL. The checksum can only be updated * with buffers with a size that is a multiple of 4. * * @see <a href="http://msdn.microsoft.com/en-us/library/windows/desktop/ms680181%28v=vs.85%29.aspx">ImageHlp Functions</a> * @see <a href="http://www.codeproject.com/KB/cpp/PEChecksum.aspx">An Analysis of the Windows PE Checksum Algorithm</a> * * @author Emmanuel Bourg * @version $Revision$, $Date$ */ class PEImageChecksum implements Checksum { private static final long MAX_UNSIGNED_INT = 0x100000000L; /** The checksum being computed */ private long checksum; /** The absolute position in the stream */ private long position; /** The position of the checksum field in the stream that must be skipped */ private final long checksumOffset; /** Tells if the checksum has already been skipped */ private boolean checksumOffsetSkipped; public PEImageChecksum(long checksumOffset) { this.checksumOffset = checksumOffset; } public void update(int b) { throw new UnsupportedOperationException("Checksum can only be updated with buffers"); } public void update(byte[] buffer, int offset, int length) { long checksum = this.checksum; for (int i = offset; i < offset + length; i += 4) { if (!checksumOffsetSkipped && position + i == checksumOffset) { // skip the checksum field checksumOffsetSkipped = true; } else { long dword = (buffer[i] & 0xFF) + ((buffer[i + 1] & 0xFF) << 8) + ((buffer[i + 2] & 0xFF) << 16) + ((buffer[i + 3] & 0xFFL) << 24); checksum += dword; if (checksum > MAX_UNSIGNED_INT) { // fold into 32 bits checksum = (checksum & 0xFFFFFFFFL) + (checksum >> 32); } } } this.checksum = checksum; position += length - offset; } public long getValue() { long checksum = this.checksum; // fold twice into 16 bits checksum = (checksum >> 16) + (checksum & 0xFFFF); checksum = (checksum >> 16) + checksum; // keep the lower 16 bits and add the file length return (checksum & 0xFFFF) + position; } public void reset() { checksum = 0; position = 0; checksumOffsetSkipped = false; } }