Vba 使用Apache POI检查Excel宏密码
您能否检查Excel中的宏代码是否受密码保护,甚至验证哪个密码?Vba 使用Apache POI检查Excel宏密码,vba,apache-poi,Vba,Apache Poi,您能否检查Excel中的宏代码是否受密码保护,甚至验证哪个密码? 我确实找到了关于密码保护的Excel工作簿和受保护的工作表的示例,但没有找到关于锁定宏代码的示例。对于重新设计的Office文档,您可以使用my,以便轻松查看嵌入元素的结构和位置 信息存储在vbaProject.bin的PROJECT流中。 这是一个很好的例子。 使用以下代码可以验证DPB元素(项目密码) import org.apache.commons.codec.DecoderException; import org.a
我确实找到了关于密码保护的Excel工作簿和受保护的工作表的示例,但没有找到关于锁定宏代码的示例。对于重新设计的Office文档,您可以使用my,以便轻松查看嵌入元素的结构和位置 信息存储在
vbaProject.bin
的PROJECT
流中。
这是一个很好的例子。
使用以下代码可以验证DPB
元素(项目密码)
import org.apache.commons.codec.DecoderException;
import org.apache.commons.codec.binary.Hex;
import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
import org.apache.poi.openxml4j.opc.PackagePart;
import org.apache.poi.openxml4j.opc.PackagePartName;
import org.apache.poi.openxml4j.opc.PackagingURIHelper;
import org.apache.poi.poifs.crypt.CryptoFunctions;
import org.apache.poi.poifs.crypt.HashAlgorithm;
import org.apache.poi.poifs.filesystem.POIFSFileSystem;
import org.apache.poi.util.LittleEndian;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.util.Arrays;
import java.util.Properties;
public class VBAProtect {
private static final String NO_PASSWORD_HASH = "0E0CD1ECDFF4E7F5E7F5E7";
public static void main(String[] args) throws DecoderException, IOException, InvalidFormatException {
try (FileInputStream is = new FileInputStream("vba-protected.xlsm");
XSSFWorkbook wb = new XSSFWorkbook(is)) {
PackagePartName pn = PackagingURIHelper.createPartName("/xl/vbaProject.bin");
PackagePart part = wb.getPackage().getPart(pn);
try (InputStream pis = part.getInputStream();
POIFSFileSystem ps = new POIFSFileSystem(pis);
InputStream dis = ps.createDocumentInputStream("PROJECT")) {
Properties prop = new Properties();
prop.load(dis);
String DPB = prop.getProperty("DPB").replace("\"", "");
if (NO_PASSWORD_HASH.equals(DPB)) {
System.out.println("no password");
} else {
EncData ed = new EncData();
ed.parse(DPB);
PasswordHash ph = new PasswordHash();
ph.parse(ed);
String pass = "password";
System.out.println("pass <" + pass + "> matches? " + ph.matches(pass));
}
}
}
}
public static class EncData {
public byte seed;
public byte version;
public byte projKey;
public int ignoredLength;
public int dataLength;
private byte[] raw;
public byte[] getData() {
byte[] data = new byte[dataLength];
// Header (3 bytes) + Ignored bytes + length (4 bytes)
System.arraycopy(raw, 3 + ignoredLength + 4, data, 0, dataLength);
return data;
}
public void parse(String data) throws DecoderException {
raw = Hex.decodeHex(data);
seed = raw[0];
byte VersionEnc = raw[1];
byte ProjKeyEnc = raw[2];
ignoredLength = ((seed & 6) / 2);
version = (byte)((seed ^ VersionEnc) & 0xFF);
projKey = (byte)((seed ^ ProjKeyEnc) & 0xFF);
byte UnencryptedByte1 = projKey;
byte EncryptedByte1 = ProjKeyEnc;
byte EncryptedByte2 = VersionEnc;
for (int offset = 3; offset < raw.length; offset++) {
byte ByteEnc = raw[offset];
byte Byte = (byte)((ByteEnc ^ (EncryptedByte2 + UnencryptedByte1)) & 0xFF);
EncryptedByte2 = EncryptedByte1;
EncryptedByte1 = ByteEnc;
raw[offset] = UnencryptedByte1 = Byte;
}
dataLength = LittleEndian.getInt(raw, 3 + ignoredLength);
}
}
public static class PasswordHash {
public final byte[] key = new byte[4];
public final byte[] passwordHash = new byte[20];
public void parse(EncData data) {
byte[] dpb = data.getData();
// first byte of grbit is reserved, so ignore it
int Grbit = LittleEndian.getInt(dpb, 0) >>> 8;
final int offset = 4;
for (int i=0; i<24; i++, Grbit >>>= 1) {
if ((Grbit & 1) == 0) {
dpb[offset+i] = 0;
}
}
System.arraycopy(dpb, offset, key, 0, 4);
System.arraycopy(dpb, offset+4, passwordHash, 0, 20);
}
public boolean matches(String password) {
// TODO: check MBCS windows encoding differences to UTF-8
// https://stackoverflow.com/questions/3298569/difference-between-mbcs-and-utf-8-on-windows
MessageDigest shaDig = CryptoFunctions.getMessageDigest(HashAlgorithm.sha1);
shaDig.update(password.getBytes(StandardCharsets.UTF_8));
shaDig.update(key);
byte[] dig = shaDig.digest();
return Arrays.equals(dig, passwordHash);
}
}
}
import org.apache.commons.codec.DecoderException;
导入org.apache.commons.codec.binary.Hex;
导入org.apache.poi.openxml4j.exceptions.InvalidFormatException;
导入org.apache.poi.openxml4j.opc.PackagePart;
导入org.apache.poi.openxml4j.opc.PackagePartName;
导入org.apache.poi.openxml4j.opc.PackagingURIHelper;
导入org.apache.poi.poifs.crypt.CryptoFunctions;
导入org.apache.poi.poifs.crypt.HashAlgorithm;
导入org.apache.poi.poifs.filesystem.poifsfsystem;
导入org.apache.poi.util.LittleEndian;
导入org.apache.poi.xssf.usermodel.xssf工作簿;
导入java.io.FileInputStream;
导入java.io.IOException;
导入java.io.InputStream;
导入java.nio.charset.StandardCharset;
导入java.security.MessageDigest;
导入java.util.array;
导入java.util.Properties;
公共类VBA保护{
私有静态最终字符串NO_PASSWORD_HASH=“0E0CD1ECDFF4E7F5E7F5E7”;
公共静态void main(字符串[]args)引发DecodeException、IOException、InvalidFormatException{
try(FileInputStream=newfileinputstream(“vba protected.xlsm”);
XSSF工作簿wb=新XSSF工作簿(is)){
PackagePartName pn=PackagingURIHelper.createPartName(“/xl/vbaProject.bin”);
PackagePart部分=wb.getPackage().getPart(pn);
try(InputStream pis=part.getInputStream();
POIFSFISTEM ps=新的POIFSFISTEM(pis);
InputStream dis=ps.createDocumentInputStream(“项目”)){
Properties prop=新属性();
道具载荷(dis);
字符串DPB=prop.getProperty(“DPB”)。替换(“\”,”);
if(无密码\u散列等于(DPB)){
System.out.println(“无密码”);
}否则{
EncData ed=新的EncData();
ed.parse(DPB);
PasswordHash ph=新的PasswordHash();
理学博士(教育);
字符串pass=“password”;
System.out.println(“通过匹配?”+ph.matches(通过));
}
}
}
}
公共静态类数据{
公共字节种子;
公共字节版本;
公共字节projKey;
公共输入信号长度;
公共int数据长度;
私有字节[]原始;
公共字节[]getData(){
字节[]数据=新字节[dataLength];
//标题(3字节)+忽略的字节+长度(4字节)
System.arraycopy(原始,3+忽略数据长度+4,数据,0,数据长度);
返回数据;
}
公共void解析(字符串数据)抛出DecoderException{
原始=十六进制。解码十六进制(数据);
种子=原始[0];
字节VersionEnc=raw[1];
字节projkeync=raw[2];
ignoredLength=((种子和6)/2);
版本=(字节)((种子^VersionEnc)和0xFF);
projKey=(字节)((种子^projkeync)和0xFF);
字节未加密字节1=projKey;
byte EncryptedByte1=ProjKeyEnc;
字节加密字节2=VersionEnc;
对于(int offset=3;offset>8;
最终整数偏移=4;
对于(int i=0;i>>=1){
如果((Grbit&1)==0){
dpb[offset+i]=0;
}
}
系统阵列复制(dpb,偏移量,键,0,4);
数组复制(dpb,偏移量+4,密码哈希,0,20);
}
公共布尔匹配(字符串密码){
//TODO:检查MBCS windows编码与UTF-8的差异
// https://stackoverflow.com/questions/3298569/difference-between-mbcs-and-utf-8-on-windows
MessageDigest shaDig=CryptoFunctions.getMessageDigest(HashAlgorithm.sha1);
update(password.getBytes(StandardCharsets.UTF_8));
shaDig.update(键);
字节[]dig=shaDig.digest();
返回数组.equals(dig,passwordHash);
}
}
}
对于重新设计的Office文档,您可以使用my,以便轻松查看嵌入元素的结构和位置
信息存储在vbaProject.bin
的PROJECT
流中。
这是一个很好的例子。
使用以下代码可以验证DPB
元素(项目密码)
import org.apache.commons.codec.DecoderException;
import org.apache.commons.codec.binary.Hex;
import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
import org.apache.poi.openxml4j.opc.PackagePart;
import org.apache.poi.openxml4j.opc.PackagePartName;
import org.apache.poi.openxml4j.opc.PackagingURIHelper;
import org.apache.poi.poifs.crypt.CryptoFunctions;
import org.apache.poi.poifs.crypt.HashAlgorithm;
import org.apache.poi.poifs.filesystem.POIFSFileSystem;
import org.apache.poi.util.LittleEndian;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.util.Arrays;
import java.util.Properties;
public class VBAProtect {
private static final String NO_PASSWORD_HASH = "0E0CD1ECDFF4E7F5E7F5E7";
public static void main(String[] args) throws DecoderException, IOException, InvalidFormatException {
try (FileInputStream is = new FileInputStream("vba-protected.xlsm");
XSSFWorkbook wb = new XSSFWorkbook(is)) {
PackagePartName pn = PackagingURIHelper.createPartName("/xl/vbaProject.bin");
PackagePart part = wb.getPackage().getPart(pn);
try (InputStream pis = part.getInputStream();
POIFSFileSystem ps = new POIFSFileSystem(pis);
InputStream dis = ps.createDocumentInputStream("PROJECT")) {
Properties prop = new Properties();
prop.load(dis);
String DPB = prop.getProperty("DPB").replace("\"", "");
if (NO_PASSWORD_HASH.equals(DPB)) {
System.out.println("no password");
} else {
EncData ed = new EncData();
ed.parse(DPB);
PasswordHash ph = new PasswordHash();
ph.parse(ed);
String pass = "password";
System.out.println("pass <" + pass + "> matches? " + ph.matches(pass));
}
}
}
}
public static class EncData {
public byte seed;
public byte version;
public byte projKey;
public int ignoredLength;
public int dataLength;
private byte[] raw;
public byte[] getData() {
byte[] data = new byte[dataLength];
// Header (3 bytes) + Ignored bytes + length (4 bytes)
System.arraycopy(raw, 3 + ignoredLength + 4, data, 0, dataLength);
return data;
}
public void parse(String data) throws DecoderException {
raw = Hex.decodeHex(data);
seed = raw[0];
byte VersionEnc = raw[1];
byte ProjKeyEnc = raw[2];
ignoredLength = ((seed & 6) / 2);
version = (byte)((seed ^ VersionEnc) & 0xFF);
projKey = (byte)((seed ^ ProjKeyEnc) & 0xFF);
byte UnencryptedByte1 = projKey;
byte EncryptedByte1 = ProjKeyEnc;
byte EncryptedByte2 = VersionEnc;
for (int offset = 3; offset < raw.length; offset++) {
byte ByteEnc = raw[offset];
byte Byte = (byte)((ByteEnc ^ (EncryptedByte2 + UnencryptedByte1)) & 0xFF);
EncryptedByte2 = EncryptedByte1;
EncryptedByte1 = ByteEnc;
raw[offset] = UnencryptedByte1 = Byte;
}
dataLength = LittleEndian.getInt(raw, 3 + ignoredLength);
}
}
public static class PasswordHash {
public final byte[] key = new byte[4];
public final byte[] passwordHash = new byte[20];
public void parse(EncData data) {
byte[] dpb = data.getData();
// first byte of grbit is reserved, so ignore it
int Grbit = LittleEndian.getInt(dpb, 0) >>> 8;
final int offset = 4;
for (int i=0; i<24; i++, Grbit >>>= 1) {
if ((Grbit & 1) == 0) {
dpb[offset+i] = 0;
}
}
System.arraycopy(dpb, offset, key, 0, 4);
System.arraycopy(dpb, offset+4, passwordHash, 0, 20);
}
public boolean matches(String password) {
// TODO: check MBCS windows encoding differences to UTF-8
// https://stackoverflow.com/questions/3298569/difference-between-mbcs-and-utf-8-on-windows
MessageDigest shaDig = CryptoFunctions.getMessageDigest(HashAlgorithm.sha1);
shaDig.update(password.getBytes(StandardCharsets.UTF_8));
shaDig.update(key);
byte[] dig = shaDig.digest();
return Arrays.equals(dig, passwordHash);
}
}
}
import org.apache.commons.codec.DecoderException;
导入org.apache.commons.codec.binary.Hex;
导入org.apache.poi.openxml4j.exceptions.InvalidFormatException;
导入org.apache.poi.openxml4j.opc.PackagePart;
导入org.apach