现充|junyu33

《加密与解密》学习笔记

与csapp相比,《加密与解密》更偏向于实践一点,内容丰富但比较杂。汇编代码语法与csapp不同,采用intel语法。

基础知识

诸如API、Unicode、Little-endian之类的介绍,此处略去。

(但是Win32 API和WOW64放到这一段就很劝退,算了还是将来用到再查文档吧——)

动态分析技术

静态分析技术

逆向分析技术

32位软件逆向技术

启动函数

WinAPI集中区域,看不懂直接跳。

函数

第N次复习栈调用的机会。

常见的调用约定(VARARG指参数的个数不确定):

①:仅当平衡栈者为caller时适用。

数据结构

局部变量:使用栈存放。

全局变量:.data区段 / cs:xxxx

数组:基址加变址寻址。为了看懂数组的汇编代码,狂补csapp的相应章节

虚函数——1/15/2022

引用虚函数:先用指针(通常由new或malloc分配)指向虚函数表(VTBL),该表存放着所有虚函数的地址,再使用虚函数表指针(VPTR)调用函数。

根据虚表可以还原这个类的虚函数个数,及虚函数代码。

疑问:汇编代码中0040101B地址,为什么eax=*VTBL=**Add(),而不是eax=*VTBL=&Add()

控制语句——1/17/2022

(大多数都在csapp讲过了,没什么特别重要的点)

a & (-b)且b是2的幂,等价于abb

sbb A B指令:A = A - B - CF

循环语句——1/23/2022

与csapp的讲述相同,本质就是高地址向低地址的跳转。

更正:

  1. 两个方案的i < 5应改为i <= 5

  2. 未优化代码中0x40102E处的注释应是“由高地址向低地址区域”。

数学运算符——1/23/2022

加减法使用lea指令加速运算。

乘法使用位移指令加速。

除法在除数已知的情况下会乘一个常数(类似于逆元),取结果的高位。特别的,如果除数是2的幂,那么直接右移即可。

如果结果是负数,值会+1(可能与负数的向0舍入有关)。

文本字符串——1/23/2022

目前惯用的C字符串结尾为'\0'。其余字符串为DOS字符串(结尾为$)、PASCAL字符串(单字节开头的ANSI字符表示长度)和Delphi字符串(双字节或四字节)。

如果出现mov ecx, FFFFFFFF一句,代表程序很有可能获取字符串的长度,汇编代码如下。

指令修改技巧——1/24/2022

感觉没什么好说的,直接贴图总结吧。

64位软件逆向技术——1/24/2022

寄存器~循环语句

什么是__security_check_cookie?

通过在栈中未使用的空间中填入0xcc,并将该值与rsp指针异或,得到的值就是__security_check_cookie。因为现在的程序开启了栈随机化的保护,使得cookie的值无法被预测,因此是一种有效防止栈溢出的方式。

这句话是什么意思?(P150)

mov eax, ds:(jpt_140001060 - 140000000h)[rcx+rax*4]

类似于(a)[b+c*d]的操作类似于AT&T语法中的a(b,c,d),就是b+c*d+a的意思。这里实现了一个进入跳表的操作,为switch语句的分支跳转做准备。

数学运算符

虚函数

演示版保护技术

序列号保护方式——2/4/2022

警告窗口——2/7/2022

int DialogBoxParam(
    HINSTANCE hInstance,
    LPCTSTR lpTemplateName,
    HWND hWndParent,
    DLGPROC lpDialogFunc,
    LPARAM dwInitParam
);

时间限制——2/8/2022

菜单功能限制——2/9/2022

KeyFile保护——2/10/2022

网络验证——2/12/2022

疑问:getasm.py脚本似乎不与IDA 7.6相兼容,如何重新实现以下代码?

#coding=utf-8
##《加密与解密》第四版
##code by DarkNess0ut

import os
import sys

