Windows内核学习

消息机制

Window消息机制的解决方案是通过GUI线程来处理,消息队列在R0层。

Linux消息机制是通过进程来处理。

1.当线程刚创建的时候,都是普通线程:

Thread.ServiceTable->KeServiceDescriptorTable (系统服务表)

2.当线程第一次调用Win32k.sys(调用服务号>100)的时候,会调用:PsConvertToGuiThread。

主要做2件事情:

a.扩充内核栈,必须换成64KB大小的内核栈,因为普通内核栈只有12KB大小
b.创建一个_KTHREAD结构体(有一个成员指针指向结构体 _THREADINFO,_THREADINFO结构体有一个成员叫MessageQueue,即消息队列,只有GUI线程才有),普通线程_KTHREAD->Win32Thread = NULL;
c.Thread.ServiceTable->KeServiceDescriptorTableShadow
d.把需要的内存数据映射到本进程空间。

总结: 可以通过KTHREAD.Win32Thread来找到消息队列,只有GDI线程才有消息队列,一对一的关系。

窗口与线程

所有的窗口在0环都有一个 _WINDOWS_OBJECT 对象,该对象->PTHREADINFO 指向其GDI线程。

一个线程有N多窗口,一个窗口只能属于一个线程。

窗口句柄是全局对象,可以被多个进程使用。

消息队列

消息队列的结构

1.SentMessagesListHead // 接收SendMessage发来的消息。

2.PostedMessagesListHead // 接到PostMessage发来的消息

3.HardwareMessagesListHead // 接收鼠标、键盘的消息

一共有7种,自行百度补全、

GetMessage 函数(
LPMSG lpMsg
HWND hWnd
UNIT wMsgMin
UNIT wMsgMax
)

注: GetMessage 会自动处理 SentMessagesListHead 队列的消息并消灭

DispatchMessage 拿着句柄进0环通过窗口对象来进行分发。

TranslateMessage 主要负责处理键盘消息 WM_CHAR消息的转换等。

PWN入门之堆学习(一)

关于Linux堆的资料,我看了很久,看的云里雾里的,今天突然发现了一些点,重新梳理了一下,大概理解了。

写的是自己的理解,不一定对,后面理解加深的话会回来修改这些错误。

首先Linux堆的实现有很多种,glibc使用的是ptmalloc2,以其为中心进行学习。

堆空间的申请从系统层面有2个函数,一个是brk,一个是mmap

arena

无论是主线程还是新创建的线程,在第一次申请内存时,都会有独立的 arena,一般申请堆空间系统会分配132KB的空间。

其头部结构如下代码所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
struct malloc_state
{
/* Serialize access. */
__libc_lock_define (, mutex);
/* Flags (formerly in max_fast). */
int flags;

/* Fastbins */
mfastbinptr fastbinsY[NFASTBINS];
/* Base of the topmost chunk -- not otherwise kept in a bin */
mchunkptr top;
/* The remainder from the most recent split of a small request */
mchunkptr last_remainder;
/* Normal bins packed as described above */
mchunkptr bins[NBINS * 2 - 2];

/* Bitmap of bins */
unsigned int binmap[BINMAPSIZE];

/* Linked list */
struct malloc_state *next;
/* Linked list for free arenas. Access to this field is serialized
by free_list_lock in arena.c. */
struct malloc_state *next_free;
/* Number of threads attached to this arena. 0 if the arena is on
the free list. Access to this field is serialized by
free_list_lock in arena.c. */

INTERNAL_SIZE_T attached_threads;
/* Memory allocated from the system in this arena. */
INTERNAL_SIZE_T system_mem;
INTERNAL_SIZE_T max_system_mem;
};

typedef struct malloc_state *mstate;

其中各种bin是我最头疼的部分:

fastbin是先进后出,存放在fastbinsY数组

fastbinsY[0]~fastbinsY[9] 分别指向:16,24,32,40,48,56,64,72,80和88大小的chunk单向链表

