C语言操作Mysql数据库

前言

前几天要用开发个工具,需要连接到数据库,然后增删查改,图形化界面,还有大量指针操作,所以只能用C比较合适,但是C语言连接Mysql没接触过,遍开始了C语言mysql尝试,之后过了几天(就是现在),又要重新配置下环境,虽然依稀记得怎么操作,但是感觉这种事情有必要记下来,不然时间久了还得再去百度搜索,麻烦的很。

环境安装

从mysql官网下载Mysql connector 6.1版本,为什么是6.1呢,因为我找了半天,8.0版本的装了好几遍,就是没有C语言的,只有C++版本的。

C语言版本下载地址

安装好后开始下一步,记得安装目录。

开始开发

C++ 头文件添加目录 C:\Program Files (x86)\MySQL\MySQL Connector C 6.1\include
C++ lib添加目录 C:\Program Files (x86)\MySQL\MySQL Connector C 6.1\lib

将 C:\Program Files (x86)\MySQL\MySQL Connector C 6.1\lib\libmysql.dll复制到程序的运行目录下。

若使用VS开发,则放到VS目录(直接调试运行时),编译的情况下放到应用程序目录即可。

代码部分

1
2
3
4
5
6
7
#include <mysql.h>

#define SERVER_ADDR "127.0.0.1"
#define SERVER_PORT 3306
#define SERVER_USER "root"
#define SERVER_PASS "root"
#define SERVER_TABLE "xxx"

关于C++网络通信开发的坑

1.协议设计

1.关键数据加密
2.防止数据重放
3.注意数据对齐

2.协议实现

1.服务端多线程同步问题。
2.客户端秘钥登录认证问题。
3.处理TCP粘包问题

Windows64位下关于SSDT寄存器

想要对SSDT进行HOOK,首先要找到SSDT表,修改表的内容,但是64位情况却有些不同。

  • 64位关于SSDT的KeServiceDescriptorTable并没有导出,无法使用extend来使用
  • 64位关于SSDT无法直接修改,有PG保护,甚至不能inline HOOK (触发蓝屏)

解决方法:

  • 使用PG补丁,强行将PG disable
  • 使用MSR接管,重新生成一份SSDT表,在自己的表中HOOK

无论是方案1还是方案2,在没有KeServiceDescriptorTable的情况下只能通过特征码搜索SSDT表

其中,关键函数就是KiSystemCall64,该函数的地址可以通过读取c0000082寄存器来获取
windbg

1
rdmsr c0000082

Windows 驱动入门

Windows驱动层是我一直想学的东西,最近也在看软件调试。

如果不了解驱动层的开发,就很难把驱动层的逆向学好。

目标很简单,先把驱动的基本安装代码实现,再把R0层与R3层通信的方式实现

最后通过R3层的程序能通过直接调用R0层的接口来实现R3层操作R0层。

DbgView配置

涉及到开发DbgView是必不可少的一个工具

windows7需要添加一个注册表项才能接受到DbgView的消息:

1
2
3
4
Windows Registry Editor Version 5.00

[HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Control\Session Manager]
"Debug Print Filter"=dword:0000000f

配置好后重启即可。

WDK + VS2012

这里用VS2012是因为win10对驱动开发不友好,选择Windows7做开发环境,VS2010代码提示有点问题,上次学C++开发CLR程序的时候根本没提示。

因此选用了VS2012,目前用的蛮好,现在我的开发环境一直用VS2012,那么WDK只能选择8.0了,8.1版本的WDK没办法支持VS2012。

还有我比较喜欢本地版本的MSDN,不想在WEB上查询代码,这些VS2012都能搞定,给大家推荐一下。

最简单的驱动程序

开发驱动,也是要从Hello,world开始入门的,这就来写一个最简单的驱动程序。

选用WDM空项目

上MSDN,找DriverEntry的代码,拷贝复制

编写一段入门程序:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <ntddk.h>

DRIVER_UNLOAD Unload;

NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject,PUNICODE_STRING RegistryPath){

DbgPrint("驱动加载成功!");

DriverObject->DriverUnload = Unload;


return STATUS_SUCCESS;
}


VOID Unload(struct _DRIVER_OBJECT *DriverObject){
DbgPrint("驱动卸载成功!");

}

将警告视为错误 这个选项禁用

编译成功,驱动成功被注册。

使用DbgView查看输出

第一步:打开选项

第二步:安装启动卸载驱动

总结一下,就是驱动要有个入口,类似于exe程序的main,然后入口的第一个参数是一个引用,你需要将引用的卸载地址指向卸载驱动的函数地址。

至此,HelloWorld之路已经走完了,下面要开始学习关于通信的方式了。

DriverEntry特性

DriverEntry()一共有 2 个参数

  • PDRIVER_OBJECT DriverObject,指向驱动程序对象的指针。
  • PUNICODE_STRING RegistryPath,驱动程序的服务主键,在DriverEntry() 返回后则会释放,需要用时记得保存。