def Getasm(ea_from, ea_to, range1, range2):
    fp = open("code.txt","w")
    ea = ea_from
    while ea < ea_to:
        cmd = GetMnem(ea)
        if cmd == "mov" or cmd == "lea":
            opcode = Dword(NextNotTail(ea)-4)
            if opcode < 0: #opcode < 0,处理 mov  edx, [ebp-350]指令,否则处理mov  edx, [ebp+350]
                opcode = (~opcode + 1)
            Message("-> %08X %08X\n" % (ea, opcode))

            if range1 <= opcode <= range2:
                delta = opcode - range1
                MakeComm(ea, "// +0x%04X" % delta) # 加注释到IDA中
                fp.write("%08X %s\n" % (ea, GetDisasm(ea)))
        ea = NextNotTail(ea)
    fp.close()
    Message("OK!")
Getasm(0x401000,0x40F951,0x41AE68,0x0041AEC1);

光盘检测——2/11/2022

只运行1个实例——2/11/2022

常用断点设置技巧——2/11/2022

掌握Win32编程技巧还是很重要滴!

加密算法

本节笔记的记录主要以算法识别为主,不涉及算法具体过程(公钥除外,因为没有特殊常数),汇编分析与crack操作。

可以使用IDA的FindCrypt或者PEiD的Krypto ANALyzer来辅助分析算法。

单项散列算法——2/27/2022

MD5

重要常数:

可能的变种方式:

SHA

SHA-1的常数5a827999h,6ed9eba1h,8f1bbcdch,ca62c1d6h.

SHA-1的160位的初始化消息摘要67452301h,efcdab89h,98badcfeh,10325476h,c3d2e1f0h.

SHA-256、SHA384、SHA512的初始化消息摘要:

SM3

公开的国密算法,过程简述:https://zhuanlan.zhihu.com/p/129692191

可能的代码实现:https://blog.csdn.net/a344288106/article/details/80094878

常数?79CC4519h,7A879D8Ah

初始化消息摘要?

7380166Fh,4914B2B9h,172442D7h,DA8A0600h,A96F30BCh,163138AAh,E38DEE4Dh,B0FB0E4Eh

对称加密算法——2/28/2022

RC4

从网上嫖来的解密脚本:

import base64
def rc4_main(key = "init_key", message = "init_message"):
    print("RC4解密主函数调用成功")
    print('\n')
    s_box = rc4_init_sbox(key)
    crypt = rc4_excrypt(message, s_box)
    return crypt
def rc4_init_sbox(key):
    s_box = list(range(256))
    print("原来的 s 盒:%s" % s_box)
    print('\n')
    j = 0
    for i in range(256):
        j = (j + s_box[i] + ord(key[i % len(key)])) % 256
        s_box[i], s_box[j] = s_box[j], s_box[i]
    print("混乱后的 s 盒:%s"% s_box)
    print('\n')
    return s_box
def rc4_excrypt(plain, box):
    print("调用解密程序成功。")
    print('\n')
    plain = base64.b64decode(plain.encode('utf-8'))
    plain = bytes.decode(plain)
    res = []
    i = j = 0
    for s in plain:
        i = (i + 1) % 256
        j = (j + box[i]) % 256
        box[i], box[j] = box[j], box[i]
        t = (box[i] + box[j]) % 256
        k = box[t]
        res.append(chr(ord(s) ^ k))
    print("res用于解密字符串,解密后是:%res" %res)
    print('\n')
    cipher = "".join(res)
    print("解密后的字符串是:%s" %cipher)
    print('\n')
    print("解密后的输出(没经过任何编码):")
    print('\n')
    return cipher
a=[0xc6,0x21,0xca,0xbf,0x51,0x43,0x37,0x31,0x75,0xe4,0x8e,0xc0,0x54,0x6f,0x8f,0xee,0xf8,0x5a,0xa2,0xc1,0xeb,0xa5,0x34,0x6d,0x71,0x55,0x8,0x7,0xb2,0xa8,0x2f,0xf4,0x51,0x8e,0xc,0xcc,0x33,0x53,0x31,0x0,0x40,0xd6,0xca,0xec,0xd4]
s=""
for i in a:
    s+=chr(i)
s=str(base64.b64encode(s.encode('utf-8')), 'utf-8')
rc4_main("Nu1Lctf233", s)

TEA

常数为0x9e3779b9,来源于32bit的黄金分割比512

(注意XTEA/XXTEA也是这个常数)

解密脚本:

