Posts under category 好好学习

Windows内核笔记 - R3和R0通信

大量的知识真的劝退,这边把重点重新写出来,后期在看什么重要来高亮吧

DLL的封装

  • kernel32.dll和user32.dll这种的底层函数就被包含在ntdll.dll中
  • ntdll.dll中的函数成对出现,分别以“Nt”和“Zw”开头,这种函数叫做Native API

前排提醒

ntdll.dll中的Nt和Zw与ntoskrnl.exe中的Nt和Zw不一样

  • ntdll.dll中的Nt*函数为ntdll!Nt
  • ntoskrnl.exe中的Nt*函数为nt!Nt
  • Zw*同理

系统调用

  • 通过中断指令'int 0x2e'
    _KiIntSystemCall@0:
    lea edx, [esp+8]
    int 0x2e
    ret
  • 通过指令'sysenter'
    _KiFastSystemCall@0:
    mov edx,csp
    sysenter
    _KiFastSystemCallRet@0:
    ret
  • 不管是中断式系统调用(通过中断指令'int 0x2e')还是快速系统调用(通过指令'sysenter'),在调用函数时都是通过将函数所对应的服务号(SSDT数组中的索引值)放入EAX中来传递的(详见后面的SSDT)
  • 'int 0x2e'用于xp以下版本
  • 'sysenter'用于xp及以上版本
  • 通过系统调用,能让R3层进入R0层
  • 'KUSER_SHARED_SYSCALL'指向KiIntSystemCall()或KiFastSystemCall()两个函数其中之一,实际开发中,直接
    mov ecx, KUSER_SHARED_SYSCALL
    CALL [ecx]

SSDT

  • SSDT是系统服务描述符表,这个表在内核ntoskrnl.exe中
  • 这个表的作用是将函数对应的服务号和nt!Nt*的API一一对应
  • SSDT查表操作在系统调用中发生,由于_KiFastSystemCall/_KiFastSystemCall是内核的总入口(就它们俩中的一个),在调用nt!Nt函数时需要将函数所对应的服务号放入EAX中来传递然后_KiFastSystemCall/_KiFastSystemCall中的'int 0x2e'/'sysenter'里面就会进行一个查表操作,把EAX传递过去的服务号当作索引来找到nt!Nt函数,然后调用这个nt!Nt*函数

从用户模式调用Native API

585678_67hi0eqg6aho6la (1).jpg

  • 通过ntdll!NT和ntdll!Zw调用是没区别的,参数都会经过严格审核
  • 图中的中介函数是ntdll!Nt,内核函数实现是nt!Nt

从内核模式调用Native API

585678_fltl4j7rhfeddup.jpg

  • 如果是快速调用的话,_KiSystemService换成_KiFastCallEntry
  • 图中的Zw是nt!Zw,Nt是nt!Nt

Previous Mode(先前模式)

  • 从用户态调用Native API则Previous Mode为用户态,Native API对传入的参数进行严格的审查
  • 从内核态调用Native API则Previous Mode为内核态,Native API不会对传入的参数进行严格的审查

Nt和Zw的区别

  • 调用nt!Nt*的API会直接调用对应的代码,Previous Mode很可能为用户态
  • 调用nt!Zw的API会通过KiSystemService/KiFastCallEntry最终跳转到对应函数代码处(nt!Nt处),Previous Mode会被设为内核态

直接调用nt!Nt*会位于用户态的原因

  • 由于内核组件可能在各任意线程的上下文中

The NtXxxx version of the native system service is the name of the function itself. Thus, when a Kernel Mode component calls the NtXxxx version of the system service, whatever is presently set into previous mode is unchanged. Thus, it is quite possible that the Kernel component could be running on an arbitrary User stack, with the requestor mode set to User. The system service will not know any better, attempt to validate the request parameters, possibly using the credentials of the arbitrary User Mode thread, and thus possibly fail the request. Another problem here is that one step in the validation process for a User Mode request is that all passed in buffers have either ProbeForRead or ProbeForWrite executed on them, depending on the buffer’s usage. These routines raise exceptions if executed on Kernel Mode addresses. Therefore, if you pass in Kernel Mode buffers with your request mode set to User, your calls into the native API return STATUS_ACCESS_VIOLATION.

  • 开发内核模式驱动的时候,可以通过nt!Zw*来避免额外的参数检查
  • 如果想要避免杀毒软件在SSDT中上Hook可以直接调用nt!Nt。要自己调用nt!Nt的话必须先将Previous Mode设为内核模式(KernelMode)。(详见:https://bbs.pediy.com/thread-55142.htm

参考

  1. 段钢-加密与解密
  2. https://bbs.pediy.com/thread-177772.htm
  3. http://www.osronline.com/article.cfm%5earticle=257.htm

2020KCTF看雪攻防赛第一题解析

第一题 十二生肖

人生中的第一道攻防题被我做出来了哈哈,看雪名次第86(我没报名提交不了),但当时只有85个人提交哦
补充:然后后来写完这篇文章我就去报名了,114名了。。。
其实答案早就算出来了,不知道干啥我跑到atoi里看半天(不熟悉C/C++的后果,立个Flag,学好C++)

题目

https://ctf.pediy.com/game-season_fight-135.htm
spring2020.rar

工具

1. IDA

QQ图片20200415110346.jpg

2. OllyDbg

images.jpg

解析

打开IDA,看String,发现good
跳转到使用这个数据的代码段
QQ截图20200415002852.png
看sub_401750的xrefs,我们来到401a3b
QQ截图;20200415003115.png
我们发现

.text:00401A3B                 call    sub_401750

这一行会导致弹出“right”;

text:00401A42                 call    sub_401790

这一行会导致弹出“wrong”
所以我们要让eax=50h
QQ截图20200415003329.png
我们从00401A36开始倒着推分析:要让eax=50h,在

.text:00401A34                 sar     eax, 1   

这一行前面要让eax=A0h

.text:00401A31                 cdq
.text:00401A32                 sub     eax, edx

再看这个cdq,cdq把EAX的第31bit复制到EDX的每一个bit上,换句话说:

如果eax为正数,edx将会为0
如果eax为负数,edx将会为-1

然后分析“sub eax, edx”:

如果eax为正数,eax=eax-edx=eax=0
如果eax为负数,eax=eax-(-1)=eax+1

要让eax=A0h,eax为负数的话怎么弄都不可能让eax变成A0h,因为给1个负整数+1是必然小于等于0的
所以eax一定是正数,且eax=A0h
现在我们开始正着推(好像我分析的时候是先正着推的?我忘了。。。)
QQ截图20200415004126.png
打开OD,在401a1b处下断点,然后运行-输入“123456”-按确认,程序被中断了,然后eax为字符串的“123456”
QQ截图20200415004525.png
然后F8往后走,发现atoi被调用了,且“123456”是参数
atoi是把字符串变成整数的参数
所以在调用完毕后,eax中存放的就是输入的字符串的整数型
QQ截图20200415004525.png
F8走到call atoi后面,发现eax果然就是输入的字符串的整数型
(1E240h=123456)(十六进制1E240=十进制123456)
QQ图片20200415004945.png
回到ida,.text:00401A28开始的那三行仔细看看就知道完全没用(秘制代码),我们正着推的和逆着推的总算碰到一块了

总结/结论/答案

放进去的是A0h的十进制字符串就对了,A0h的十进制字符串就是这道题的答案:160