如何在没有&"消耗&"的情况下读取键盘输入它在x86 DOS组件中? [英] How to read keyboard input without "consuming" it in x86 DOS assembly?

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

问题描述

我需要编写一种可以从C代码中调用的按键记录程序功能.这意味着c程序将调用一个名为startlog的汇编函数,该函数将指示开始记录所按下的按键,直到调用名为endlog的函数为止.日志记录应该像这样工作:在不影响startlog和endlog之间的C代码的情况下,写任何按下的键的ascii值,这意味着如果C代码也需要读取输入(比如用scanf,它将正常工作)./p>

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

解决方案

以下是您的操作方法:

 //用Borland的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,./???";无效中断(* pOldInt9)(void);无效中断(* pOldInt1C)(void);char far * pInDosFlag;#定义SCAN_BUF_SIZE 1024易失的未签名字符ScanBuf [SCAN_BUF_SIZE];易失性无符号ScanReadIdx = 0;易失性无符号ScanWriteIdx = 0;易失的未签名LogFileHandle;void DosWriteFile(unsigned handle,void * data,size_t size);易失性无符号InDos0cnt = 0;void TryToSaveLog(void){未签名的cnt;如果(* pInDosFlag)返回;cnt =(ScanWriteIdx-ScanReadIdx)&(SCAN_BUF_SIZE-1);InDos0cnt ++;而(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];如果((scanCode& 0x7F)< strlen(ScanToChar)){s [6] = ScanToChar [scanCode&0x7F];}DosWriteFile(LogFileHandle,s,strlen(s));ScanReadIdx ++;ScanReadIdx& = SCAN_BUF_SIZE-1;}}void中断NewInt9(void){无符号字符scanCode = inp(0x60);ScanBuf [ScanWriteIdx ++] = scanCode;ScanWriteIdx& = SCAN_BUF_SIZE-1;pOldInt9();}易失性无符号int1Ccnt = 0;void中断NewInt1C(void){int1Ccnt ++;pOldInt1C();TryToSaveLog();}未签名的DosCreateFile(const char * name){工会REGS法规;构造SREGS sregs;regs.h.ah = 0x3C;regs.x.cx = 0;sregs.ds = FP_SEG(name);regs.x.dx = FP_OFF(名称);intdosx(& regs,& regs,& sregs);返回regs.x.cflag吗?0:regs.x.ax;}void DosWriteFile(无符号句柄,void *数据,size_t大小){工会REGS法规;构造SREGS sregs;如果(!size)返回;regs.h.ah = 0x40;regs.x.bx =句柄;regs.x.cx =大小;sregs.ds = FP_SEG(data);regs.x.dx = FP_OFF(数据);intdosx(& regs,& regs,& sregs);}DosCloseFile(未签名的句柄){工会REGS法规;构造SREGS sregs;regs.h.ah = 0x3E;regs.x.bx =句柄;intdosx(& regs,& regs,& sregs);}无效的StartLog(const char * FileName){工会REGS法规;构造SREGS sregs;LogFileHandle = DosCreateFile(FileName);regs.h.ah = 0x34;//获取InDos标志地址intdosx(& regs,& regs,& sregs);pInDosFlag = MK_FP(sregs.es,regs.x.bx);pOldInt1C = getvect(0x1C);setvect(0x1C,& NewInt1C);pOldInt9 = getvect(9);setvect(9,& NewInt9);}无效EndLog(void){setvect(9,pOldInt9);while(ScanWriteIdx!= ScanReadIdx);setvect(0x1C,pOldInt1C);DosCloseFile(LogFileHandle);LogFileHandle = 0;}int main(无效){char str [256];StartLog("keylog.txt");printf(请输入一些文本:\ n");得到(str);printf(您已经输入\"%s \"\ n",str);EndLog();printf("int 1Ch count:%u \ n",int1Ccnt);printf("InDos = 0 count:%u \ n",InDos0cnt);返回0;} 

输出(在Windows XP上运行):

 请输入一些文本:qweasdzxc123您输入了"qweasdzxc123"int 1Ch计数:175InDos = 0计数: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文件I/O例程的情况下,有一个循环缓冲区来存储扫描代码的原因. EndLog()等待直到缓冲区耗尽.您可能还需要更早强制排水.

我还尝试了将int 28h挂钩来替代int 1Ch,但是我从未调用过用于int 28h的ISR,不确定原因.

我避免在日志文件中使用C的 fopen() fwrite()/ fprintf()干扰不知道后台发生的事情的主程序.出于相同的原因,ISR中仅使用了最简单的标准C函数.

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).

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;
}

Output (run on 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 "?"

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()).

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.

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.

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.

这篇关于如何在没有&amp;&quot;消耗&amp;&quot;的情况下读取键盘输入它在x86 DOS组件中?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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