#include <stdio.h>
#include <stdint.h>
#define DELTA 0x9e3779b9
#define MX (((z>>5^y<<2) + (y>>3^z<<4)) ^ ((sum^y) + (key[(p&3)^e] ^ z)))

void btea (uint32_t* v,int n, uint32_t* k) { // however the 'n' is useless
	uint32_t v0=v[0], v1=v[1], sum=0xC6EF3720, i;  /* set up */
	uint32_t delta=0x9e3779b9;                     /* a key schedule constant */
	uint32_t k0=k[0], k1=k[1], k2=k[2], k3=k[3];   /* cache key */
	for (i=0; i<32; i++) {                         /* basic cycle start */
		v1 -= ((v0<<4) + k2) ^ (v0 + sum) ^ ((v0>>5) + k3);
		v0 -= ((v1<<4) + k0) ^ (v1 + sum) ^ ((v1>>5) + k1);
		sum -= delta;
	}                                              /* end cycle */
	v[0]=v0; v[1]=v1;
}

int main()
{
	uint32_t v[2]= {0x3e8947cb,0xcc944639};
	uint32_t w[2]= {0x31358388,0x3b0b6893};
	uint32_t x[2]= {0xda627361,0x3b2e6427};

	uint32_t const k[4]= {17477,16708,16965,17734};
	int n = 2; //n的绝对值表示v的长度,取正表示加密,取负表示解密
	// v为要加密的数据是两个32位无符号整数
	// k为加密解密密钥,为4个32位无符号整数,即密钥长度为128位
	btea(v, -n, k);
	printf("%x %x ",v[0],v[1]);
	btea(w, -n, k);
	printf("%x %x ",w[0],w[1]);
	btea(x, -n, k);
	printf("%x %x",x[0],x[1]);
	return 0;
}

IDEA

52个子密钥是加密密钥对16bit加法和乘法(216+1)的逆元。

子密钥应按照加密密钥相反的顺序使用。

解密代码略。自行搜索bouncycastle

BlowFish

基于Feistel网络。

P数组(取Pi的小数部分):

243f6a88h,85a308d3h,13198a2eh,03707344h

解密代码略。

AES(Rijndael)

解密的模式有:

知道这些并没有什么用,到时候还是得一个一个试。

S盒:

网上的解密网站都只能解自己的,解不了别人的。

解密网站:http://tool.chacuo.net/cryptaes

SM4

https://zhuanlan.zhihu.com/p/363900323

S盒:

img

img

系统参数FKi的取值:

img

32个固定参数CKi的具体值:

img

解密工具见SM2处链接。

公钥加密算法——3/1/2022

RSA

​ 由此,我们得到了公钥(N,e)与私钥(N,d)

关于对RSA攻击是密码学的专门内容,可以参考这篇文章

ElGamal

对离散对数的攻击:BSGS、Pollard-Rho、Index-Calculus Algorithm、Pohlig-Hellman Algorithm等等。

如果用于不同明文加密的k和私钥x相同,那么有特殊的攻击方法。

DSA——3/2/2022

用于签名,不能用于加解密。

xy需要定期更新,原因同ElGamal.

ECC with GF(p)——3/3/2022

由于对数学知识要求较高,主要用于Crypto出题,在Re遇到的可能性较低,这里不给出具体原理.

算法原理 from wikipedia: https://en.wikipedia.org/wiki/Elliptic-curve_cryptography

算法原理 + py实现 from zhihu: https://zhuanlan.zhihu.com/p/101907402

可能有用的ECC模版:

import collections
import random

EllipticCurve = collections.namedtuple('EllipticCurve', 'name p a b g n h')

curve = EllipticCurve(
   'secp256k1',
   # Field characteristic.
   p=int(input('p=')),
   # Curve coefficients.
   a=int(input('a=')),
   b=int(input('b=')),
   # Base point.
   g=(int(input('Gx=')),
      int(input('Gy='))),
   # Subgroup order.
   n=int(input('k=')),
   # Subgroup cofactor.
   h=1,
)
# Modular arithmetic ##########################################################

