Как читать ввод с клавиатуры, не «потребляя» его в сборке x86 DOS? - PullRequest
0 голосов
/ 12 ноября 2011

Мне нужно написать своего рода функцию кейлоггера, которую можно вызывать из кода Си.это означает, что программа ac будет вызывать функцию сборки, называемую startlog, которая будет указывать, чтобы начать регистрировать нажатые клавиши, пока не будет вызвана функция endlog.регистрация должна работать следующим образом: записать значение ascii любой нажатой клавиши, не нарушая код C между startlog и endlog, что означает, что если код C также должен прочитать ввод (скажем, с помощью scanf, он будет работать нормально).

Мне удалось записать регистратор, изменив 9-ю запись вектора прерывания (прерывание для нажатия клавиатуры) на функцию, которую я написал, которая записывает значения в файл, и все работает нормально.однако код C не получает ввод.По сути, я прочитал нажатую клавишу, используя int 21h, однако после прочтения значения ascii он «расходуется», поэтому мне нужен способ либо имитировать нажатие клавиши еще раз, либо прочитать значение, не «потребляя» его, поэтому в следующий раз клавишачитается он читает тот же ключ.(Я описал код на английском, потому что это длинный и неуклюжий ассемблерный код)

Ответы [ 2 ]

2 голосов
/ 12 ноября 2011

Вот как вы можете это сделать:

// 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):

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.EndLog() ждет, пока буфер не будет слит.Возможно, вам также придется принудительно слить ранее.

Я также пытался подключить int 28h в качестве альтернативы int 1Ch, но мой ISR для int 28h никогда не вызывался, не знаю почему.

Я избегаю использования C * fopen() и fwrite() / fprintf() для файла журнала, чтобы не мешать основной программе, которая не знает, что происходит в фоновом режиме.По той же причине в ISR используются только самые тривиальные стандартные функции C.

1 голос
/ 12 ноября 2011

Если INT 9 является прерыванием клавиатуры, и вы изменяете этот вектор так, чтобы он указывал на ваш собственный код для перехвата символов, почему вы не можете просто сохранить старый вектор и перейти к нему в конце кода хука?

...