何もすることがない場合は、書いたドライブを逆にして理解を深めてください。
ここでは逆ドライブが分割されDebug版
、Release版
そして加载PDB版
。3つのバージョンの特徴は次のとおりです。
デバッグバージョンはコンパイラによって最適化されず、リバースエンジニアリングの学習により適しています。
リリース版は外界にリリースされており、リバースエンジニアリングの過程で多くの構造や分解が最適化・変形されており、オリジナルコードの特徴を持っているとしか言えません。
PDBバージョンのロードは、ソースコードの読み取りとほぼ同じであり、問題はありません。
したがって、ここでの調査の焦点は、逆デバッグバージョンによってコンパイルされた独自のドライバーです。
デバッグ版
IDAによって認識されるDriverEntryの最初のドラッグは、実際のDriverEntry関数ではありません。sub_401250です。
入口でのDestinationString構造体の割り当てとWdfVersionBind関数はすべて、コンパイル後にシステムによって追加されることに注意してください。
特定の初期化関数は何ですか?PDBをロードして確認できますが、これはほとんど重要ではありません。
メイン関数の登録コールバック関数sub_4012F0はDriverUnloadであり、登録コールバック関数sub_401210はMajorFunctionです。
これが二重リンクリストをトラバースしていることがわかりますが、これがLDR_DATA_TABLE_ENTRY構造であると推測できない場合があります。DriverObject + 0x14はDriverSectionであるため、ここにドライバーモジュールのリンクリストがあります。
関数が逆方向にディスパッチされる場合、MajorFunctionの配列インデックスに従ってIRPのタイプを確認でき、注意深く調査する必要のあるディスパッチ関数が特定されます。これらのディスパッチ関数の最初のパラメーターは、ほとんどがDriverObjectまたはDeviceObjectです。
PDBをロードした後、sub401bc0が__CheckForDebuggerJustMyCodeであることがわかりました。
DriverObjectの構造は非常に重要なので、ここで前後を見てください。
ソースコード内のMajorFunctionの要素数は0x1B、0x1B + 1 = 0x1Chであり、これは図の10進数の28です。28x4 = 112を占有します。つまり、HEX値は70hであるため、DriverObjectのサイズはA8hです。
typedef struct _IO_STACK_LOCATION {
UCHAR MajorFunction;
UCHAR MinorFunction;
UCHAR Flags;
UCHAR Control;
Union{
}Parameters;
PDEVICE_OBJECT DeviceObject;
PFILE_OBJECT FileObject;
PIO_COMPLETION_ROUTINE CompletionRoutine;
PVOID Context;
} IO_STACK_LOCATION, *PIO_STACK_LOCATION;
IDAにはWindowsDriver Kit 7/10 32ビットの署名しかなく、Wdm.hで定義されている一部の関数が認識されないため、リバースエンジニアリングが困難になります。
ソースコードのReadCompleteRoutine関数。この関数を解読して、a2がPIRP構造であると推測したい。逆の鍵は、構造を推測することです。1つ目は、開発経験を積むことです。これにより、コンテキストの意図をよりよく探求できます。次に、コードを作成するときに一般的に使用されるいくつかのシステム構造に精通します。
IRP + 0X18hはIoStatusです。IPR + 0xChはポインタAssociatedIrpです。ソースコードはSystemBufferへのポインタです。
union {
struct _IRP *MasterIrp;
__volatile LONG IrpCount;
PVOID SystemBuffer;
} AssociatedIrp;
0x1Chのオフセットは推測されません。_IO_STATUS_BLOCK構造を見てください。
typedef struct _IO_STATUS_BLOCK {
union {
NTSTATUS Status;
PVOID Pointer;
} DUMMYUNIONNAME;
ULONG_PTR Information;
} IO_STATUS_BLOCK, *PIO_STATUS_BLOCK;
オフセット0x1Chアドレスは0X18hであり、次にオフセット0x4hアドレスであり、IO_STATUS_BLOCK構造体であることがわかります。率直に言って、構造または構造内のデータの意味を推測する限り、コードの意図を正しく見ることができます。
最後に、ReadCompleteRoutine関数であるsub_401AB0関数のソースコードが添付されています。
NTSTATUS ReadCompleteRoutine(PDEVICE_OBJECT pDevObj, PIRP pIrp, PVOID pContext)
{
NTSTATUS status = pIrp->IoStatus.Status;
PKEYBOARD_INPUT_DATA pKeyboardInputData = NULL;
ULONG ulKeyCount = 0, i = 0;
if (NT_SUCCESS(status))
{
pKeyboardInputData = (PKEYBOARD_INPUT_DATA)pIrp->AssociatedIrp.SystemBuffer;
ulKeyCount = (ULONG)pIrp->IoStatus.Information / sizeof(KEYBOARD_INPUT_DATA);
// 获取按键数据
for (i = 0; i < ulKeyCount; i++)
{
// Key Press
if (KEY_MAKE == pKeyboardInputData[i].Flags)
{
// 按键扫描码
DbgPrint("[Down][0x%X]\n", pKeyboardInputData[i].MakeCode);
}
// Key Release
else if (KEY_BREAK == pKeyboardInputData[i].Flags)
{
// 按键扫描码
DbgPrint("[Up][0x%X]\n", pKeyboardInputData[i].MakeCode);
}
//可以添加上这这一句,然后按键全部被改为了 按下 A
// pKeyboardInputData[i].MakeCode = 0x1e;
}
}
if (pIrp->PendingReturned)
{
IoMarkIrpPending(pIrp);
}
// 减少IRP在队列的数量
((PDEVICE_EXTENSION)pDevObj->DeviceExtension)->ulIrpInQuene--;
status = pIrp->IoStatus.Status;
return status;
}