def inverse_mod(k, p):
   """Returns the inverse of k modulo p.
  This function returns the only integer x such that (x * k) % p == 1.
  k must be non-zero and p must be a prime.
  """
   if k == 0:
       raise ZeroDivisionError('division by zero')
   if k < 0:
       # k ** -1 = p - (-k) ** -1 (mod p)
       return p - inverse_mod(-k, p)
   # Extended Euclidean algorithm.
   s, old_s = 0, 1
   t, old_t = 1, 0
   r, old_r = p, k

   while r != 0:
       quotient = old_r // r
       old_r, r = r, old_r - quotient * r
       old_s, s = s, old_s - quotient * s
       old_t, t = t, old_t - quotient * t
   gcd, x, y = old_r, old_s, old_t

   assert gcd == 1
   assert (k * x) % p == 1
   return x % p

# Functions that work on curve points #########################################

def is_on_curve(point):
   """Returns True if the given point lies on the elliptic curve."""
   if point is None:
       # None represents the point at infinity.
       return True
   x, y = point
   return (y * y - x * x * x - curve.a * x - curve.b) % curve.p == 0

def point_neg(point):
   """Returns -point."""
   assert is_on_curve(point)
   if point is None:
       # -0 = 0
       return None
   x, y = point
   result = (x, -y % curve.p)
   assert is_on_curve(result)
   return result

def point_add(point1, point2):
   """Returns the result of point1 + point2 according to the group law."""
   assert is_on_curve(point1)
   assert is_on_curve(point2)
   if point1 is None:
       # 0 + point2 = point2
       return point2
   if point2 is None:
       # point1 + 0 = point1
       return point1
   x1, y1 = point1
   x2, y2 = point2

   if x1 == x2 and y1 != y2:
       # point1 + (-point1) = 0
       return None
   if x1 == x2:
       # This is the case point1 == point2.
       m = (3 * x1 * x1 + curve.a) * inverse_mod(2 * y1, curve.p)
   else:
       # This is the case point1 != point2.
       m = (y1 - y2) * inverse_mod(x1 - x2, curve.p)

   x3 = m * m - x1 - x2
   y3 = y1 + m * (x3 - x1)
   result = (x3 % curve.p, -y3 % curve.p)
   assert is_on_curve(result)
   return result

def scalar_mult(k, point):
   """Returns k * point computed using the double and point_add algorithm."""
   assert is_on_curve(point)
   if k < 0:
       # k * point = -k * (-point)
       return scalar_mult(-k, point_neg(point))
   result = None
   addend = point
   while k:
       if k & 1:
           # Add.
           result = point_add(result, addend)
       # Double.
       addend = point_add(addend, addend)
       k >>= 1
   assert is_on_curve(result)
   return result

# Keypair generation and ECDHE ################################################
def make_keypair():
   """Generates a random private-public key pair."""
   private_key = curve.n
   public_key = scalar_mult(private_key, curve.g)
   return private_key, public_key

private_key, public_key = make_keypair()
print("private key:", hex(private_key))
print("public key: (0x{:x}, 0x{:x})".format(*public_key))

SM2

基于ECC的一种国密算法。

SM2~SM4加解密工具:https://github.com/ASTARCHEN/snowland-smx-python

其它算法——3/1/2022

CRC32

只能用于校验文件,不能用来加密。

重点在于初始化生成的crctab表的识别。

算法就几行代码的事:

#include <stdio.h>
int crctab[256]; 
void gentable() {
    for(int i = 0; i < len; i++) {
        int crc = i;
        for(int j = 0; j < 8; j++) {
            if(crc & 1)
                crc = (crc >> 1) ^ 0xedb88320; // or 04c11db7h
            else
                crc >>= 1;
        }
        crctab[i] = crc;
    }
}
int main()
{
    gentable();
    int dwCRC = 0xffffffff;
    for(int i = 0; i < Len; i++) {
        dwCRC = crctab[(dwCRC ^ Data[i]) & 0xff] ^ (dwCRC >> 8); // Data is the bytevalue of your file. 
    }
    dwCRC = ~dwCRC;
    return 0;
}

base64

因为比赛中可能会换表,甚至会修改算法的一些逻辑,因此了解一下其实现过程是很有必要的。

因为3×8=4×6,而且64=26,因此base64的核心就是将三字节的数据对应到四字节的码表中。