bins里面存放着 Unsorted bin 、Small bin 、Large bin的双向链表,是先进先出结构

1
2
3
4
5
6
7
8
9
bins[0] 存放着 Unsorted bin 双向链表,用来存储未分类的回收chunk。

bins[1] - bins[62] 存放着 Small bin 双向链表,用来存储16, 24, ... , 504 bytes的chunk。

bins[63] - bins[126] 存放着 、Large bin 双向链表,用来存储 xxxxx。

这部分应该还没理解,等理解来修改,双向练兵应该有2位,类似 bins[0] 和 bins[1] 存放着 Unsorted bin 双向链表

后面理解透彻了再回来修正,总觉得这部分理解不到位,得多找些资料学习一下。

然后最重要的是Top chunk,开空间的用的 free空间的时候可能将空间放入bins,也可能合并空间。

今天主要是把链表这部分理解了,就是根据不同大小生成的链表,之前这部分一直没理解。

Pwn相关知识学习

今天开始学习CTF各类逆向题型,Android通过JEB已经可以进行大部分基础的逆向,不难,所以有空再写,但是PWN这玩意玩起来就脑壳疼了,不记下来指不定过几天我就忘了。

格式化字符创漏洞学习

格式化字符创这东西由于参数个数的不确定性,因此可以构造恶意参数来读写内存。

输出函数

1
2
3
4
5
6
#include <stdio.h>
int printf(const char *format, ...);
int fprintf(FILE *stream, const char *format, ...);
int dprintf(int fd, const char *format, ...);
int sprintf(char *str, const char *format, ...);
int snprintf(char *str, size_t size, const char *format, ...);

对于输出函数,即使是printf函数也可以被用来操作内存的读写,以下程序为例:

1
2
3
4
5
6
7
8
9
#include<stdio.h>
void main() {
char format[128];
int arg1 = 1, arg2 = 0x88888888, arg3 = -1;
char arg4[10] = "ABCD";
scanf("%s", format);
printf(format, arg1, arg2, arg3, arg4);
printf("\n");
}

注:进行编译需要冠以 aslr和关闭pie

1
2
echo 0 > /proc/sys/kernel/randomize_va_space
gcc -m32 -fno-stack-protector -no-pie main.c

通过输入%p%p%p%p%p%p%p%p%p%p%p%p 即可打印处堆栈内的信息,若需读任意地址,配合上%n$s即可,具体应用:
python -c ‘print(“\x00\x10\x40\x00%13$s”)’ 如此便可打印处 00401000的内存地址的值

重点:%13$s 这种格式需要大家自行查询了解,这里给出一个例子:

printf(“%2$s,%1$d”,123,”Hello”)

这个函数输出的结果为 Hello,123。 至于我上面给的%13$s 中的13,只是个例子,每个程序都不一样,上诉例子在调试过程中我在调试过程中获取到的是13。

可以对任意内存进行读取之后,需要对任意内存进行写操作,这里给一个关于printf写内存的例子:

1
2
3
4
5
6
7
#include<stdio.h>
void main() {
int i;
char str[] = "hello";
printf("%s %n\n", str, &i);
printf("%d\n", i);
}

输出结果为 :

1
2
hello
6

这里的%n代表的是将前面所包含的char数组长度写入到i变量中。

printf(“%0134512640d%n\n”, 1, &i);

以上代码 将 0x8048000 写入到i当中,实际利用情况 我们使用 %hhn % hn 来写入内存,hhn表示的是char类型地址。

python2 -c ‘print(“\x38\xd5\xff\xff”+”\x39\xd5\xff\xff”+”\x3a\xd5\xff\xff””\x3b\xd5\xff\xff”+”%104c%13$hhn”+”%222c%14$hhn”+”%
222c%15$hhn”+”%222c%16$hhn”)’ | ./a.out

因此就完成了对任意地址的写入。但是前面的字符创占用了大小,自行处理(二次覆盖或者把%13$n移动到前面去但是要注意内存对齐)。

WinDbg调试指令

