본문 바로가기

RestRoom/Routine

PS/2 키로거 90% 완성...!!

 디바이스 드라이버를 공부하면서 PS/2 키로거를 만들어 보게 되었습니다..ㅠㅠ

만들면서 많은걸 배우고 삽질도 하고... 블루스크린도 거진 100번은 이상은 띄운거 같아요...흑..

90%라고 말한 이유는 기술 구현은 다 됬지만 
커널에 로드 시키는 로더도 만들어야 하고 아직 코드 리펙토링이 덜 되어 있기 때문입니다...

구현 방법만 적어보겠습니다.
DriverEntry
1. WorkItem 루틴과 DpcForIsr 루틴을 사용하기 위해 디바이스를 하나 생성한다.
2. 실시간으로 파일에 누른 키를 저장하기 위해 C:\Windows 폴더에 파일을 생성시킨다.
3. WorkItem루틴과 DpcForIsr 루틴을 사용하기 위한 초기화 작업.
4. SMP 환경에서 모든 CPU의 IDT를 훅 하기 위해 CustomDPC루틴을 사용한다.

GetInterruptNum 
( IDT 후킹 시 필요한 인터럽트 벡터 값과 OS마다 다른 ISR주소와 PKINTERRUPT 주소 차이를 구한다. )
1. OS의 버전을 알아온다.
2. OS 버전을 알았으면 그 버전에 맞는 ISR - PKINTERRUPT 값을 넣는다. ( XP -> 0x3C, Windows 7 -> 0x58 )
3. HalGetInterruptVector함수를 사용해 필요한 Interrupt Vector 값을 실시간으로 얻어온다.

IDT_HOOK ( PKINTERRUPT -> ServiceRoutine 에 있는 값 바꿈 )
1. sidt 명령어로 IDTR의 정보를 가져온다.
2. IDTR에서 IDT 엔트리 주소를 얻는다.
3. IDT 에서 해당 인터럽트 벡터 값에 맞는 ISR 주소를 구한다.
4. ISR 주소에서 GetInterruptNum에서 구한 (ISR - PKINTERRUPT) 값 빼준다. ( PKINTERRUPT 구함 )
5. PKINTERRUPT -> ServiceRoutine의 값을 저장해 두고 나의 핸들러 주소를 넣어준다. ( MyInterruptFunc )

MyInterruptFunc ( naked 함수 )
1. DriverEntry에서 초기화 해둔 DpcForIsr 루틴을 DPC큐에 넣어준다.
2. IDT_HOOK에서 저장해둔 원래의 ServiceRoutine으로 점프한다.

DsrForIsr
1. 현재는 DPC큐에서 작동하는 코드이므로 IRQL이 DISPATCH_LEVEL인데 파일 입출력을 위해 PASSIVE_LEVEL을 보장해야 한다. 그러므로 DriverEntry에서 초기화해둔 WorkItem루틴을 호출

WorkItem
1. READ_PORT_UCHAR함수를 사용해 0x60포트로부터 값을 읽어온다.
2. 읽어온 키보드 스캔 코드 값을 보기좋기 다듬는다. ( a..z, 1..0, up,down... etc... )
3. 다듬은 스캔 코드 값을 아까 C:\Windows 폴더에 열어둔 파일에 값을 쓴다.

UnLoad
1. 후킹한 ServiceRoutine을 다시 언훅 해준다. 과정은 SMP환경에서 IDT 후킹한 거와 같음. CustomDPC루틴 사용
2. 모든 핸들을 닫아주고 할당했던 메모리들을 해제해준다.

구현은 이게 전부입니다.
코드는 대략 400줄이 나왔는데 유저어플개발과 다르게
역시 커널에서 작동하는 코드라 그런지 코드를 짜는 시간보다 안정화 시키는 시간이 더 걸렸습니다 ㅠㅠ

제가 만든 드라이버 테스트 결과 Windows7, Windows XP 둘다 잘 됬고 당연히 SMP환경에서 테스트 했습니다.
Windows 7 -> Ultimate K, Enterprise K
Windows XP -> SP3

나중에 더 업그레이드 시켜서 학교에서 프로젝트 내라면 내봐야겠습니다..