对应方式很简单,就是列出3字节,24bit的01串(big endian)。然后每组6bit,分成四组。

每6个bit只能表示0~63这64个数,对应到base64的表(数组)中,替换成这64个字符。

为了保证转换后的串长是4的倍数,如果有6bit没有填充(注意这个跟值为0不同),就替换成=作为paddling。

举例如下图:

核心代码:

for(i=0,j=0;i<len-2;j+=3,i+=4)  
{  
    res[i]=base64_table[str[j]>>2]; //取出第一个字符的前6位并找出对应的结果字符  
    res[i+1]=base64_table[(str[j]&0x3)<<4 | (str[j+1]>>4)]; //将第一个字符的后位与第二个字符的前4位进行组合并找到对应的结果字符  
    res[i+2]=base64_table[(str[j+1]&0xf)<<2 | (str[j+2]>>6)]; //将第二个字符的后4位与第三个字符的前2位组合并找出对应的结果字符  
    res[i+3]=base64_table[str[j+2]&0x3f]; //取出第三个字符的后6位并找出结果字符  
}  

常见加密库接口及其识别——3/3/2022

Miracl、FGInt、Crypto++、OpenSSL等等。

加密算法在软件保护中的应用——3/3/2022

许多软件证明安全和体验是矛盾的.

Windows 内核基础

内核理论基础——1/4/2023

#if defined(_AMD64_) 
//
// Interrupt Request Level definitions
//

#define PASSIVE_LEVEL 0                 // Passive release level
#define LOW_LEVEL 0                     // Lowest interrupt level
#define APC_LEVEL 1                     // APC interrupt level
#define DISPATCH_LEVEL 2                // Dispatcher level
#define CMCI_LEVEL 5                    // CMCI handler level

#define CLOCK_LEVEL 13                  // Interval clock level
#define IPI_LEVEL 14                    // Interprocessor interrupt level
#define DRS_LEVEL 14                    // Deferred Recovery Service level
#define POWER_LEVEL 14                  // Power failure level
#define PROFILE_LEVEL 15                // timer used for profiling.
#define HIGH_LEVEL 15                   // Highest interrupt level

#endif 

有一个蓝屏错误代码就是 irql_not_less_or_equal

The IRQL_NOT_LESS_OR_EQUAL bug check has a value of 0x0000000A. This bug check indicates that Microsoft Windows or a kernel-mode driver accessed paged memory at an invalid address while at a raised interrupt request level (IRQL). The cause is typically a bad pointer or a pageability problem.

内核重要数据结构——1/5/2023

内核对象

常见的有 Dispatcher 对象、I/O 对象、进程对象与线程对象。

SSDT

https://www.ired.team/miscellaneous-reversing-forensics/windows-kernel-internals/glimpse-into-ssdt-in-windows-x64-kernel#finding-address-of-all-ssdt-routines

https://m0uk4.gitbook.io/notebooks/mouka/windowsinternal/ssdt-hook

System Service Dispatch Table or SSDT, simply is an array of addresses to kernel routines for 32 bit operating systems or an array of relative offsets to the same routines for 64 bit operating systems.

SSDT is the first member of the Service Descriptor Table kernel memory structure as shown below:

typedef struct tagSERVICE_DESCRIPTOR_TABLE {
    SYSTEM_SERVICE_TABLE nt; //effectively a pointer to Service Dispatch Table (SSDT) itself
    SYSTEM_SERVICE_TABLE win32k;
    SYSTEM_SERVICE_TABLE sst3; //pointer to a memory address that contains how many routines are defined in the table
    SYSTEM_SERVICE_TABLE sst4;
} SERVICE_DESCRIPTOR_TABLE;

In x64, the relation between SSDT and its function address is shown below:

FuncAddr = ([KeServiceDescriptortable+index*4]>>4 + KeServiceDescriptortable)

SSDT lookup:

.foreach /ps 1 /pS 1 ( offset {dd /c 1 nt!KiServiceTable L poi(nt!KeServiceDescriptorTable+10)}){ r $t0 = ( offset >>> 4) + nt!KiServiceTable; .printf "%p - %y\n", $t0, $t0 }