dt 指令,用于打印数据结构 举例 dt _PEB
x 指令,查找全局函数或变量,需要PDB支持 举例 x calc!g*
.reload 重载符号表
dd 32位数据显示 dq 64位数据显示
u 反汇编,u 00401000 返汇编目标地址
.formats 将虚拟地址转为二进制,用于分页机制中查找其内存地址
!process 0 0 命令列出所有进程

软件调试

CPU 基础

CPU的操作模式

保护模式和实模式

保护模式指的是具有强大的虚拟内存支持和完善的任务保护机制,实模式是直接操作物理IO地址。虚拟8086模式即保护模式下的类实模式。

寄存器

寄存器相关的逆向工程核心原理已介绍

特权级

描述符特权级 : Descriptor Privilege Level 简称DPL,位于段描述符或门描述符中,用于表示一个段或门的特权级别

PE文件结构学习总结

之前是基于数据来学习PE结构,学习的是PE结构的基础,但为了快速开发,应该使用winnt中提供的结构体来实现基于PE结构的相关功能,如内存载入DLL,内存注入,IAT HOOK等技术。

从文件头开始,即 0x0这个地址开始,遇到的会是一个DOS头。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
typedef struct _IMAGE_DOS_HEADER {      // DOS .EXE header
    WORD   e_magic;                     // Magic number
    WORD   e_cblp;                      // Bytes on last page of file
    WORD   e_cp;                        // Pages in file
    WORD   e_crlc;                      // Relocations
    WORD   e_cparhdr;                   // Size of header in paragraphs
    WORD   e_minalloc;                  // Minimum extra paragraphs needed
    WORD   e_maxalloc;                  // Maximum extra paragraphs needed
    WORD   e_ss;                        // Initial (relative) SS value
    WORD   e_sp;                        // Initial SP value
    WORD   e_csum;                      // Checksum
    WORD   e_ip;                        // Initial IP value
    WORD   e_cs;                        // Initial (relative) CS value
    WORD   e_lfarlc;                    // File address of relocation table
    WORD   e_ovno;                      // Overlay number
    WORD   e_res[4];                    // Reserved words
    WORD   e_oemid;                     // OEM identifier (for e_oeminfo)
    WORD   e_oeminfo;                   // OEM information; e_oemid specific
    WORD   e_res2[10];                  // Reserved words
    LONG   e_lfanew;                    // File address of new exe header
  } IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER;

逆向工程核心原理

第一章 软件逆向工程

软件逆向分析的2种方法:

1.静态分析

该方法主要使用IDA PRO等工具,对软件进行静态分析,整理软件分支结构,从而判断出软件的真正意图。

该方法适合简单应用程序以及未对代码进行保护的程序,通常加壳程序需要先对其进行脱壳才可使用IDA PRO 进行分析。

2.动态分析

该方法使用Ollydbg工具进行动态调试,使用Ollydbg进行动态调试时需要对PE文件结构,TSL回调函数,软件执行流程等有一点的认知

软件逆向工程中需要的基础知识:

1.C/C++代码知识
2.十六/二进制思维
3.汇编语言基础
4.操作系统知识

第二章 使用两种方式打补丁

1.通过直接在内存中修改

在内存中修改字符创后,重新生成PE文件,该方法局限于源字符串大小。

第三章 小端标记法

大端标记法常出现在底层,unix系统和网络协议大多使用大端标记法

小段标记法在windows和linux中被使用

小端的特点是高位放在高地址,低位放在低地址

大端的特点就是存储时像字符串一样

内存中的数据大多以4K对其方式

第四章 寄存器

X86体系结构计算机通常有如下寄存器:

8个通用寄存器 : eax ebx ecx edx esi edi esp ebp
6个段寄存器: cs ds ss es fs gs
一个状态/控制寄存器: eflags
一个程序指针寄存器: eip

eax常用于结果返回,edx通常协助eax进行计算
ebx用于地址计算
ecx用于累加
esi edi 用于数据传输
esp 堆栈当前地址
ebp 堆栈参考指针
cs 代码段
ds 数据段
ss 堆栈段
es 拓展数据段
fs gs 拓展数据段(有用的数据)

