最近遇到要翻某个公司的pacs,但是他们的影像是加密的,如果不解密的话,历史影像是不能正常解析.破解这个历史影像这个任务只能含泪接下啦.
他们用的是dcm4che,但是这个开源的版本系列也很多了,没法确定用的哪个版本的源码.只能把源码解析流程一起分析了.
1.入口
正常解析dicom文件的流程 一般如下
File destFile = new File("F:\\00463981_test.dcm"); try(DicomOutputStream dos = new DicomOutputStream(destFile); DicomInputStream inputStream = new DicomInputStream(srcFile)) { Attributes dataset = inputStream.readDataset(-1,-1);
/ /到这里截止,发现取出来的字段乱码.... 问题在上部分
............
}
2 .DicomInputStream inputStream = new DicomInputStream(srcFile))
这个方法构造,干了些什么呢
先介绍一下dicom文件存储结构:
DataElement是存储文件信息的主体
Tag由2 Byte的组号和2 Byte的元素号组成,VR是DICOM特有的值表示法,按照VR的类型以及是否显示VR,隐式VR.
上面那个函数,除 this.guessTransferSyntax(); 其他的都是初始变量. 这个方法猜测是处理传输协议的,我们进去看看.
3. this.guessTransferSyntax()
这里在处理dicm 前面的导言和"dicom" 这里就是132个字节了, 然后从这个128导言中取信息知道dicom 是大端还小端存储,是隐式还是显式.
/** Verification SOP Class, */ public static final String VerificationSOPClass = "1.2.840.10008.1.1"; /** Implicit VR Little Endian, */ public static final String ImplicitVRLittleEndian = "1.2.840.10008.1.2"; /** Explicit VR Little Endian, */ public static final String ExplicitVRLittleEndian = "1.2.840.10008.1.2.1";
这部分只是处理简单大端小端,没用处理数据,那我们再看看 Attributes dataset = inputStream.readDataset(-1,-1);
4.readDataset(-1,-1)
看这个方法名,猜测是解析数据集
是的,就是读数据集,
this.readFileMetaInformation(); 读文件头信息,这里就代大家看了
课代表汇报:
这个方法主要是读文件头,(1)刚才构造方法读成功了就不处理了 (2)dcm文件不完整,缺失这部分,会试着去先看看tag字段的是不是符合dicom字段的规定(dicom字段一般分元素号+组号),类型是不是dicom可以解析字段类型.如果可以解析的话,试着把猜测的传输协议设置进去.
this.readAttributes(attrs, len, stopTag);
读dcm属性集,这里继续代大家看了
课代表汇报:
1. 这里就一直按先去读dcm的tag字段(元素号+组号) ,确定tag类型(每种类型长度不同)
3.然后去读值(根据长度去读值),
(tag类型2019的标准中是34种,相比27种多加了OD、OL、OV、SV、UC、UR、UV),
特殊的是 sq 条目序列,值是零个或多个项的序列
un 未知的,可能是多个项的序列
常规把这些看成一个序列(如果是上面的特殊的,看成一个个属性attributes循环又去处理),读相应的字节就好了.
红色标的就是常规的序列.
整体的解析就是这样的.(个人理解,如果有错,请见谅)
后面破解之后,才发现加密的dcm是对组号和元素号以某个key进行偏移,然后value也是以这个key进行移位.