SSDT(shadow) struct:

struct SSDTStruct
{
    LONG* pServiceTable;
    PVOID pCounterTable;
#ifdef _WIN64
    ULONGLONG NumberOfServices;
#else
    ULONG NumberOfServices;
#endif
    PCHAR pArgumentTable;
};

Function Index to real function address:

readAddress = (ULONG_PTR)(ntTable[FunctionIndex] >> 4) + SSDT(Shadow)BaseAddress;

SSDT(shadow) lookup:

In x64 there is no symbols.

0: kd> !process 0 0 mspaint.exe
PROCESS ffff850e48ee1080
    SessionId: 1  Cid: 0adc    Peb: 219f280000  ParentCid: 12c8
    DirBase: 28b00002  ObjectTable: ffffe5088ad08e80  HandleCount: 296.
    Image: mspaint.exe

0: kd> .process /p ffff850e48ee1080
Implicit process is now ffff850e`48ee1080
.cache forcedecodeuser done
    
0: kd> dps nt!KeServiceDescriptorTableShadow
fffff806`451da980  fffff806`45095570 nt!KiServiceTable       # SSDT base address
fffff806`451da988  00000000`00000000
fffff806`451da990  00000000`000001cf
fffff806`451da998  fffff806`45095cb0 nt!KiArgumentTable
fffff806`451da9a0  fffff528`64b6b000 win32k!W32pServiceTable # SSDT Shadow base address
fffff806`451da9a8  00000000`00000000
fffff806`451da9b0  00000000`000004da
fffff806`451da9b8  fffff528`64b6c84c win32k!W32pArgumentTable
fffff806`451da9c0  00000000`00111311
fffff806`451da9c8  00000000`00000000
fffff806`451da9d0  ffffffff`80000010
fffff806`451da9d8  00000000`00000000
fffff806`451da9e0  00000000`00000000
fffff806`451da9e8  00000000`00000000
fffff806`451da9f0  00000000`00000000
fffff806`451da9f8  00000000`00000000

0: kd> dd /c 1 win32k!W32pServiceTable l10
fffff528`64b6b000  ff972820                                  # GDI Function offset
fffff528`64b6b004  ff972940
fffff528`64b6b008  ff972a60
fffff528`64b6b00c  ff972b80
fffff528`64b6b010  ff972ca2
fffff528`64b6b014  ff972dc0
fffff528`64b6b018  ff972ee0
fffff528`64b6b01c  ff973000
fffff528`64b6b020  ff973120
fffff528`64b6b024  ff973240
fffff528`64b6b028  ff973363
fffff528`64b6b02c  ff973487
fffff528`64b6b030  ff9735a0
fffff528`64b6b034  ff9736c0
fffff528`64b6b038  ff9737e0
fffff528`64b6b03c  ff973900

TEB

TEB(Thread Environment Block,线程环境块)系统在此 TEB 中保存频繁使用的线程相关的数据。位于用户地址空间,在比 PEB 所在地址低的地方。进程中的每个线程都有自己的一个 TEB。一个进程的所有 TEB 都以堆栈的方式,存放在从0x7FFDE000开始的线性内存中,每 4KB 为一个完整的 TEB,不过该内存区域是向下扩展的。在用户模式下,当前线程的 TEB 位于独立的 4KB 段,可通过CPU的FS寄存器来访问该段,一般存储在FS:[0]。在用户态下 WinDbg 中可用命令$thread取得 TEB 地址。

FS:[000] 指向SEH链指针 FS:[004] 线程堆栈顶部 FS:[008] 线程堆栈底部 FS:[00C] SubSystemTib FS:[010] FiberData FS:[014] ArbitraryUserPointer FS:[018] 指向TEB自身 FS:[020] 进程PID FS:[024] 线程ID FS:[02C] 指向线程局部存储指针 FS:[030] PEB结构地址(进程结构) FS:[034] 上个错误号

// Thread Environment Block (TEB)
typedef struct _TEB
{
    NT_TIB Tib;                             /* 00h */
    PVOID EnvironmentPointer;               /* 1Ch */
    CLIENT_ID Cid;                          /* 20h */
    PVOID ActiveRpcHandle;                  /* 28h */
    PVOID ThreadLocalStoragePointer;        /* 2Ch */
    struct _PEB *ProcessEnvironmentBlock;   /* 30h */
    ULONG LastErrorValue;                   /* 34h */
    ULONG CountOfOwnedCriticalSections;     /* 38h */
    PVOID CsrClientThread;                  /* 3Ch */
    struct _W32THREAD* Win32ThreadInfo;     /* 40h */
    ULONG User32Reserved[0x1A];             /* 44h */
    ULONG UserReserved[5];                  /* ACh */
    PVOID WOW32Reserved;                    /* C0h */
    LCID CurrentLocale;                     /* C4h */
    ULONG FpSoftwareStatusRegister;         /* C8h */
    PVOID SystemReserved1[0x36];            /* CCh */
    LONG ExceptionCode;                     /* 1A4h */
    struct _ACTIVATION_CONTEXT_STACK *ActivationContextStackPointer; /* 1A8h */
    UCHAR SpareBytes1[0x28];                /* 1ACh */
    GDI_TEB_BATCH GdiTebBatch;              /* 1D4h */
    CLIENT_ID RealClientId;                 /* 6B4h */
    PVOID GdiCachedProcessHandle;           /* 6BCh */
    ULONG GdiClientPID;                     /* 6C0h */
    ULONG GdiClientTID;                     /* 6C4h */
    PVOID GdiThreadLocalInfo;               /* 6C8h */
    ULONG Win32ClientInfo[62];              /* 6CCh */
    PVOID glDispatchTable[0xE9];            /* 7C4h */
    ULONG glReserved1[0x1D];                /* B68h */
    PVOID glReserved2;                      /* BDCh */
    PVOID glSectionInfo;                    /* BE0h */
    PVOID glSection;                        /* BE4h */
    PVOID glTable;                          /* BE8h */
    PVOID glCurrentRC;                      /* BECh */
    PVOID glContext;                        /* BF0h */
    NTSTATUS LastStatusValue;               /* BF4h */
    UNICODE_STRING StaticUnicodeString;     /* BF8h */
    WCHAR StaticUnicodeBuffer[0x105];       /* C00h */
    PVOID DeallocationStack;                /* E0Ch */
    PVOID TlsSlots[0x40];                   /* E10h */
    LIST_ENTRY TlsLinks;                    /* F10h */
    PVOID Vdm;                              /* F18h */
    PVOID ReservedForNtRpc;                 /* F1Ch */
    PVOID DbgSsReserved[0x2];               /* F20h */
    ULONG HardErrorDisabled;                /* F28h */
    PVOID Instrumentation[14];              /* F2Ch */
    PVOID SubProcessTag;                    /* F64h */
    PVOID EtwTraceData;                     /* F68h */
    PVOID WinSockData;                      /* F6Ch */
    ULONG GdiBatchCount;                    /* F70h */
    BOOLEAN InDbgPrint;                     /* F74h */
    BOOLEAN FreeStackOnTermination;         /* F75h */
    BOOLEAN HasFiberData;                   /* F76h */
    UCHAR IdealProcessor;                   /* F77h */
    ULONG GuaranteedStackBytes;             /* F78h */
    PVOID ReservedForPerf;                  /* F7Ch */
    PVOID ReservedForOle;                   /* F80h */
    ULONG WaitingOnLoaderLock;              /* F84h */
    ULONG SparePointer1;                    /* F88h */
    ULONG SoftPatchPtr1;                    /* F8Ch */
    ULONG SoftPatchPtr2;                    /* F90h */
    PVOID *TlsExpansionSlots;               /* F94h */
    ULONG ImpersionationLocale;             /* F98h */
    ULONG IsImpersonating;                  /* F9Ch */
    PVOID NlsCache;                         /* FA0h */
    PVOID pShimData;                        /* FA4h */
    ULONG HeapVirualAffinity;               /* FA8h */
    PVOID CurrentTransactionHandle;         /* FACh */
    PTEB_ACTIVE_FRAME ActiveFrame;          /* FB0h */
    PVOID FlsData;                          /* FB4h */
    UCHAR SafeThunkCall;                    /* FB8h */
    UCHAR BooleanSpare[3];                  /* FB9h */
} TEB, *PTEB;

PEB

https://www.cnblogs.com/viwilla/p/5109966.html

内容已过时,目前Win10/Win11的PEB偏移已经变成了0x60。

PEB(Process Environment Block,进程环境块)存放进程信息,每个进程都有自己的PEB信息。位于用户地址空间。在Win 2000下,进程环境块的地址对于每个进程来说是固定的,在0x7FFDF000处,这是用户地址空间,所以程序能够直接访问。

准确的 PEB 地址应从系统的EPROCESS结构的0x1b0偏移处获得,但由于EPROCESS在系统地址空间,访问这个结构需要有ring0的权限。

还可以通过TEB结构的偏移0x30处获得PEB的位置,FS段寄存器指向当前的TEB结构:

mov eax, dword ptr fs:[0x30]

或者通过TEB的指针获取:

mov eax, dword ptr fs:[0x18] ;eax = *TEB
mov eax, dword ptr [eax+0x30] ;eax = *PEB

在用户态下 WinDbg 中可用命令$proc取得 PEB 地址。

//Process Environment Block
typedef struct _PEB
{
    UCHAR InheritedAddressSpace; // 00h
    UCHAR ReadImageFileExecOptions; // 01h
    UCHAR BeingDebugged; // 02h
    UCHAR Spare; // 03h
    PVOID Mutant; // 04h
    PVOID ImageBaseAddress; // 08h
    PPEB_LDR_DATA Ldr; // 0Ch
    PRTL_USER_PROCESS_PARAMETERS ProcessParameters; // 10h
    PVOID SubSystemData; // 14h
    PVOID ProcessHeap; // 18h
    PVOID FastPebLock; // 1Ch
    PPEBLOCKROUTINE FastPebLockRoutine; // 20h
    PPEBLOCKROUTINE FastPebUnlockRoutine; // 24h
    ULONG EnvironmentUpdateCount; // 28h
    PVOID* KernelCallbackTable; // 2Ch
    PVOID EventLogSection; // 30h
    PVOID EventLog; // 34h
    PPEB_FREE_BLOCK FreeList; // 38h
    ULONG TlsExpansionCounter; // 3Ch
    PVOID TlsBitmap; // 40h
    ULONG TlsBitmapBits[0x2]; // 44h
    PVOID ReadOnlySharedMemoryBase; // 4Ch
    PVOID ReadOnlySharedMemoryHeap; // 50h
    PVOID* ReadOnlyStaticServerData; // 54h
    PVOID AnsiCodePageData; // 58h
    PVOID OemCodePageData; // 5Ch
    PVOID UnicodeCaseTableData; // 60h
    ULONG NumberOfProcessors; // 64h
    ULONG NtGlobalFlag; // 68h
    UCHAR Spare2[0x4]; // 6Ch
    LARGE_INTEGER CriticalSectionTimeout; // 70h
    ULONG HeapSegmentReserve; // 78h
    ULONG HeapSegmentCommit; // 7Ch
    ULONG HeapDeCommitTotalFreeThreshold; // 80h
    ULONG HeapDeCommitFreeBlockThreshold; // 84h
    ULONG NumberOfHeaps; // 88h
    ULONG MaximumNumberOfHeaps; // 8Ch
    PVOID** ProcessHeaps; // 90h
    PVOID GdiSharedHandleTable; // 94h
    PVOID ProcessStarterHelper; // 98h
    PVOID GdiDCAttributeList; // 9Ch
    PVOID LoaderLock; // A0h
    ULONG OSMajorVersion; // A4h
    ULONG OSMinorVersion; // A8h
    ULONG OSBuildNumber; // ACh
    ULONG OSPlatformId; // B0h
    ULONG ImageSubSystem; // B4h
    ULONG ImageSubSystemMajorVersion; // B8h
    ULONG ImageSubSystemMinorVersion; // C0h
    ULONG GdiHandleBuffer[0x22]; // C4h
    PVOID ProcessWindowStation; // ???
} PEB, *PPEB;

内核调试基础——1/10/2023

见链接: http://blog.junyu33.me/2023/01/10/winkernel_environ.html