在DriverEntry中,一般需要做一下几件事情:

  • 设置驱动卸载例程
  • 注册 IRP 的派遣函数
  • 创建设备对象

驱动卸载例程与 IRP 派遣函数都是对驱动对象设置的。

驱动通信的方式有很多:

  • 共享内存
  • 共享事件
  • IOCTL宏
  • ReadFile() 或 WriteFile()

今天这里学习的使用IOCTL宏来进行通信。

Win32程序使用 DeviceIoControl() 与驱动进行通信。

驱动程序与 I/O 管理器通信,使用的是 IRP,即 I/O 请求包。

IRP 分为2部分:

  • IRP 首部
  • IRP堆栈。

在驱动程序中,IRP 派遣例程几乎都有对应的 Win32 API 函数,下面介绍IRP派遣例程。

名称 描述 调用者
IRP_MJ_CREATE 请求一个句柄 CreateFile
IRP_MJ_CLEANUP 在关闭句柄时取消悬挂的IRP CloseHandle
IRP_MJ_CLOSE 关闭句柄 CloseHandle
IRP_MJ_READ 从设备得到数据 ReadFile
IRP_MJ_WRITE 传送数据到设备 WriteFile
IRP_MJ_DEVICE_CONTROL 控制操作(利用IOCTL宏) DeviceIoControl
IRP_MJ_INTERNAL_DEVICE_CONTROL 控制操作(只能被内核调用) N/A
IRP_MJ_QUERY_INFORMATION 得到文件的长度 GetFileSize
IRP_MJ_SET_INFORMATION 设置文件的长度 SetFileSize
IRP_MJ_FLUSH_BUFFERS 写输出缓冲区或丢弃输入缓冲区 FlushFileBuffers\FlushConsoleInputBuffer \PurgeComm
IRP_MJ_SHUTDOWN 系统关闭 InitiateSystemShutdown

IRP(I/O Request Package)

Ring3(用户层)应用程序调用kernel32.dll导出的DeviceIoControl函数后,会调用到ntdll.dll导出的NtDeviceIoControlFile函数,进而调用到ntoskrnl.exe提供的服务函数NtDeviceIo ControlFile,该函数会将I/O请求转化为IRP包,并发送到对应驱动的派遣例程函数中,其机制类似于Windows的消息机制。

IRP是一个很复杂的数据结构,IRP两个基本的属性:

  • MajorFunction
  • MinorFunction

分别记录IRP的主类型和子类型。

操作系统根据MajorFunction将IRP 分发给派遣函数,在派遣函数中还可以判断IRP属于哪种MinorFunction。

DriverEntry有个函数指针数组MajorFunction.都是派遣函数地址。

C语言编写网站目录扫描器实战

最近又开始入门渗透一波,网上有很多基于字典和爬虫的网站目录扫描器。

下载了个wwwscan,玩了几下,发现是一个别人修改过的版本。

渗透工具很多网上传来传去,最后有没有木马都不知道。

我希望能攒出自己的一套工具,顺带提高一下自己的C语言水平。

顺带把这些工具所提供的字典好好的收集一波(攒一下自己的字典)。

至于为什么用C语言是因为C语言写的工具效率会更高些。

New、delete、指针及引用

在C++中,使用new 和 delete 进行动态控件申请时,代码如下:

1
2
int* p = new int;
Delete p ;

使用一维数组时,代码如下;

1
int* p = new int[9];

使用部分定长二维数组时,代码如下:

1
2
3
4
5
6
7
8
int n = 10;
int(* p)[20] = new int[n][20];
for (int i = 0; i < n; i++) {
for (int j = 0; j < 20; j++) {
p[i][j] = 0;
}
}
delete[] p;

C语言函数调用约定【转】

在C语言中,假设我们有这样的一个函数:

1
int function(int a,int b)

调用时只要用result = function(1,2)这样的方式就可以使用这个函数。但是,当高级语言被编译成计算机可以识别的机器码时,有一个问题就凸现出来:在CPU中,计算机没有办法知道一个函数调用需要多少个、什么样的参数,也没有硬件可以保存这些参数。也就是说,计算机不知道怎么给这个函数传递参数,传递参数的工作必须由函数调用者和函数本身来协调。为此,计算机提供了一种被称为栈的数据结构来支持参数传递。

栈是一种先进后出的数据结构,栈有一个存储区、一个栈顶指针。栈顶指针指向堆栈中第一个可用的数据项(被称为栈顶)。用户可以在栈顶上方向栈中加入数据,这个操作被称为压栈(Push),压栈以后,栈顶自动变成新加入数据项的位置,栈顶指针也随之修改。用户也可以从堆栈中取走栈顶,称为弹出栈(pop),弹出栈后,栈顶下的一个元素变成栈顶,栈顶指针随之修改。

函数调用时,调用者依次把参数压栈,然后调用函数,函数被调用以后,在堆栈中取得数据,并进行计算。函数计算结束以后,或者调用者、或者函数本身修改堆栈,使堆栈恢复原装。