程序调试常用到FS寄存器,用于计算SEH 结构化异常处理机制,TEB 线程环境块 ,PEB 进程环境块等地址,属于高级调试必备的数据。

还有CR0-CR4 折4个控制寄存器,学习到了再讨论

第五章 栈

先进先出

第七章 栈帧

EBP保存当前栈空间位置,退出函数还原,即栈帧的概念

第八章 VB程序

消息框函数 rtcMsgBox

第十章 函数调用约定

cdecl 调用方式,调用者负责处理堆栈平衡,add esp,8
stdcall 调用方式,被调用者自行处理堆栈,return 8
fastcall 调用方式,与stdcall类似,但是会用到寄存器

第十三章 PE文件结构

用010 editor观察即可,最难的是导入表和导出表这部分

导出表 由 导出表的数据目录指向,数据目录指向的导出表地址是个链表,直到00000000结束

数据内容为一个结构一,其中有OriginalFirstThunk 和 FirstThunk 2个指针,指向一个指针数字

1数组指向需要的dll函数名
2在执行的时候,会把DLL文件中函数名的覆盖地址到该指针数组,但是未执行时该数组和1数组内容相同

第二十一章 DLL注入之Windows消息钩子

HOOK消息钩子,实现DLL注入。

第二十三章 DLL注入

方法:
1.线程注入DLL
2.注册表注入DLL
3.HOOK注入DLL
4.输入法注入DLL
5.导入表注入
6.内存注入/代码注入

第二十九章 API钩取技术(HOOK)

第三十七章 X64函数调用约定

X64中统一采用fastcall方式来调用

参数1 放在rcx XMM0
参数2 放在rdx XMM1
参数3 放在r8 XMM2
参数4 放在r9 XMM3

减少 push的使用 用move 传递参数,直接使用rsp进行堆栈操作

C++反汇编与逆向分析实战

今天开始学习这本书,做一些笔记,之前有一些基础,理解的部分便少做些记录了。

IDA Pro 常用快捷键

快捷键 功能
A 结实光标的地址为一个字符的首地址
B 十六进制和二进制进行转换
C 解释光标处地址为一条指令
D 解释光标为止为数据
G 快速查找大对应地址
H 十六进制和十进制转换
K 将数据解释为栈变量
; 添加注释
M 解释为枚举成员
N 重新命名
O 解释数据为一个数据读研偏移量
T 解释数据为一个结构体成员
X 切换师徒到交叉参考模式
Shift +F9 添加结构体

PE文件结构浅解析

1.序言

时光荏苒,再次重拾起逆向程序这一块其实我是很开心的,当初之所以选择了别的方向,主要还是担心就业问题,既然能够有机会从事这方面的工作,我得赶紧把之前学习过的知识补救回来,那么就从PE结构开始吧,也怪当初自己学习的时候没有做笔记的习惯,导致现在比较麻烦些。

2.PE文件结构介绍

首先PE结构是windows可执行应用程序的一种格式,如果我们的程序不遵顼PE文件格式,那么windows将无法识别我们的程序,我们在实际编写程序的时候,其实并不需要关注PE文件结构,因为编译器会为我们做好这些,但我们写程序并不能止步于此,懂PE文件结构,懂调试的人甚至能把你整个代码逻辑挖出来,代码基本上很难做到完美无缺陷,一旦被找到漏洞,那么别人的可操作空间就非常高了。因此,我们要知其然且知其所以然,做软件安全方面的工作,要能会攻击,知道别人怎么攻击你的软件,这时候我们才能做出防御,就好比绝地求生这款游戏,游戏开发的很精彩,但由于开发人员对于安全防护上面的不足,导致这款游戏外挂横行,几乎被毁了。

3.PE文件结构浅解析

扯了这么多,要开始学习PE文件结构了,这里用到16进制文件查看器PETools,为了更加清晰,这里所有的数值都使用16进制