; getSegInfo.asm
; 读Win32 平台上用户级进程(其实就是本线程)的各段寄存器的值,并解析其指向的段的信息
; 是对WOWOCOCK的一个例子程序的改写
include common.asm
.data
strtable tblSregsNames, TXT("cs"), TXT("ds"), TXT("ss"), TXT("es"), TXT("fs"), TXT("gs"), \
TXT("ldtr"), TXT("tr")
strtable tblTableNames, TXT("gdt"), TXT("ldt")
strtable tblPresent, TXT("NOT present"), TXT("present")
strtable tblGran, TXT("byte"), TXT("4KB")
strtable tblDataSegClass,\
TXT('不可写数据段'),TXT('不可写数据段, 已访问'),TXT('可写数据段'),TXT('可写数据段, 已访问'),\
TXT('不可写数据段, 向下扩展'),TXT('不可写数据段, 向下扩展,已访问'),\
TXT('可写数据段, 向下扩展'),TXT('可写数据段, 向下扩展,已访问')
strtable_cont TXT('不可读代码段'),TXT('不可读代码段, 已访问'),TXT('可读代码段'),TXT('可读代码段, 已访问'),\
TXT('不可读代码段, 一致性'),TXT('不可读代码段, 一致性, 已访问'),\
TXT('可读代码段, 一致性'),TXT('可读代码段, 一致性, 已访问')
strtable tblSystemClass,TXT('保留'),TXT('16位TSS段(可用)'),TXT('LDT段'),TXT('16位TSS段(忙)'),\
TXT('16位调用门'),TXT('任务门'), TXT('16位中断门'),TXT('16位陷阱门')
strtable_cont TXT('保留'),TXT('32位TSS段(可用)'), TXT('保留'),TXT('32位TSS段(忙)') ,\
TXT('32位调用门'),TXT('保留'), TXT('32位中断门'),TXT('32位陷阱门')
strtable tblClasses, offset tblSystemClass, offset tblDataSegClass ; 二级表
.code
PrintSegInfo proc uses ebx esi edi pszBuf, pszSegName, selector
local @szBuf[256]:byte
; 检查有效性
.if !(selector & 0FFF8H) ;NULL
invoke wsprintf,pszBuf,CTXT('%s = %04X --> null', CR, LF),\
pszSegName,selector
ret
.endif
mov eax, selector
mov edx, eax
mov ecx, eax
mov ebx, eax
and eax, 011B ;get RPL
and edx, 0FFF8H ; get offset in table
shr ecx, 3 ; get index into table
shr ebx, 2
and ebx, 1 ; get table index
invoke wsprintf,pszBuf,\
CTXT('%s = %04X: ti = %d, 指向的段描述符是%s的第%04X个(offset = %04X), rpl = %d', CR, LF),\
pszSegName, selector, ebx, tblTableNames[ebx*4], ecx, edx, eax
;msgbox ,pszBuf
comment ^
lar : load access rights byte 全取出来
lar r16, r/m16
r16<-描述符的高32位 masked by FF00H
(jcw: mask即and 位与)
lar r32, r/m32
r32<-描述符的高32位 masked by 00FxFF00H
获得指定段选择子对应的段描述符描述的段的存取权限字节。若加载成功,置ZF,否则清ZF。
如果操作数长度是32位的,那么获得的存取权限值包括type, DPL字段,以及S,P,AVL,D/B,G标志,这些
原来都在段描述符的高双字中。
本指令执行时会做的检查同lsl指令,除了对本指令有效的描述符还有调用门。
^
lar ebx, selector
.if !zero? ;ZF=0
; 选择子可能超出所在表的范围; 指向数据段或非一致代码段且其DPL特权级高于CPL或选择子的RPL级别;
; 是以下系统描述符:16位中断门、16位陷阱门、32位中断门、32位陷阱门、保留类型
invoke lstrcat,pszBuf, \
CTXT(' |',CR,LF,' 选择子无效, 或对应的描述符因为其类型或特权级而在当前特权级下不允许被访问',CR,LF,CR,LF)
ret
; 取不到属性字节的选择子更不会取到段偏移上限, 也不会是在当前特权级下可读或可写的代码段或数据段
.endif
; got access rights: 输出各种属性信息
invoke lstrcat,pszBuf,CTXT(' --> 描述符属性: ',CR,LF)
.if ebx & 8000h ;P
mov eax, 1
.else
mov eax, 0
.endif
invoke wsprintf,addr @szBuf,CTXT(TAB,'p = %d, %s',CR,LF),eax,tblPresent[eax*4]
invoke lstrcat,pszBuf,addr @szBuf
;msgbox ,addr @szBuf
.if ebx & 1000h ;S
mov eax, 1
.else
mov eax, 0
.endif
mov esi, tblClasses[eax*4]
mov edx, ebx
shr edx, 8
and edx, 0Fh ; TYPE
invoke wsprintf,addr @szBuf,CTXT(TAB,'s = %d, type = %X, %s; '),\
eax, edx, dword ptr [esi+edx*4]
invoke lstrcat,pszBuf,addr @szBuf
;msgbox ,addr @szBuf
.if ebx & 400000H ; D/B =32
invoke wsprintf,addr @szBuf,CTXT('d/b = %d, 默认操作数长度为%d位; '),1,32
.else ;16
invoke wsprintf,addr @szBuf,CTXT('d/b = %d, 默认操作数长度为%d位; '),0,16
.endif
invoke lstrcat,pszBuf,addr @szBuf
;msgbox ,addr @szBuf
mov eax, ebx
shr eax, 13
and eax, 3 ; DPL
mov edx, ebx
shr edx, 20
and edx, 1 ; avl
invoke wsprintf,addr @szBuf,\
CTXT('dpl = %d',CR,LF,TAB,'avl(软件可利用位) = %d; '),eax,edx
invoke lstrcat,pszBuf,addr @szBuf
mov eax, ebx
shr eax, 23
and eax, 1
invoke wsprintf,addr @szBuf,CTXT('g = %d, 段偏移上限的单位为%s',CR,LF),\
eax,tblGran[eax*4]
invoke lstrcat,pszBuf,addr @szBuf
; 看是不是数据、代码段或是系统段;调用门、任务门没有段偏移上限这个属性
;.if ebx & 1000h ; S
; ;get_seg_limit ; code / data seg
; .endif
; 不用麻烦了,直接执行lsl,看ZF
comment ^
lsl: load segment limit 加载指定段选择子关联的段的偏移上限
lsl r16, r/m16
load: r16<-segment limit, selector r/m16
lsl r32, r/m32
load: r32<-segment limit, selector r/m32
若加载成功,获得指定段选择子对应的段描述符经解码后的段限长。若加载成功,置ZF,否则清ZF。
段限长包含在段描述符的第0和第1字节以及第6字节的低4位中。
得到的结果是以字节计。若段描述符的段限长粒度为4KB,会先被换算成粒度为1字节的值。
若操作数长度为16位,计算的结果值是有效的32位值,那么高16位会被截掉,只保留低16位到目的寄存器中。
本指令执行时会做以下检查:
选择子不是NULL;
选择子指向的描述符是在GDT或LDT的限长范围内;
描述符类型是对本指令有效的:所有代码和数据段描述符是有效的。此外还有LDT、Task段描述符;
若该段不是一致代码段,指令会检查该段描述符在当前特权级(CPL)下是否可见,即CPL、段选择子的RPL是否小于或等于段选择子的DPL。
^
;get seg limit
lsl eax, selector
.if !zero? ; 说明是调用门、任务门
ret
.endif
;got seg limit
invoke wsprintf,addr @szBuf,CTXT(TAB,'段内偏移上限为%08X',CR,LF), eax
invoke lstrcat,pszBuf,addr @szBuf
; 是不是数据或代码段,如果是系统段如TSS,则不能使用verr/verw
.if ! (ebx & 1000h) ; system段
ret
.endif
comment ^
verr/verw: verify a segment for reading or writing
verr/verw r/m16 ; set ZF=1 if segment specified with r/m16 can be read/written
verify whether the code or data segment specified with the source operand is readable or writable from the current privilege level(CPL).
If the segment is accessible and readable (VERR) or writable (VERW), the ZF flag is set; otherwise, the ZF flag is cleared. Code segments are never verified as writable. This check cannot be performed on system segments.
执行完设置ZF的条件如下:
选择子不是NULL;
选择子指向的描述符是在GDT或LDT的限长范围内;
选择子指向的段必须是代码段或数据段(不能是系统段或门);
该段可读(VERR)或该段是可写的数据段(VERW);
若该段不是一致代码段,指令会检查该段描述符在当前特权级(CPL)下是否可见,即CPL、段选择子的RPL是否小于或等于段选择子的DPL。
^
;verify r w: 在当前特权级下可写不?
verr word ptr selector
.if zero?
invoke lstrcat,pszBuf,CTXT('在当前特权级下可读; ')
.else
invoke lstrcat,pszBuf,CTXT('在当前特权级下不可读; ')
.endif
verw word ptr selector
.if zero?
invoke lstrcat,pszBuf,CTXT('在当前特权级下可写', CR, LF)
.else
invoke lstrcat,pszBuf,CTXT('在当前特权级下不可写', CR, LF)
.endif
ret
PrintSegInfo endp
start proc
local @gdt:fword,@idt:fword
; fword是3个word, 即6个字节,对应df, 是far指针的意思:32位偏移再加16位段选择子
local @sregs[8] ; 各段寄存器的值,按cs,ds,ss,es,fs,gs,ldt,tr顺序存储
local @szBuf[2048]:byte
sgdt fword ptr @gdt
sidt fword ptr @idt
mov @sregs[0*4], cs ;生成的指令是mov word ptr [xxx],cs
mov @sregs[1*4], ds
mov @sregs[2*4], ss
mov @sregs[3*4], es
mov @sregs[4*4], fs
mov @sregs[5*4], gs
sldt word ptr @sregs[6*4]
str word ptr @sregs[7*4]
comment /* ; 读cs指向的段描述符
mov ebx, dword ptr @gdt+2
mov eax, cs
and eax, 0FFF8H ;去掉RPL域与TI位
mov eax, [ebx+eax] ; 一个段描述符占8个字节, 但这里不用乘以8
mov edx, [ebx+eax+4] ; edx: eax
; 上面那2条指令执行出现异常,因为gdt为Ring0级
*/
movzx eax, word ptr @gdt ; 表中最后一个字节的偏移
movzx edx, word ptr @idt
movzx ecx, word ptr @sregs[6*4] ; 16位零扩展到32位
movzx ebx, word ptr @sregs[7*4]
invoke wsprintf, addr @szBuf,\
CTXT("gdt @ %08X, 上限偏移 = %04X",CR,LF,"idt @ %08X, 上限偏移 = %04X",CR,LF,"ldtr = %04X(selector)",CR,LF,"tr = %04X(selector)",CR,LF,CR,LF),\
dword ptr @gdt+2, eax, dword ptr @idt+2, edx, ecx, ebx
;msgbox ,addr @szBuf
;mov eax, cs ; 不能用push cs, 因为其等价于sub esp, 4; mov word ptr [esp], cs
;push eax
xor ebx, ebx
.while ebx<8
invoke lstrlen, addr @szBuf
lea eax, @szBuf[eax]
movzx edx, word ptr @sregs[ebx*4] ;16位零扩展到32位
invoke PrintSegInfo, eax, tblSregsNames[ebx*4], edx
invoke lstrcat, addr @szBuf, CTXT(CR,LF)
inc ebx
.endw
msgbox ,addr @szBuf,CTXT("查看系统寄存器及段的信息")
ret
start endp
end start
; 读Win32 平台上用户级进程(其实就是本线程)的各段寄存器的值,并解析其指向的段的信息
; 是对WOWOCOCK的一个例子程序的改写
include common.asm
.data
strtable tblSregsNames, TXT("cs"), TXT("ds"), TXT("ss"), TXT("es"), TXT("fs"), TXT("gs"), \
TXT("ldtr"), TXT("tr")
strtable tblTableNames, TXT("gdt"), TXT("ldt")
strtable tblPresent, TXT("NOT present"), TXT("present")
strtable tblGran, TXT("byte"), TXT("4KB")
strtable tblDataSegClass,\
TXT('不可写数据段'),TXT('不可写数据段, 已访问'),TXT('可写数据段'),TXT('可写数据段, 已访问'),\
TXT('不可写数据段, 向下扩展'),TXT('不可写数据段, 向下扩展,已访问'),\
TXT('可写数据段, 向下扩展'),TXT('可写数据段, 向下扩展,已访问')
strtable_cont TXT('不可读代码段'),TXT('不可读代码段, 已访问'),TXT('可读代码段'),TXT('可读代码段, 已访问'),\
TXT('不可读代码段, 一致性'),TXT('不可读代码段, 一致性, 已访问'),\
TXT('可读代码段, 一致性'),TXT('可读代码段, 一致性, 已访问')
strtable tblSystemClass,TXT('保留'),TXT('16位TSS段(可用)'),TXT('LDT段'),TXT('16位TSS段(忙)'),\
TXT('16位调用门'),TXT('任务门'), TXT('16位中断门'),TXT('16位陷阱门')
strtable_cont TXT('保留'),TXT('32位TSS段(可用)'), TXT('保留'),TXT('32位TSS段(忙)') ,\
TXT('32位调用门'),TXT('保留'), TXT('32位中断门'),TXT('32位陷阱门')
strtable tblClasses, offset tblSystemClass, offset tblDataSegClass ; 二级表
.code
PrintSegInfo proc uses ebx esi edi pszBuf, pszSegName, selector
local @szBuf[256]:byte
; 检查有效性
.if !(selector & 0FFF8H) ;NULL
invoke wsprintf,pszBuf,CTXT('%s = %04X --> null', CR, LF),\
pszSegName,selector
ret
.endif
mov eax, selector
mov edx, eax
mov ecx, eax
mov ebx, eax
and eax, 011B ;get RPL
and edx, 0FFF8H ; get offset in table
shr ecx, 3 ; get index into table
shr ebx, 2
and ebx, 1 ; get table index
invoke wsprintf,pszBuf,\
CTXT('%s = %04X: ti = %d, 指向的段描述符是%s的第%04X个(offset = %04X), rpl = %d', CR, LF),\
pszSegName, selector, ebx, tblTableNames[ebx*4], ecx, edx, eax
;msgbox ,pszBuf
comment ^
lar : load access rights byte 全取出来
lar r16, r/m16
r16<-描述符的高32位 masked by FF00H
(jcw: mask即and 位与)
lar r32, r/m32
r32<-描述符的高32位 masked by 00FxFF00H
获得指定段选择子对应的段描述符描述的段的存取权限字节。若加载成功,置ZF,否则清ZF。
如果操作数长度是32位的,那么获得的存取权限值包括type, DPL字段,以及S,P,AVL,D/B,G标志,这些
原来都在段描述符的高双字中。
本指令执行时会做的检查同lsl指令,除了对本指令有效的描述符还有调用门。
^
lar ebx, selector
.if !zero? ;ZF=0
; 选择子可能超出所在表的范围; 指向数据段或非一致代码段且其DPL特权级高于CPL或选择子的RPL级别;
; 是以下系统描述符:16位中断门、16位陷阱门、32位中断门、32位陷阱门、保留类型
invoke lstrcat,pszBuf, \
CTXT(' |',CR,LF,' 选择子无效, 或对应的描述符因为其类型或特权级而在当前特权级下不允许被访问',CR,LF,CR,LF)
ret
; 取不到属性字节的选择子更不会取到段偏移上限, 也不会是在当前特权级下可读或可写的代码段或数据段
.endif
; got access rights: 输出各种属性信息
invoke lstrcat,pszBuf,CTXT(' --> 描述符属性: ',CR,LF)
.if ebx & 8000h ;P
mov eax, 1
.else
mov eax, 0
.endif
invoke wsprintf,addr @szBuf,CTXT(TAB,'p = %d, %s',CR,LF),eax,tblPresent[eax*4]
invoke lstrcat,pszBuf,addr @szBuf
;msgbox ,addr @szBuf
.if ebx & 1000h ;S
mov eax, 1
.else
mov eax, 0
.endif
mov esi, tblClasses[eax*4]
mov edx, ebx
shr edx, 8
and edx, 0Fh ; TYPE
invoke wsprintf,addr @szBuf,CTXT(TAB,'s = %d, type = %X, %s; '),\
eax, edx, dword ptr [esi+edx*4]
invoke lstrcat,pszBuf,addr @szBuf
;msgbox ,addr @szBuf
.if ebx & 400000H ; D/B =32
invoke wsprintf,addr @szBuf,CTXT('d/b = %d, 默认操作数长度为%d位; '),1,32
.else ;16
invoke wsprintf,addr @szBuf,CTXT('d/b = %d, 默认操作数长度为%d位; '),0,16
.endif
invoke lstrcat,pszBuf,addr @szBuf
;msgbox ,addr @szBuf
mov eax, ebx
shr eax, 13
and eax, 3 ; DPL
mov edx, ebx
shr edx, 20
and edx, 1 ; avl
invoke wsprintf,addr @szBuf,\
CTXT('dpl = %d',CR,LF,TAB,'avl(软件可利用位) = %d; '),eax,edx
invoke lstrcat,pszBuf,addr @szBuf
mov eax, ebx
shr eax, 23
and eax, 1
invoke wsprintf,addr @szBuf,CTXT('g = %d, 段偏移上限的单位为%s',CR,LF),\
eax,tblGran[eax*4]
invoke lstrcat,pszBuf,addr @szBuf
; 看是不是数据、代码段或是系统段;调用门、任务门没有段偏移上限这个属性
;.if ebx & 1000h ; S
; ;get_seg_limit ; code / data seg
; .endif
; 不用麻烦了,直接执行lsl,看ZF
comment ^
lsl: load segment limit 加载指定段选择子关联的段的偏移上限
lsl r16, r/m16
load: r16<-segment limit, selector r/m16
lsl r32, r/m32
load: r32<-segment limit, selector r/m32
若加载成功,获得指定段选择子对应的段描述符经解码后的段限长。若加载成功,置ZF,否则清ZF。
段限长包含在段描述符的第0和第1字节以及第6字节的低4位中。
得到的结果是以字节计。若段描述符的段限长粒度为4KB,会先被换算成粒度为1字节的值。
若操作数长度为16位,计算的结果值是有效的32位值,那么高16位会被截掉,只保留低16位到目的寄存器中。
本指令执行时会做以下检查:
选择子不是NULL;
选择子指向的描述符是在GDT或LDT的限长范围内;
描述符类型是对本指令有效的:所有代码和数据段描述符是有效的。此外还有LDT、Task段描述符;
若该段不是一致代码段,指令会检查该段描述符在当前特权级(CPL)下是否可见,即CPL、段选择子的RPL是否小于或等于段选择子的DPL。
^
;get seg limit
lsl eax, selector
.if !zero? ; 说明是调用门、任务门
ret
.endif
;got seg limit
invoke wsprintf,addr @szBuf,CTXT(TAB,'段内偏移上限为%08X',CR,LF), eax
invoke lstrcat,pszBuf,addr @szBuf
; 是不是数据或代码段,如果是系统段如TSS,则不能使用verr/verw
.if ! (ebx & 1000h) ; system段
ret
.endif
comment ^
verr/verw: verify a segment for reading or writing
verr/verw r/m16 ; set ZF=1 if segment specified with r/m16 can be read/written
verify whether the code or data segment specified with the source operand is readable or writable from the current privilege level(CPL).
If the segment is accessible and readable (VERR) or writable (VERW), the ZF flag is set; otherwise, the ZF flag is cleared. Code segments are never verified as writable. This check cannot be performed on system segments.
执行完设置ZF的条件如下:
选择子不是NULL;
选择子指向的描述符是在GDT或LDT的限长范围内;
选择子指向的段必须是代码段或数据段(不能是系统段或门);
该段可读(VERR)或该段是可写的数据段(VERW);
若该段不是一致代码段,指令会检查该段描述符在当前特权级(CPL)下是否可见,即CPL、段选择子的RPL是否小于或等于段选择子的DPL。
^
;verify r w: 在当前特权级下可写不?
verr word ptr selector
.if zero?
invoke lstrcat,pszBuf,CTXT('在当前特权级下可读; ')
.else
invoke lstrcat,pszBuf,CTXT('在当前特权级下不可读; ')
.endif
verw word ptr selector
.if zero?
invoke lstrcat,pszBuf,CTXT('在当前特权级下可写', CR, LF)
.else
invoke lstrcat,pszBuf,CTXT('在当前特权级下不可写', CR, LF)
.endif
ret
PrintSegInfo endp
start proc
local @gdt:fword,@idt:fword
; fword是3个word, 即6个字节,对应df, 是far指针的意思:32位偏移再加16位段选择子
local @sregs[8] ; 各段寄存器的值,按cs,ds,ss,es,fs,gs,ldt,tr顺序存储
local @szBuf[2048]:byte
sgdt fword ptr @gdt
sidt fword ptr @idt
mov @sregs[0*4], cs ;生成的指令是mov word ptr [xxx],cs
mov @sregs[1*4], ds
mov @sregs[2*4], ss
mov @sregs[3*4], es
mov @sregs[4*4], fs
mov @sregs[5*4], gs
sldt word ptr @sregs[6*4]
str word ptr @sregs[7*4]
comment /* ; 读cs指向的段描述符
mov ebx, dword ptr @gdt+2
mov eax, cs
and eax, 0FFF8H ;去掉RPL域与TI位
mov eax, [ebx+eax] ; 一个段描述符占8个字节, 但这里不用乘以8
mov edx, [ebx+eax+4] ; edx: eax
; 上面那2条指令执行出现异常,因为gdt为Ring0级
*/
movzx eax, word ptr @gdt ; 表中最后一个字节的偏移
movzx edx, word ptr @idt
movzx ecx, word ptr @sregs[6*4] ; 16位零扩展到32位
movzx ebx, word ptr @sregs[7*4]
invoke wsprintf, addr @szBuf,\
CTXT("gdt @ %08X, 上限偏移 = %04X",CR,LF,"idt @ %08X, 上限偏移 = %04X",CR,LF,"ldtr = %04X(selector)",CR,LF,"tr = %04X(selector)",CR,LF,CR,LF),\
dword ptr @gdt+2, eax, dword ptr @idt+2, edx, ecx, ebx
;msgbox ,addr @szBuf
;mov eax, cs ; 不能用push cs, 因为其等价于sub esp, 4; mov word ptr [esp], cs
;push eax
xor ebx, ebx
.while ebx<8
invoke lstrlen, addr @szBuf
lea eax, @szBuf[eax]
movzx edx, word ptr @sregs[ebx*4] ;16位零扩展到32位
invoke PrintSegInfo, eax, tblSregsNames[ebx*4], edx
invoke lstrcat, addr @szBuf, CTXT(CR,LF)
inc ebx
.endw
msgbox ,addr @szBuf,CTXT("查看系统寄存器及段的信息")
ret
start endp
end start