Open rainit2006 opened 7 years ago
if (string.IsNullOrEmpty(strInputPath) &&
string.IsNullOrEmpty(strOutputPath))
{
strInputPath = AppDomain.CurrentDomain.BaseDirectory;
strOutputPath = Path.Combine(strInputPath, "output.csv");
strInputPath.TrimEnd('\\');
}
IDictionary インターフェイス Represents a nongeneric collection of key/value pairs.
IDictionary<string, IDictionary<string, long>> fileMap = new Dictionary<string, IDictionary<string, long>>();
IDictionary<string, long> folder = new Dictionary<string, long>();
for (int iFileCounter = 0; iFileCounter < iFileCount; ++iFileCounter)
{
int iFileNameLength = reader.ReadInt32() * sizeof(char);
long lFileSize = reader.ReadInt32();
byte[] bufferFileName = new byte[iFileNameLength];
reader.Read(bufferFileName, 0, iFileNameLength);
folder.Add(Encoding.Unicode.GetString(bufferFileName), lFileSize); //把文件名称和size信息放入folder里
}
fileMap.Add(Encoding.Unicode.GetString(bufferFolderName), folder); //再把目录名称和folder变量放入fileMap变量里。
读取Pak-ma的Binary文件信息 需要根据Pak-ma内部结构来读取相应的信息。
IDictionary<string, IDictionary<string, long>> fileMap = new Dictionary<string, IDictionary<string, long>>();
try
{
using (BinaryReader reader = new BinaryReader(File.OpenRead(file.FullName)))
{
reader.BaseStream.Seek(32, SeekOrigin.Begin);
//这是因为Pak-ma文件的0x0020(即32)记录了Preload Data开始的address信息。
long lOffset = reader.ReadInt64();
if (lOffset <= 0)
{
Console.Out.WriteLine("Not a valid PackMan package file.");
return;
}
reader.BaseStream.Seek(lOffset, SeekOrigin.Begin);
int iFolderCount = reader.ReadInt32();
for (int iFolderCounter = 0; iFolderCounter < iFolderCount; ++iFolderCounter)
{
int iFolderNameLength = reader.ReadInt32() * sizeof(char);
int iFileCount = reader.ReadInt32();
byte[] bufferFolderName = new byte[iFolderNameLength];
reader.Read(bufferFolderName, 0, iFolderNameLength);
IDictionary<string, long> folder = new Dictionary<string, long>();
for (int iFileCounter = 0; iFileCounter < iFileCount; ++iFileCounter)
{
int iFileNameLength = reader.ReadInt32() * sizeof(char);
long lFileSize = reader.ReadInt32();
byte[] bufferFileName = new byte[iFileNameLength];
reader.Read(bufferFileName, 0, iFileNameLength);
folder.Add(Encoding.Unicode.GetString(bufferFileName), lFileSize);
}
fileMap.Add(Encoding.Unicode.GetString(bufferFolderName), folder);
}
FileInfo(strFile).Length.ToString('D');
//关于ToString参考 https://dobon.net/vb/dotnet/string/inttostring.html
判断文件是否存在
File.Exists(strFilePath)
写数据到cvs文件里
writer = new StreamWriter(strFilePath, true, new UTF8Encoding(true));
//第二个参数代表是否append。true:ファイルが存在する場合は、ファイルの末尾に追加できます。
//false: ファイルを上書きします。
//指定されたファイルが存在しない場合、このパラメーターは無効であり、コンストラクターは新しいファイルを作成します。
writer.WriterLine(string.Join(",", new string[]{
"data1", "data2", "data3", ....
}));
writer.Close(); wirter.Dispose();
- 从字符串解析成version对象
Version.tryParse ("2.1.0.0330", version);
这样version对象里包含了メジャー バージョン番号、マイナー バージョン番号、ビルド番号、およびリビジョン番号情报。
从IMAGE_FILE_HEADER構造体里取“TimeDateStamp”
private DateTime GetBuildTime()
{
string filePath = System.Reflection.Assembly.GetCallingAssembly().Location;
const int c_PeHeaderOffset = 60;
const int c_LinkerTimestampOffset = 8;
byte[] b = new byte[2048];
System.IO.Stream s = null;
try
{
s = new System.IO.FileStream(filePath, System.IO.FileMode.Open, System.IO.FileAccess.Read);
s.Read(b, 0, 2048);
}
finally
{
if (s != null)
{
s.Close();
}
}
int i = System.BitConverter.ToInt32(b, c_PeHeaderOffset);
int secondsSince1970 = System.BitConverter.ToInt32(b, i + c_LinkerTimestampOffset);
DateTime dt = new DateTime(1970, 1, 1, 0, 0, 0);
dt = dt.AddSeconds(secondsSince1970);
dt = dt.AddHours(TimeZone.CurrentTimeZone.GetUtcOffset(dt).Hours);
return dt;
}
弹出文件选择对话框 Key: Microsoft.Win32.OpenFileDialog
关于RelayCommand类的实现可参照: http://sourcechord.hatenablog.com/entry/2014/01/13/200039
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
RelayCommand _browseFileCommand;
public ICommand BrowseFileCommand
{
get
{
if (this._browseFileCommand == null)
{
this._browseFileCommand = new RelayCommand(
(o) =>
{
var dialog = new Microsoft.Win32.OpenFileDialog();
dialog.ShowReadOnly = true;
dialog.Filter = "Executable files (.exe)|*.exe|All files (*.*)|*.*";
dialog.FilterIndex = 0;
var result = dialog.ShowDialog();
if (result == true)
{
this.FilePath = dialog.FileName;
}
},
(o) =>
{
return !this.IsProcessing;
});
this.PropertyChanged += (s, e) =>
{
if (e.PropertyName == nameof(IsProcessing))
{
this._browseFileCommand.RaiseCanExecuteChanged();
}
};
}
return this._browseFileCommand;
}
}
关于DebuggerBrowsable(DebuggerBrowsableState.Never) http://qiita.com/tomochan154/items/b7a7f99a60fa2d98cc1b 让某些不关注属性可以在debug时不在watch widnow里可见。
关于Binary文件的构造 https://codezine.jp/article/detail/403
MZシグネチャ EXEファイルをメモ帳で開いてみると、必ず先頭部分に「MZ」という文字列があります。これは、EXEファイルであることを裏付けるためのデータであり、シグネチャと呼ばれるものです。特に深い意味はなく、署名のようなものだと思えば良いでしょう。
マシンタイプ EXEファイルがどのようなマシン上での動作を想定しているのか、次のようなマシンを識別するためのフラグが格納されています。
Intel 386以降およびその互換プロセッサ
MIPS(r)リトルエンディアン
Alpha AXP(tm)
Motorola 68000シリーズ
Power PC、リトルエンディアン
日立SH3
日立SH4
ネイティブコード ネイティブコードとは機械語データのことを指します。プログラムの実行手順が書かれており、このネイティブコード部分がソフトウェアの中核になります。一般的にはx86に対応する機械語命令が並んでいます。
あくまで機械語なので、アセンブラではありません。メモ帳やバイナリエディタで開いても意味不明な文字の羅列しか表示されませんので、アセンブリソースを閲覧されたい方は、逆アセンブルを行う必要があります。
リソース アプリケーションにはそれぞれ独自のアイコンが用意されていますが、それらのアイコンは、EXEファイルの中のリソース部に格納されています。ここには画像や音楽、その他、さまざまなデータを格納することができます。
EXEファイルのロード方法 OSは、先ほど説明したMZシグネチャ、マシンタイプ、ネイティブコードなどの各情報をもとに、EXEファイルをメモリ上にロードし実行します。OSがEXEファイルを実行するまでの手順は次のようになります。 1,シグネチャ、マシンタイプなどを確認します。EXEファイルが正規のフォーマットで記載されているかをチェックします(なお、このタイミングでデータが間違っているとデバッグすら実行しません)。 2,EXEイメージをメモリ上にコピーします。 3,PEヘッダ(或NEヘッダ)に記載されているデータをもとに、EXEイメージの初期化を行います。 4, PEヘッダで指定されたスタートポジション(エントリポイント)からプログラムを実行します。
重要なメモリ指定概念「RVA」 EXEファイルに記載されるデータの中に、RVAというものがよく出現します。EXEファイルイメージは実行前にプロセスメモリ上の任意の場所にロードされますが、その任意の場所、すなわち、EXEイメージが読み込まれたメモリの先頭位置を「イメージベース」と呼びます。RVAとは「Relative Virtual Address」の略で、イメージベースからの相対オフセット値のことを言います。
イメージベース + RVA = アドレス
IMAGE_DOS_HEADER構造体の中にある情報は、現在主流であるWindowsであまり扱われません。そのため、ほとんどが意味を持ちません。しかし、次に挙げる情報は重要なので、チェックしておきましょう。
e_magicメンバ …… 必ず0x5A4Dを保持しています
e_lfanewメンバ …… PEヘッダ(或NEヘッダ)が格納されているファイル位置を保持します
`NT ヘッダーの位置を得るのには、ImageNtHeader という関数を使うこともできます。`
-- MS-DOS用スタブ 万が一、MS-DOS上でプログラムを動作させてしまった場合に、MS-DOSのプログラムを実行するためのネイティブコードを保存できるスペースです。MS-DOS用スタブは、IMAGE_DOS_HEADERの直後に配置されます。 PEヘッダはWindowsに対応しているデータなので、MS-DOSでは読み込むことができません。MS-DOSで解釈可能なのは、IMAGE_DOS_HEADER構造体と、それに続くネイティブコード、つまり、このMS-DOS用スタブになります。
このスタブデータは、一般的には「This program cannot be run in DOS mode.」という文字列を表示して終了するプログラムが保持されていますが、MS-DOS用にカスタマイズされたプログラムをこのスペースに格納しておいても問題はありません。
-- NULL空間 一般的には、0x0100バイト目までNULL空間が存在し、0x0100バイト目からPEヘッダが配置されます。
-- PEヘッダ
特殊目录
Environment.SpecialFolder.ProgramFiles
Environment.SpecialFolder.ProgramFilesX86
Environment.SpecialFolder.ProgramFiles
Environment.SpecialFolder.ProgramFilesX86
验证签名是否有效
关键: WintrustData WinVerifyTrust函数
GUID GenericActionId = WINTRUST_ACTION_GENERIC_VERIFY_V2;
// WinVerifyTrust verifies signatures as specified by the GUID
// and Wintrust_Data.
lStatus = WinVerifyTrust(
NULL,
&GenericActionId,
&WinTrustData);
// Now attempt to verify all secondary signatures that were found
for(DWORD x = 1; x <= WintrustData.pSignatureSettings->cSecondarySigs; x++)
{
wprintf(L"Verify secondary signature at index %d... ", x);
// Need to clear the previous state data from the last call to WinVerifyTrust
WintrustData.dwStateAction = WTD_STATEACTION_CLOSE;
Error = WinVerifyTrust(NULL, &GenericActionId, &WintrustData);
if (Error != ERROR_SUCCESS)
{
//No need to call WinVerifyTrust again
WintrustCalled = false;
PrintError(Error);
goto Cleanup;
}
DWORD = 4 byte;
WORD = 2 byte;
PE ファイルについて (1) - IMAGE_DOS_HEADER http://tech.blog.aerie.jp/entry/2015/12/21/183741
PE ファイルについて (2) - IMAGE_FILE_HEADER http://tech.blog.aerie.jp/entry/2015/12/23/000000
IMAGE_NT_HEADERS structure https://msdn.microsoft.com/en-us/library/windows/desktop/ms680336(v=vs.85).aspx
typedef struct _IMAGE_NT_HEADERS {
DWORD Signature;
IMAGE_FILE_HEADER FileHeader;
IMAGE_OPTIONAL_HEADER OptionalHeader;
} IMAGE_NT_HEADERS, *PIMAGE_NT_HEADERS;
■Signature:
A 4-byte signature identifying the file as a PE image. The bytes are "PE\0\0".
■FileHeader里包含下面信息:
Machine:
この PE ファイルがターゲットとする CPU のアーキテクチャーを表します。
x86(32bit)の場合は 0x014c、x64(64bit)の場合は 0x8664 です。
それ以外にも IA64 や ARM から MIPS、ALPHA、PowerPC、SH5 など、様々な値が定義されています。
VC++ では、リンカーオプション /MACHINE で指定します。
NumberOfSections:このファイルに含まれるセクションの数を表します。
...
reader.BaseStream.Seek(e_lfanew + 6, SeekOrigin.Begin);
var numberOfSections = reader.ReadUInt16();
...
TimeDateStamp:ファイルの作成時刻 NumberOfSymbols: COFF シンボルテーブルのシンボルの数です。PE ファイルの場合は 0 のようです。 SizeOfOptionalHeader: オプショナル ヘッダーのサイズです。IMAGE_OPTIONAL_HEADER 構造体のサイズになります。
..
reader.BaseStream.Seek(e_lfanew + 8, SeekOrigin.Begin);
var builtTime = reader.ReadUInt32();
reader.BaseStream.Seek(e_lfanew + 16, SeekOrigin.Begin);
var numberOfSymbols = reader.ReadUInt32();
reader.BaseStream.Seek(e_lfanew + 20, SeekOrigin.Begin);
var sizeOfOptionalHeader = reader.ReadUInt16();
..
PE ファイルについて (3) - IMAGE_OPTIONAL_HEADER http://tech.blog.aerie.jp/entry/2015/12/24/013344
■Magic: このファイルが 32bit 用か 64bit 用かを表します。 IMAGE_NT_OPTIONAL_HDR32_MAGIC (0x10b) : 32bit 用 IMAGE_NT_OPTIONAL_HDR64_MAGIC (0x20b) : 64bit 用
// IMAGE_NT_HEADERS:IMAGE_OPTIONAL_HEADER32/64
const UInt16 IMAGE_NT_OPTIONAL_HDR32_MAGIC = 0x10b;
const UInt16 IMAGE_NT_OPTIONAL_HDR64_MAGIC = 0x20b;
//const UInt16 IMAGE_ROM_OPTIONAL_HDR_MAGIC = 0x107;
var ioh_offset = e_lfanew + 24;
reader.BaseStream.Seek(ioh_offset, SeekOrigin.Begin);
var ioh_magic = reader.ReadUInt16();
if (ioh_magic != IMAGE_NT_OPTIONAL_HDR32_MAGIC
&& ioh_magic != IMAGE_NT_OPTIONAL_HDR64_MAGIC) throw new FileFormatException();
bool isX64 = ioh_magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC;
■SizeOfImage:この PE ファイルをロードしたときにメモリ上に占めるサイズです。SectionAlignment の値の倍数に丸められます。ファイルサイズとは異なります。 ■SizeOfHeaders:この PE ファイル中の全てのヘッダーサイズの合計です。FileAlignment の値の倍数に丸められます。
var ioh_offset = e_lfanew + 24;
reader.BaseStream.Seek(ioh_offset + 56, SeekOrigin.Begin);
var ioh_sizeOfImage = reader.ReadUInt32();
var ioh_sizeOfHeaders = reader.ReadUInt32();
■DataDirectory:データ ディレクトリです。 IMAGE_DATA_DIRECTORY 構造体の配列になっており、要素数は NumberOfRvaAndSizes で示されます。
typedef struct _IMAGE_DATA_DIRECTORY { DWORD VirtualAddress; DWORD Size; } IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY;
// IMAGE_NT_HEADERS:IMAGE_OPTIONAL_HEADER:IMAGE_DATA_DIRECTORY
var ioh_idd_offset = ioh_offset + (isX64 ? 112 : 96);
reader.BaseStream.Seek(ioh_idd_offset + 8 * 4, SeekOrigin.Begin);
var ioh_idd_cert_offset = reader.ReadUInt32(); //get virtualAddress
var ioh_idd_cert_size = reader.ReadUInt32(); // get size.
PE ファイルについて (4) - IMAGE_SECTION_HEADER https://msdn.microsoft.com/en-us/library/windows/desktop/ms680305.aspx
セクション データ: セクション データはヘッダーの後の PE ファイルのデータ本体で、セクションとは、その本体領域を用途ごとにいくつかに区切ったものです。 1 つのセクションにつき、1 つのセクション ヘッダーがあります。これが IMAGE_SECTION_HEADER 構造体です。 この構造体は、IMAGE_NT_HEADERS 構造体には含まれていません。ので、厳密には NT ヘッダーの一部ではないです。
セクションがいくつあるかは、第 2 回でやりましたが、IMAGE_FILE_HEADER::NumberOfSections に書かれています。
MSI数据读取
参考资料: インストーラー介绍网页: http://eternalwindows.jp/ http://eternalwindows.jp/installer/msidata/msidata03.html
打开MSI文件的database MSIファイルからデータを取得するには、まずデータベースのハンドルを取得することになります。 ここで言うデータベースとは、いわばMSIファイルそのものなのですが、 データベースという単位に見立てることで、テーブルやレコードといった概念が扱えます。 次に示すMsiOpenDatabaseは、データベースをオープンします。
UINT MsiOpenDatabase(
LPCTSTR szDatabasePath,
LPCTSTR szPersist,
MSIHANDLE *phDatabase
);
关闭msi数据 取得したMSIHANDLEは、不要になったらMsiCloseHandleで閉じることになります。
UINT MsiCloseHandle(
MSIHANDLE hAny
);
指定したテーブルがデータベースに存在するかどうか MsiDatabaseIsTablePersistentという関数を呼び出せば、 指定したテーブルがデータベースに存在するかどうかを調べることができます。
MSICONDITION MsiDatabaseIsTablePersistent(
MSIHANDLE hDatabase,
LPCTSTR szTableName
);
函数返回值是MSICONDITION_TRUE的话,则说明存在指定的数据table。
UINT uResult;
MSICONDITION condition;
MSIHANDLE hDatabase;
uResult = MsiOpenDatabase(TEXT("sample.msi"), MSIDBOPEN_READONLY, &hDatabase);
if (uResult != ERROR_SUCCESS) {
MessageBox(NULL, TEXT("データベースのオープンに失敗しました。"), TEXT("OK"), MB_ICONWARNING);
return 0;
}
condition = MsiDatabaseIsTablePersistent(hDatabase, TEXT("Control"));
if (condition == MSICONDITION_TRUE)
MessageBox(NULL, TEXT("テーブルは存在します。"), TEXT("OK"), MB_OK);
else
MessageBox(NULL, TEXT("テーブルは存在しません。"), NULL, MB_ICONWARNING);
MsiCloseHandle(hDatabase);
MsiDatabaseIsTablePersistentの名前から予想がつくかもしれませんが、 MSIの関数の名前は第1引数に指定するハンドルの種類と一致するようになっています。 つまり、データベースのハンドルを指定する場合は、MsiDatabaseから始まり、 ビューやレコードの場合はそれぞれ、MsiView、MsiRecordとなります。 MSIの関数は非常に種類が多いので、この法則を覚えておくと関数の用途が分かりやすくなります。
判断MSI文件里是否包含“IS“文字
static bool IsExistISInnerTbl(string filePath)
{
bool isExist = false;
string tableName = string.Empty;
IntPtr hMsiHandle = IntPtr.Zero;
uint uiStatus = MsiOpenDatabase(filePath, MSIDBOPEN_READONLY, out hMsiHandle);
if (uiStatus == 0)
{
try
{
IntPtr hView = IntPtr.Zero;
string szQuery = "SELECT * FROM _Tables";
uiStatus = MsiDatabaseOpenView(hMsiHandle, szQuery, out hView);
if (uiStatus == 0)
{
try
{
IntPtr hRecord = IntPtr.Zero;
uiStatus = MsiViewExecute(hView, hRecord);
if (uiStatus == 0)
{
try
{
while (MsiViewFetch(hView, out hRecord) == 0)
{
IntPtr fieldVlu = IntPtr.Zero;
uint dwStringLen = 0;
var result1 = MsiRecordGetString(hRecord, 1, fieldVlu, ref dwStringLen);
if (dwStringLen > 0)
{
fieldVlu = Marshal.AllocHGlobal(((int)++dwStringLen) * sizeof(char));
var result2 = MsiRecordGetString(hRecord, 1, fieldVlu, ref dwStringLen);
if (fieldVlu != IntPtr.Zero)
{
tableName = Marshal.PtrToStringUni(fieldVlu, (int)dwStringLen);
Marshal.FreeHGlobal(fieldVlu);
}
}
if (tableName.Length >= 2 && tableName.Substring(0, 2).Equals("IS"))
{
isExist = true;
break;
}
}
}
finally
{
if (hRecord != IntPtr.Zero)
{
MsiCloseHandle(hRecord);
}
}
}
}
finally
{
if (hView != IntPtr.Zero)
{
MsiCloseHandle(hView);
}
}
}
}
finally
{
if (hMsiHandle != IntPtr.Zero)
{
MsiCloseHandle(hMsiHandle);
}
}
}
return isExist;
}
- MsiGetSummaryInformation
サマリー情報とは、製品会社などの簡略な製品情報をまとめたもので、 これを利用することで、たとえばPropertyテーブルのManufacturerという列の フィールド値を取得するといったような手順を踏む必要がなくなります。 サマリー情報へのアクセスは、MsiGetSummaryInformationの呼び出しから始まります。
サマリー情報に含まれる個々のデータは、プロパティという単位で区切られています。 プロパティはIDによって識別され、プロパティ毎にデータの型も異なります。 これらの情報は、マイクロソフトのリファレンスに掲載されている プロパティセットとよばれる表から参照することができます。
![image](https://user-images.githubusercontent.com/12871721/31120425-502bdca6-a86f-11e7-906e-daaec353d3db.png)
从MSI文件里取得程序名称(id=18)
static string GetProgramName(string strFile)
{
string programName = string.Empty;
IntPtr hSummaryInfo;
uint uiStatus = MsiGetSummaryInformation(IntPtr.Zero, strFile, 0, out hSummaryInfo);
if (uiStatus == 0)
{
try
{
uint dataType = 0;
int pvalue = 0;
long time = 0;
uint dwBufferLength = 0;
var result1 = MsiSummaryInfoGetProperty(hSummaryInfo, 18, out dataType, out pvalue, out time, IntPtr.Zero, ref dwBufferLength);
if (dwBufferLength > 0)
{
IntPtr pProgramName = Marshal.AllocHGlobal(((int)++dwBufferLength) * sizeof(char));
var result2 = MsiSummaryInfoGetProperty(hSummaryInfo, 18, out dataType, out pvalue, out time, pProgramName, ref dwBufferLength);
if (pProgramName != IntPtr.Zero)
{
programName = Marshal.PtrToStringUni(pProgramName, (int)dwBufferLength);
Marshal.FreeHGlobal(pProgramName);
}
}
}
finally
{
if (hSummaryInfo != IntPtr.Zero)
{
MsiCloseHandle(hSummaryInfo);
}
}
}
return programName;
}
签名处理
关键点: 用C++语言实现 //利用的库文件: WinTrust.dll
WinVerifyTrust 函数
LONG WINAPI WinVerifyTrust(
_In_ HWND hWnd,
_In_ GUID *pgActionID,
_In_ LPVOID pWVTData
);
//pgActionID [in] : A pointer to a GUID structure that identifies an action and the trust provider that supports that action. 比如值指定“WINTRUST_ACTION_GENERIC_VERIFY_V2” 表示“Verify a file or object using the Authenticode policy provider.”
//pWVTData [in] : A pointer that, when cast as a WINTRUST_DATA structure, contains information that the trust provider needs to process the specified action identifier.
<C++的处理>
WINTRUST_FILE_INFO结构体
WINTRUST_DATA结构体
typedef struct _WINTRUST_DATA { DWORD cbStruct; LPVOID pPolicyCallbackData; LPVOID pSIPClientData; DWORD dwUIChoice; DWORD fdwRevocationChecks; DWORD dwUnionChoice; union { struct WINTRUST_FILEINFO pFile; struct WINTRUST_CATALOGINFO pCatalog; struct WINTRUST_BLOBINFO pBlob; struct WINTRUST_SGNRINFO pSgnr; struct WINTRUST_CERTINFO pCert; }; DWORD dwStateAction; HANDLE hWVTStateData; WCHAR pwszURLReference; DWORD dwProvFlags; DWORD dwUIContext; WINTRUST_SIGNATURE_SETTINGS pSignatureSettings; } WINTRUST_DATA, PWINTRUST_DATA;
WINTRUST_SIGNATURE_SETTINGS结构体
CMSG_SIGNER_INFO structure
The CMSG_SIGNER_INFO structure contains the content of the PKCS #7 defined SignerInfo in signed messages. In decoding a received message, CryptMsgGetParam is called for each signer to get a CMSG_SIGNER_INFO structure.
typedef struct _CMSG_SIGNER_INFO { DWORD dwVersion; CERT_NAME_BLOB Issuer; CRYPT_INTEGER_BLOB SerialNumber; CRYPT_ALGORITHM_IDENTIFIER HashAlgorithm; CRYPT_ALGORITHM_IDENTIFIER HashEncryptionAlgorithm; CRYPT_DATA_BLOB EncryptedHash; CRYPT_ATTRIBUTES AuthAttrs; CRYPT_ATTRIBUTES UnauthAttrs; } CMSG_SIGNER_INFO, *PCMSG_SIGNER_INFO;
-----------------------------------------
- 判断是否存在签名
bool CSignCheck::IsCheckSigned(LPCTSTR lpFileName) { bool bSignedModule = false;
if (lpFileName && lstrlen(lpFileName))
{
USES_CONVERSION;
WINTRUST_FILE_INFO stFileInfo = {0};
stFileInfo.cbStruct = sizeof(stFileInfo);
stFileInfo.pcwszFilePath = lpFileName;
WINTRUST_DATA stTrustData = {0};
stTrustData.cbStruct = sizeof(stTrustData);
stTrustData.dwUIChoice = WTD_UI_NONE;
stTrustData.fdwRevocationChecks = WTD_REVOKE_NONE;
stTrustData.dwUnionChoice = WTD_CHOICE_FILE;
stTrustData.pFile = &stFileInfo;
stTrustData.dwProvFlags = WTD_REVOCATION_CHECK_NONE;
GUID stGuidAction = WINTRUST_ACTION_GENERIC_VERIFY_V2;
LONG lResult = ::WinVerifyTrust((HWND)INVALID_HANDLE_VALUE, &stGuidAction, &stTrustData);
bSignedModule = lResult == 0;
}
return bSignedModule;
}
- 取得数字签名的数量
关键点: WSS_GET_SECONDARY_SIG_COUNT
//Set this value to return the number of secondary signatures found in the cSecondarySigs member.
签名总数 = WSS_GET_SECONDARY_SIG_COUNT + 1。
- 取得签名日期
CMSG_SIGNER_INFO. AuthAttrs
(1)取得CMSG_SIGNER_INFO. AuthAttrs里的pszObjId,
(2)如果pszObjId是szOID_RFC3161_counterSign 或szOID_RSA_counterSign,则分别通过不同方法取得timestamp。
RFC3161是一种時刻認証プロトコル(Time-Stamp Protocol)。
RSA是一种加密算法
public static readonly string szOID_RFC3161_counterSign = "1.3.6.1.4.1.311.3.3.1"; public static readonly string szOID_RSA_signingTime = "1.2.840.113549.1.9.5"; public static readonly string szOID_RSA_counterSign = "1.2.840.113549.1.9.6";
(3)调用Crypt32.dll的CryptMsg方法来获得CMsgSignerInfo情报
CryptMsgOpenToDecode(ENCODING_TYPE, 0, 0, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero);
CryptMsgGetParam(hMsg, Crypt32.MsgParamType.CMSG_SIGNER_INFO_PARAM, 0, IntPtr.Zero, ref dwSize))
CryptMsgGetParam(hMsg, Crypt32.MsgParamType.CMSG_SIGNER_INFO_PARAM, 0, pDataBuffer, ref dwSize))
CMSG_SIGNER_INFO counterSignerInfo = new Crypt32.CMsgSignerInfo(pDataBuffer);
(4)获得CMSG_SIGNER_INFO的AuthAttrs里的pzObjID 等于szOID_RSA_signingTime的AuthAttrs对象,
调用
CryptDecodeObject(ENCODING_TYPE, szOID_RSA_signingTime, attrBlob.pbData, attrBlob.cbData, 0, pDataBuffer, ref dwSize);
if (fResult)
{
ft =
(System.Runtime.InteropServices.ComTypes.FILETIME)Marshal.PtrToStructure(pDataBuffer, typeof(System.Runtime.InteropServices.ComTypes.FILETIME)); //ft就是timestamp。
fReturn = true;
}
-----------------------
可以参考的网页:
http://eternalwindows.jp/crypto/filesign/filesign00.html
取得文件的version信息,并进一步取得major version信息。
判定文件的copyright里是否包含DW字样
其中, AllDirectories:Includes the current directory and all its subdirectories in a search operation. This option includes reparse points such as mounted drives and symbolic links in the search. TopDirectoryOnly:Includes only the current directory in a search operation.