如何读取键盘输入,而不"消费"它的x86 DOS汇编? [英] How to read keyboard input without "consuming" it in x86 DOS assembly?

查看:225
本文介绍了如何读取键盘输入,而不"消费"它的x86 DOS汇编?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我需要写一个排序关键字记录器功能,可以从C code调用。
它的意思是一个C程序会调用汇编函数调用startlog这将表明开始记录pssed,直到一个叫endlog函数被调用键$ P $。日志应该像这样工作:写不影响startlog和endlog之间的C code,这意味着如果C code也需要读取输入(让我们通过说pssed任意键$ P $的ASCII值scanf函数,它会工作确定)。

I need to write a sort of key-logger function that can be called from C code. what it means is a c program would call an assembly function called startlog that would indicate to start logging the keys pressed until a function called endlog is called. the logging should work like this: write the ascii value of any key pressed without disturbing the C code between startlog and endlog, meaning that if the C code also needs to read the input (let's say by scanf, it would work ok).

我设法通过改变中断向量9个项(中断键盘preSS)传递给函数我写的值写入到一个文件中写入记录仪,并能正常工作。然而C code没有获得输入。
基本上,我所做的是阅读的关键$ P $用INT 21H pssed,阅读它是消费的ASCII值后,但是,所以我需要一种方法来无论是再次模拟键preSS或读取值,而消费它使下一次关键是看它读取相同的密钥。
(我描述了code在英语,因为它是漫长的,笨拙的组装code)

I managed to write the logger by changing the interrupt vector 9th entry (interrupt for keyboard press) to a function I wrote that writes the values to a file, and it works fine. however the C code does not get the input. Basically what i did is read the key pressed using int 21h, however after reading the ascii value it is "consumed" so I need a way to either simulate the key press again or read the value without "consuming" it so next time a key is read it reads the same key. (I described the code in english because it is long and clumsy assembly code)

推荐答案

下面是你如何能做到这一点:

Here's how you can do it:

// 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[] = "0123456789ABCDEF";
    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上运行):

Output (run on Windows XP):

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

KEYLOG.TXT:

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忙,即使它在等待这样简单的事情,如键盘输入(例如,在获得())。

There're a few problems here. You can't use some DOS functions when it's busy. This is why I'm checking the InDos flag. At the same time InDos can indicate that DOS is busy even when it's waiting for such simple things as keyboard input (e.g. in gets()).

这是为什么有用于扫描codeS的积累,而这些程序不能安全地调用DOS文件I / O例程循环缓冲区。 EndLog()等待,直到缓冲区被排干。您可能需要强制排水早期为好。

This is why there's a circular buffer for the scan codes that accumulates them while the program can't safely call DOS file I/O routines. EndLog() waits until the buffer is drained. You may need to force draining earlier as well.

我也试着挂钩INT 28H作为替代诠释代上,但我对INT 28H ISR得到了从未引用,不知道为什么。

I've also tried hooking int 28h as an alternative to int 1Ch, but my ISR for int 28h got never invoked, not sure why.

我避免使用C的 fopen()函数的fwrite() / fprintf中()日志文件,以免与主程序那是不知道的事情在后台发生冲突。只有最琐碎的标准C函数是在中断服务程序中使用出于同样的原因。

I'm avoiding the use of C's fopen() and fwrite()/fprintf() for the log file so as not to interfere with the main program that's unaware of the things happening in the background. Only the most trivial standard C functions are used in the ISRs for the same reason.

这篇关于如何读取键盘输入,而不&QUOT;消费&QUOT;它的x86 DOS汇编?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