2011-11-12 71 views
0

我需要编写一种可以从C代码调用的键盘记录器函数。 这意味着一个c程序会调用一个名为startlog的程序集函数,它会指示开始记录按下的键,直到调用一个名为endlog的函数。日志应该像这样工作:在不打扰startlog和endlog之间的C代码的情况下写入按下的任何按键的ascii值,这意味着如果C代码也需要读取输入(比如scanf,那么它将工作正常)。如何读取键盘输入而不在x86 DOS程序集中“消耗”它?

我设法通过更改中断向量第9项(键盘按下中断)写入记录器到我写的函数,将值写入文件,并且它工作正常。但是C代码没有得到输入。 基本上,我所做的是读取使用int 21h按下的键,然而在读取ascii值后它被“消耗”了,所以我需要一种方法来再次模拟按键或读取值而不“消耗”它,所以下一次键读取它读取相同的密钥。 (我用英文描述了代码,因为它是长且笨拙的汇编代码)

+0

你是否从'int 9'处理函数中调用'int 21'并且它不会爆炸? – ninjalj

+0

如果你正确地调用它们,什么都不爆炸...... – Bob

回答

2

这里是你如何能做到这一点:

// Compile with Borland's Turbo C++ 1.01 

#include <stdlib.h> 
#include <stdio.h> 
#include <string.h> 
#include <dos.h> 

const char ScanToChar[] = 
    "??1234567890-=??" 
    "QWERTYUIOP[]??AS" 
    "DFGHJKL;\"`?\\ZXCV" 
    "BNM,./??? "; 

void interrupt (*pOldInt9)(void); 
void interrupt (*pOldInt1C)(void); 
char far* pInDosFlag; 

#define SCAN_BUF_SIZE 1024 
volatile unsigned char ScanBuf[SCAN_BUF_SIZE]; 
volatile unsigned ScanReadIdx = 0; 
volatile unsigned ScanWriteIdx = 0; 

volatile unsigned LogFileHandle; 
void DosWriteFile(unsigned handle, void* data, size_t size); 

volatile unsigned InDos0cnt = 0; 

void TryToSaveLog(void) 
{ 
    unsigned cnt; 

    if (*pInDosFlag) 
    return; 

    cnt = (ScanWriteIdx - ScanReadIdx) & (SCAN_BUF_SIZE - 1); 

    InDos0cnt++; 

    while (cnt--) 
    { 
    static const char hex[] = "ABCDEF"; 
    char s[80] = "0xXX \"?\"\r\n"; 
    unsigned char scanCode = ScanBuf[ScanReadIdx]; 

    s[2] = hex[scanCode >> 4]; 
    s[3] = hex[scanCode & 0xF]; 

    if ((scanCode & 0x7F) < strlen(ScanToChar)) 
    { 
     s[6] = ScanToChar[scanCode & 0x7F]; 
    } 

    DosWriteFile(LogFileHandle, s, strlen(s)); 

    ScanReadIdx++; 
    ScanReadIdx &= SCAN_BUF_SIZE - 1; 
    } 
} 

void interrupt NewInt9(void) 
{ 
    unsigned char scanCode = inp(0x60); 

    ScanBuf[ScanWriteIdx++] = scanCode; 
    ScanWriteIdx &= SCAN_BUF_SIZE - 1; 

    pOldInt9(); 
} 

volatile unsigned int1Ccnt = 0; 

void interrupt NewInt1C(void) 
{ 
    int1Ccnt++; 
    pOldInt1C(); 
    TryToSaveLog(); 
} 

unsigned DosCreateFile(const char* name) 
{ 
    union REGS regs; 
    struct SREGS sregs; 

    regs.h.ah = 0x3C; 
    regs.x.cx = 0; 

    sregs.ds = FP_SEG(name); 
    regs.x.dx = FP_OFF(name); 

    intdosx(&regs, &regs, &sregs); 

    return regs.x.cflag ? 0 : regs.x.ax; 
} 

void DosWriteFile(unsigned handle, void* data, size_t size) 
{ 
    union REGS regs; 
    struct SREGS sregs; 

    if (!size) return; 

    regs.h.ah = 0x40; 
    regs.x.bx = handle; 
    regs.x.cx = size; 

    sregs.ds = FP_SEG(data); 
    regs.x.dx = FP_OFF(data); 

    intdosx(&regs, &regs, &sregs); 
} 

void DosCloseFile(unsigned handle) 
{ 
    union REGS regs; 
    struct SREGS sregs; 

    regs.h.ah = 0x3E; 
    regs.x.bx = handle; 

    intdosx(&regs, &regs, &sregs); 
} 

void StartLog(const char* FileName) 
{ 
    union REGS regs; 
    struct SREGS sregs; 

    LogFileHandle = DosCreateFile(FileName); 

    regs.h.ah = 0x34; // get InDos flag address 
    intdosx(&regs, &regs, &sregs); 
    pInDosFlag = MK_FP(sregs.es, regs.x.bx); 

    pOldInt1C = getvect(0x1C); 
    setvect(0x1C, &NewInt1C); 

    pOldInt9 = getvect(9); 
    setvect(9, &NewInt9); 
} 

void EndLog(void) 
{ 
    setvect(9, pOldInt9); 

    while (ScanWriteIdx != ScanReadIdx); 

    setvect(0x1C, pOldInt1C); 

    DosCloseFile(LogFileHandle); 
    LogFileHandle = 0; 
} 

int main(void) 
{ 
    char str[256]; 

    StartLog("keylog.txt"); 

    printf("please enter some text:\n"); 
    gets(str); 
    printf("you have entered \"%s\"\n", str); 

    EndLog(); 

    printf("int 1Ch count: %u\n", int1Ccnt); 
    printf("InDos=0 count: %u\n", InDos0cnt); 

    return 0; 
} 

输出(在Windows XP上运行):

please enter some text: 
qweasdzxc123 
you have entered "qweasdzxc123" 
int 1Ch count: 175 
InDos=0 count: 1 

KEYLOG.TXT:

0x10 "Q" 
0x90 "Q" 
0x11 "W" 
0x91 "W" 
0x12 "E" 
0x92 "E" 
0x1E "A" 
0x9E "A" 
0x1F "S" 
0x9F "S" 
0x20 "D" 
0xA0 "D" 
0x2C "Z" 
0xAC "Z" 
0x2D "X" 
0xAD "X" 
0x2E "C" 
0xAE "C" 
0x02 "1" 
0x82 "1" 
0x03 "2" 
0x83 "2" 
0x04 "3" 
0x84 "3" 
0x1C "?" 

是有几个问题这里。繁忙时不能使用某些DOS功能。这就是我检查InDos标志的原因。与此同时,InDos可以指示即使DOS正在等待键盘输入等简单操作时(例如,在gets()中),DOS仍处于忙碌状态。

这就是为什么有一个循环缓冲区的扫描代码积累他们,而程序不能安全地调用DOS文件I/O例程。 EndLog()等待缓冲区排空。您可能需要尽早强制排空。

我也试过将int 28h作为int 1Ch的替代方法,但是我的int 28h的ISR从来没有调用过,不知道为什么。

对于日志文件,我避免使用C的fopen()fwrite()/fprintf(),以免干扰不知道背景中发生的事情的主程序。出于同样的原因,在ISR中只使用最平凡的标准C函数。

+0

注意:使用'“??”',这段代码几乎使用[trigrpahs](https://en.wikipedia.org/wiki/Digraphs_and_trigraphs#C)。也许保证双''?''是分开的,如:'“??” “1234567890- =”“??” ...' – chux

1

如果INT 9是键盘中断,并且您正在更改此向量以指向您自己的代码来拦截字符,为什么不能只存储旧的矢量,并在你的钩子代码的末尾跳到它?

+0

我这样做,我跳到原来的INT 9,它来处理键盘输入,然后调用int21h来读取它。 – Bob

+0

好吧,它像你需要缓冲字符并钩int 21一样。 –