关于定位lsass内存中的明文密码(转)

所谓“不到黄河心不死”、“不撞南墙不回头”、“不见棺材不掉泪”,人是不容易死心的。在没搞清楚lsass内存中明文密码从哪里来到哪里去之前,对它总抱有一丝幻想。最早是在netxeyes看到关于lsass里有明文密码的帖子,当时就想跟踪一下,确定它的位置。后来在安焦又见有人提起,于是建议高手研究一下,可是没有回应。可能是因为高手都一眼看出这没有研究价值。为了消灭“最终の幻想”,我只好自己动手。对于Debug我是很菜的,乘机练习一下。

在VMWare上登陆几次win2003,密码位置一样。好,有可重复性。再挂上WinDbg。因为要跟踪登陆过程,用内核调试器比较好。用户态调试器也可以,在注册表里把lsass.exe的debugger设置为ntsd就行。不过WinDbg的窗口模式用着比较爽。

先找到lsass.exe

kd> !process 0 0
**** NT ACTIVE PROCESS DUMP ****
PROCESS 80ead020  SessionId: none  Cid: 0004    Peb: 00000000  ParentCid: 0000
    DirBase: 00039000  ObjectTable: e1000d58  HandleCount: 184.
    Image: System

PROCESS ffbdd868  SessionId: none  Cid: 0174    Peb: 7ffdf000  ParentCid: 0004
    DirBase: 0324f000  ObjectTable: e1274f58  HandleCount:  17.
    Image: smss.exe

PROCESS 80d7c970  SessionId: 0  Cid: 01ac    Peb: 7ffdf000  ParentCid: 0174
    DirBase: 0414c000  ObjectTable: e12bb720  HandleCount: 243.
    Image: csrss.exe

PROCESS ffb73440  SessionId: 0  Cid: 01c4    Peb: 7ffdf000  ParentCid: 0174
    DirBase: 00459000  ObjectTable: e14aab18  HandleCount: 417.
    Image: winlogon.exe

PROCESS 80d9f7d8  SessionId: 0  Cid: 01f0    Peb: 7ffdf000  ParentCid: 01c4
    DirBase: 001b0000  ObjectTable: e15e6ea0  HandleCount: 254.
    Image: services.exe

PROCESS 80d91550  SessionId: 0  Cid: 01fc    Peb: 7ffdf000  ParentCid: 01c4
    DirBase: 03343000  ObjectTable: e15e9e08  HandleCount: 375.
    Image: lsass.exe

PROCESS ffb88b08  SessionId: 0  Cid: 02a4    Peb: 7ffdf000  ParentCid: 01f0
    DirBase: 00914000  ObjectTable: e163c108  HandleCount: 146.
    Image: svchost.exe

PROCESS ffb8c690  SessionId: 0  Cid: 02e0    Peb: 7ffdf000  ParentCid: 01f0
    DirBase: 03164000  ObjectTable: e16628c8  HandleCount: 112.
    Image: svchost.exe

PROCESS ffb557d8  SessionId: 0  Cid: 0350    Peb: 7ffdf000  ParentCid: 01f0
    DirBase: 004d6000  ObjectTable: e17290a0  HandleCount:  55.
    Image: svchost.exe

PROCESS ffb52938  SessionId: 0  Cid: 035c    Peb: 7ffdf000  ParentCid: 01f0
    DirBase: 00494000  ObjectTable: e17134e0  HandleCount:  76.
    Image: svchost.exe

PROCESS ffb4d7f0  SessionId: 0  Cid: 038c    Peb: 7ffdf000  ParentCid: 01f0
    DirBase: 00e19000  ObjectTable: e174b078  HandleCount: 270.
    Image: svchost.exe

PROCESS ffb34620  SessionId: 0  Cid: 040c    Peb: 7ffdf000  ParentCid: 01f0
    DirBase: 0adb3000  ObjectTable: e176df38  HandleCount: 156.
    Image: inetinfo.exe

PROCESS ffb2c020  SessionId: 0  Cid: 0434    Peb: 7ffdf000  ParentCid: 01f0
    DirBase: 0ab79000  ObjectTable: e168a218  HandleCount:  45.
    Image: VMwareService.exe

PROCESS ff9e2d88  SessionId: 0  Cid: 0500    Peb: 7ffdf000  ParentCid: 01f0
    DirBase: 09d6c000  ObjectTable: e1879210  HandleCount: 136.
    Image: svchost.exe

切换到lsass的进程空间,然后在明文密码的起始位置设一个内存读写断点。

kd> .context 03343000
WARNING: .cache forcedecodeuser is not enabled

我的虚拟机上,密码总是从0x002b5cd0开始。

kd> ba r2 /p 80d91550 002b5cd0
kd> g

然后登陆,过一会就到断到了。

Breakpoint 0 hit
001b:77d7d9a9 f3a5        rep     movsd

重新加载symbol

kd> .reload
Connected to Windows Server 2003 3790 x86 compatible target, ptr64 FALSE
Loading Kernel Symbols
..............................................................................................
Loading unloaded module list
..
Loading User Symbols
...........................................................

看一下寄存器

kd> r
eax=00000000 ebx=00000010 ecx=00000003 edx=00000010 esi=00082014 edi=002b5cd4
eip=77d7d9a9 esp=00b3faac ebp=002b5cb8 iopl=0         nv up ei pl nz ac pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000212
ADVAPI32!MD4Update+0x113:
001b:77d7d9a9 f3a5       rep  movsd ds:00082014=006b0068 es:002b5cd4=00000000

中断在一个串传送指令上。因为此时该指令已经执行过一次了,所以源地址是esi-4

kd> du esi-4
00082010  "ph4nt0m!.."    <-- 看到密码了

密码从哪里来的?当然是作为参数传递进来的,查一下堆栈。

kd> kb
ChildEBP RetAddr  Args to Child
00b3fab8 76624d61 00000000 00082010 00000010 ADVAPI32!MD4Update+0x113
00b3fac8 766258de 002b5cb8 00000010 00082010 cryptdll!md4Sum+0x11
00b3fae4 71c388be 00b3fb00 00081ff8 00093b60 cryptdll!rc4HmacHashPassword+0x3e
00b3fb0c 71c189c7 00b3fd74 00b3fb94 00000017 kerberos!_imp__lstrlenA <PERF> (kerberos+0x388be)
00b3fbb0 71c09d1c 00b3fd74 0000009c 00093aa0 kerberos!KerbCreateEmptyContext+0xd
00b3fbec 71c2bbd0 00000000 00b3fd6c 00b3fd74 kerberos!KerbGetTgsTicket+0x477
00b3fc1c 71c1f34b 00093a40 00b3fd64 00b3fd6c kerberos!KRB5_Module_Startup+0xa886
00b3fc70 7423d44a 00000002 000cf1b8 00b3fd5c kerberos!KerbQueryTicketCache+0x231
00b3fcc4 74246b56 00000002 000cf1b8 00b3fd5c LSASRV!LsaIAuditAccountLogon+0x15b
00b3fd00 7421f583 00000002 000cf1b8 006110f8 LSASRV!LsarSetInformationTrustedDomain+0x2
00b3fe74 74236843 00b3fe8c 005f8e98 00000000 LSASRV!NegHandleClientRequest+0x523
00b3fe84 74208cbf 00602270 00625ce8 005f8e98 LSASRV!LsapCaptureSamInfo+0x84
00000000 00000000 00000000 00000000 00000000 LSASRV!NegpAcquireCredHandle+0x20e

可以看到,ADVAPI32!MD4Update第二个参数0x00082010正是密码的源地址,后面的0x10则是密码长度。
注意到cryptdll!md4Sum也有同样的参数,只是顺序不同,它的第一个参数0x002b5cb8什么意思呢?

kd> dd 002b5cb8
002b5cb8  67452301 efcdab89 98badcfe 10325476  <-- 一串很有规律的值
002b5cc8  00000080 00000000 00340035 00000000
002b5cd8  00000000 00000000 00000000 00000000
002b5ce8  00000000 00000000 00000000 00000000
002b5cf8  00000000 00000000 00000000 00000000
002b5d08  00000000 00000000 e0cfd631 31e96ad1
002b5d18  d7593cb7 c089c0e0 000e0013 000c0182
002b5d28  00000344 ffffffff 00000000 00000000

结合函数名md4Sum和MD4Update,我们很容易知道,那串有规律的值其实是MD4算法要求的hash初始值。
继续查看各个函数的入口参数,我们还能发现一些有趣的东西。

kd> dS 00b3fb00
00082010  "ph4nt0m!"
kd> dS 00b3fd74
00082010  "ph4nt0m!"
kd> dS 000cf1b8
01284660  "Administrator"

用户名和密码的源头一直可以追溯到LSASRV!NegHandleClientRequest。而真正的源头则是winlogon,它通过LPC将用户在登陆界面输入的内容传递给lsass。但是,这些都不是我们关心的。既然lsass内存里只能找到一处明文密码,那就说明这些源密码、源源密码最后都被清零了。我们只关心那个串操作的目的地址是怎么来的。这需要反汇编ADVAPI32!MD4Update。先把经过分析后的函数原型写出来:

void MD4Update(MDstruct *pMD4, WCHAR *pPassword, ULONG PassLeng);

其中第一个参数pMD4是个结构指针,指向下面这个结构。
typedef _MD4 {
    ULONG Buffer[0]; // = 0x67452301;        // Initial values for MD4
    ULONG Buffer[1]; // = 0xefcdab89;
    ULONG Buffer[2]; // = 0x98badcfe;
    ULONG Buffer[3]; // = 0x10325476;
    LARGE_INTEGER Count; // by bit
    WCAHR String[32];
    USHORT Hash[16];
} MD4, *pMD4

这个结构是参考了RFC1186中MD4的数据结构MDstruct,再加上后面的分析得出的。

下面来看MD4Update。

kd> u ADVAPI32!MD4Update L40
ADVAPI32!MD4Update:
77d7d93a 53               push    ebx
77d7d93b 8b5c2410         mov     ebx,[esp+0x10]         ;ebx=PassLeng (by byte)
77d7d93f 55               push    ebp
77d7d940 8b6c240c         mov     ebp,[esp+0xc]          ;ebp=pMD4
77d7d944 8b4d10           mov     ecx,[ebp+0x10]         ;ecx=pMD4->Count.LowPart (by bit)
77d7d947 8bc1             mov     eax,ecx
77d7d949 c1e803           shr     eax,0x3                ;eax=pMD4->Count.LowPart/8 (by byte)
77d7d94c 8d0cd9           lea     ecx,[ecx+ebx*8]        ;ecx+=PassLeng*8
77d7d94f 83e03f           and     eax,0x3f               ;eax%=64
77d7d952 8d14dd00000000   lea     edx,[00000000+ebx*8]   ;edx=PassLeng*8
77d7d959 3bca             cmp     ecx,edx
77d7d95b 56               push    esi
77d7d95c 57               push    edi
77d7d95d 89442414         mov     [esp+0x14],eax         ;pMD4=eax
77d7d961 894d10           mov     [ebp+0x10],ecx         ;pMD4->Count.LowPart+=PassLeng*8
77d7d964 0f82437e0100     jb      ADVAPI32!MD4Update+0x2c (77d957ad)
77d7d96a 8b5514           mov     edx,[ebp+0x14]         ;edx=pMD4->Count.HighPart (by bit)
77d7d96d 8bcb             mov     ecx,ebx
77d7d96f c1e91d           shr     ecx,0x1d               ;ecx=edx/0x20000000
77d7d972 03d1             add     edx,ecx                ;edx=PassLeng*8+edx/0x20000000
77d7d974 85c0             test    eax,eax                ;第一次调用时pMD4->Count为0,所以此时eax为0
77d7d976 895514           mov     [ebp+0x14],edx         ;pMD4->Count.HighPart=edx
77d7d979 0f8761ffffff     jnbe    ADVAPI32!MD4Update+0x40 (77d7d8e0)
77d7d97f f644241803       test    byte ptr [esp+0x18],0x3;检测pPassword是否4K对齐
77d7d984 0f85f3f8ffff     jne     ADVAPI32!MD4Update+0x96 (77d7d27d)
77d7d98a 83fb40           cmp     ebx,0x40               ;PassLeng>64则跳转
77d7d98d 7325             jnb     ADVAPI32!MD4Update+0xdb (77d7d9b4)
77d7d98f 85db             test    ebx,ebx                ;PassLeng!=0则跳转
77d7d991 7507             jnz     ADVAPI32!MD4Update+0x104 (77d7d99a)
77d7d993 5f               pop     edi
77d7d994 5e               pop     esi
77d7d995 5d               pop     ebp
77d7d996 5b               pop     ebx
77d7d997 c20c00           ret     0xc
77d7d99a 8b742418         mov     esi,[esp+0x18]         ;esi=pPassword <-跳到这里
77d7d99e 8bcb             mov     ecx,ebx                ;ecx=PassLeng
77d7d9a0 8bd1             mov     edx,ecx
77d7d9a2 c1e902           shr     ecx,0x2
77d7d9a5 8d7c2818         lea     edi,[eax+ebp+0x18]     ;edi=pMD4->String
77d7d9a9 f3a5             rep     movsd                  ;memcpy(pMD4->String, pPassword, PassLeng)
77d7d9ab 8bca             mov     ecx,edx
77d7d9ad 83e103           and     ecx,0x3
77d7d9b0 f3a4             rep     movsb
77d7d9b2 ebdf             jmp     ADVAPI32!MD4Update+0x11c (77d7d993)    ;return
77d7d9b4 8bf3             mov     esi,ebx                ;以下处理PassLeng>64的情况,不关心
77d7d9b6 c1ee06           shr     esi,0x6
77d7d9b9 8b442418         mov     eax,[esp+0x18]
77d7d9bd 50               push    eax
77d7d9be 55               push    ebp
77d7d9bf e8b3faffff       call    ADVAPI32!MD4Transform (77d7d477)
77d7d9c4 8b542418         mov     edx,[esp+0x18]
77d7d9c8 83c240           add     edx,0x40
77d7d9cb 83eb40           sub     ebx,0x40
77d7d9ce 4e               dec     esi
77d7d9cf 89542418         mov     [esp+0x18],edx
77d7d9d3 75e4             jnz     ADVAPI32!MD4Update+0xe0 (77d7d9b9)
77d7d9d5 8b442414         mov     eax,[esp+0x14]
77d7d9d9 ebb4             jmp     ADVAPI32!MD4Update+0x100 (77d7d98f)
77d7d9db 90               nop
77d7d9dc 90               nop
77d7d9dd 90               nop
77d7d9de 90               nop
77d7d9df 90               nop
ADVAPI32!MD5Init:
77d7d9e0 8b442404         mov     eax,[esp+0x4]

现在知道,我们看到的明文密码是pMD4的一部分:pMD4->String。那么pMD4从哪里来的?
继续往前追。

kd> u cryptdll!md4Sum
cryptdll!md4Sum:
76624d50 ff742408         push    dword ptr [esp+0x8]
76624d54 ff742410         push    dword ptr [esp+0x10]
76624d58 ff74240c         push    dword ptr [esp+0xc]
76624d5c e89f100000       call    cryptdll!MD4Update (76625e00)
76624d61 33c0             xor     eax,eax
76624d63 c20c00           ret     0xc
cryptdll!md4Finalize:
76624d66 90               nop
76624d67 90               nop

md4Sum果然只是换了一下参数位置而已。函数原型:

BOOL md4Sum(MDstruct *pMD4, ULONG PassLeng, WCHAR *pPassword);

前面已经知道rc4HmacHashPassword的第一个参数是个PUNICODE_STRING,指向源密码。
分析后的函数原型:

NTSTATUS rc4HmacHashPassword(PUNICODE_STRING uPassword, USHORT *pHash);

kd> u cryptdll!rc4HmacHashPassword L30
cryptdll!rc4HmacHashPassword:
766258a0 55               push    ebp
766258a1 8bec             mov     ebp,esp
766258a3 51               push    ecx                  ;居然是__fastcall
766258a4 51               push    ecx
766258a5 8d45fc           lea     eax,[ebp-0x4]        ;得先分析CDLocateCheckSum才知道ebp-0x4是什么
766258a8 50               push    eax
766258a9 6a02             push    0x2
766258ab e890b8ffff       call    cryptdll!CDLocateCheckSum (76621140)
766258b0 85c0             test    eax,eax
766258b2 7d07             jge    cryptdll!rc4HmacHashPassword+0x1b (766258bb)
766258b4 b842030880       mov     eax,0x80080342
766258b9 eb3b             jmp    cryptdll!rc4HmacHashPassword+0x56 (766258f6)
766258bb 8d45f8           lea     eax,[ebp-0x8]        ;ebp-0x8也有待后面分析
766258be 50               push    eax
766258bf 8b45fc           mov     eax,[ebp-0x4]
766258c2 6a00             push    0x0
766258c4 ff500c           call    dword ptr [eax+0xc]
766258c7 85c0             test    eax,eax
766258c9 7c2b             jl     cryptdll!rc4HmacHashPassword+0x56 (766258f6)
766258cb 8b4508           mov     eax,[ebp+0x8]
766258ce ff7004           push    dword ptr [eax+0x4]  ;push uPassword->Buffer
766258d1 0fb700           movzx   eax,word ptr [eax]
766258d4 50               push    eax                  ;push uPassword->Length
766258d5 ff75f8           push    dword ptr [ebp-0x8]  ;这个就是密码缓存的地址,位置待定
766258d8 8b45fc           mov     eax,[ebp-0x4]
766258db ff5010           call    dword ptr [eax+0x10] ;在这里call cryptdll!md4Sum
766258de ff750c           push    dword ptr [ebp+0xc]
766258e1 8b45fc           mov     eax,[ebp-0x4]
766258e4 ff75f8           push    dword ptr [ebp-0x8]
766258e7 ff5014           call    dword ptr [eax+0x14]
766258ea 8d45f8           lea     eax,[ebp-0x8]
766258ed 50               push    eax
766258ee 8b45fc           mov     eax,[ebp-0x4]
766258f1 ff5018           call    dword ptr [eax+0x18]
766258f4 33c0             xor     eax,eax
766258f6 c9               leave
766258f7 c20800           ret     0x8
766258fa 90               nop
766258fb 90               nop
766258fc 90               nop
766258fd 90               nop
766258fe 90               nop
766258ff 90               nop
76625900 90               nop
76625901 90               nop
76625902 90               nop
76625903 90               nop
76625904 90               nop

在搞清楚CDLocateCheckSum前,一切都不明朗。先说结论:

NTSTATUS CDLocateCheckSum(ULONG Type, PCHECK_SUM *ppChechSum);

根据所给的Type,在一组CHECK_SUM中找到需要的那个,返回其指针。

kd> u cryptdll!CDLocateCheckSum L20
cryptdll!CDLocateCheckSum:
76621140 a190806276       mov     eax,[cryptdll!cCheckSums (76628090)] ;eax=0x0000000f
76621145 85c0             test    eax,eax
76621147 7431             jz      cryptdll!CDLocateCheckSum+0x23 (7662117a)
76621149 8d0cc0           lea     ecx,[eax+eax*8]        ;结构大小=0xf*9=0x24
7662114c 8d0c8da0806276   lea     ecx,[cryptdll!CheckSumFns (766280a0)+ecx*4] ;ecx指向_CHECK_SUM
76621153 83e924           sub     ecx,0x24               ;结构指针自减
76621156 8b11             mov     edx,[ecx]              ;获取CheckSums的类型?
76621158 48               dec     eax                    ;那么eax(初值0xf)就是CHECK_SUM的总数
76621159 3b542404         cmp     edx,[esp+0x4]          ;查找类型2?
7662115d 7406             jz      cryptdll!CDLocateCheckSum+0x2b (76621165)
7662115f 85c0             test    eax,eax
76621161 7417             jz      cryptdll!CDLocateCheckSum+0x23 (7662117a)
76621163 ebee             jmp     cryptdll!CDLocateCheckSum+0x13 (76621153) ;循环
76621165 8b4c2408         mov     ecx,[esp+0x8]
76621169 8d04c0           lea     eax,[eax+eax*8]        ;根据循环变量定位结构头
7662116c 8d0485a0806276   lea     eax,[cryptdll!CheckSumFns (766280a0)+eax*4]
76621173 8901             mov     [ecx],eax              ;返回结构指针
76621175 33c0             xor     eax,eax
76621177 c20800           ret     0x8
7662117a b842030880       mov     eax,0x80080342
7662117f ebf6             jmp     cryptdll!CDLocateCheckSum+0x28 (76621177)
76621181 90               nop
76621182 90               nop
76621183 90               nop
76621184 90               nop
76621185 90               nop
76621186 90               nop
76621187 90               nop
76621188 90               nop
76621189 90               nop
7662118a 90               nop
7662118b 90               nop
kd> dd 76628090 L1
76628090  0000000f

看一下CHECK_SUM究竟有什么:

kd> dd cryptdll!CheckSumFns L40
766280a0  ffffff77 00000010 00000002 76624ed0
766280b0  76621110 76621690 766213f0 76621bf0
766280c0  00000000 ffffff76 00000010 00000002
766280d0  76624ed0 76621110 76621690 766213f0
766280e0  76621840 00000000 00000002 00000010
766280f0  00000000 76624cf0 76624d50 76624d80
76628100  766211f0 76624d30 00000000 00000007
76628110  00000010 00000000 76621220 76621110
76628120  76621260 766211f0 76621bd0 00000000
76628130  00000001 00000004 00000000 76625aa0
76628140  76625980 76625b10 766211f0 76625b60
76628150  00000000 ffffff7a 00000008 00000002
76628160  76624ed0 76625040 76624f60 76624fb0
76628170  76624ef0 00000000 ffffff79 00000008
76628180  00000002 76624ed0 76621110 76624ea0
76628190  766213f0 76624db0 00000000 ffffff7b
kd> u 76624cf0
cryptdll!md4Initialize:
76624cf0 56               push    esi
76624cf1 6a68             push    0x68
76624cf3 ff156c106276     call   dword ptr [cryptdll!_imp__malloc (7662106c)]
76624cf9 8bf0             mov     esi,eax
76624cfb 85f6             test    esi,esi
76624cfd 59               pop     ecx
76624cfe 7507             jnz     cryptdll!md4Initialize+0x17 (76624d07)
76624d00 b8170000c0       mov     eax,0xc0000017
kd> u 76624d50
cryptdll!md4Sum:
76624d50 ff742408         push    dword ptr [esp+0x8]
76624d54 ff742410         push    dword ptr [esp+0x10]
76624d58 ff74240c         push    dword ptr [esp+0xc]
76624d5c e89f100000       call    cryptdll!MD4Update (76625e00)
76624d61 33c0             xor     eax,eax
76624d63 c20c00           ret     0xc
cryptdll!md4Finalize:
76624d66 90               nop
76624d67 90               nop
kd> u 76624d80
cryptdll!md4Finalize:
76624d80 56               push    esi
76624d81 8b742408         mov     esi,[esp+0x8]
76624d85 57               push    edi
76624d86 56               push    esi
76624d87 e894100000       call    cryptdll!MD4Final (76625e20)
76624d8c 8b7c2410         mov     edi,[esp+0x10]
76624d90 83c658           add     esi,0x58
76624d93 a5               movsd
kd> u 766211f0
cryptdll!md4Finish:
766211f0 56               push    esi
766211f1 8b742408         mov     esi,[esp+0x8]
766211f5 ff36             push    dword ptr [esi]
766211f7 ff1570106276     call    dword ptr [cryptdll!_imp__free (76621070)]
766211fd 832600           and     dword ptr [esi],0x0
76621200 59               pop     ecx
76621201 33c0             xor     eax,eax
76621203 5e               pop     esi
kd> u 76624d30
cryptdll!md4InitializeEx:
76624d30 ff742410         push    dword ptr [esp+0x10]
76624d34 6a00             push    0x0
76624d36 e8b5ffffff       call    cryptdll!md4Initialize (76624cf0)
76624d3b c21000           ret     0x10
cryptdll!md4Sum:
76624d3e 90               nop
76624d3f 90               nop
76624d40 90               nop
76624d41 90               nop

原来是这样:
trpedef _CHECK_SUM {
    DWORD Type;                //??
    ULONG Unknow1;
    ULONG Unknoe2;
    PVOID Function[5];
    ULONG Unknow3;        // 总是0?
}

对于Type==2的情况,Function[5]分别指向:
cryptdll!md4Initialize
cryptdll!md4Sum
cryptdll!md4Finalize
cryptdll!md4Finish
cryptdll!md4InitializeEx

再回到rc4HmacHashPassword。

kd> u cryptdll!rc4HmacHashPassword L30
cryptdll!rc4HmacHashPassword:
766258a0 55               push    ebp
766258a1 8bec             mov     ebp,esp
766258a3 51               push    ecx
766258a4 51               push    ecx
766258a5 8d45fc           lea     eax,[ebp-0x4]        ;ebp-0x4是结构指针地址&pChechSum
766258a8 50               push    eax
766258a9 6a02             push    0x2                  ;查找类型2的CHECK_SUM
766258ab e890b8ffff       call    cryptdll!CDLocateCheckSum (76621140) ;CDLocateCheckSum(2,&pChechSum)
766258b0 85c0             test    eax,eax
766258b2 7d07             jge    cryptdll!rc4HmacHashPassword+0x1b (766258bb)
766258b4 b842030880       mov     eax,0x80080342
766258b9 eb3b             jmp    cryptdll!rc4HmacHashPassword+0x56 (766258f6)
766258bb 8d45f8           lea     eax,[ebp-0x8]        ;ebp-0x8=&pMD4
766258be 50               push    eax                  ;push &pMD4
766258bf 8b45fc           mov     eax,[ebp-0x4]        ;eax=pChechSum
766258c2 6a00             push    0x0
766258c4 ff500c           call    dword ptr [eax+0xc]  ;md4Initialize(0,&pMD4)
766258c7 85c0             test    eax,eax
766258c9 7c2b             jl     cryptdll!rc4HmacHashPassword+0x56 (766258f6)
766258cb 8b4508           mov     eax,[ebp+0x8]
766258ce ff7004           push    dword ptr [eax+0x4]  ;push uPassword->Buffer (pPassword)
766258d1 0fb700           movzx   eax,word ptr [eax]
766258d4 50               push    eax                  ;push uPassword->Length (PassLeng)
766258d5 ff75f8           push    dword ptr [ebp-0x8]  ;现在知道pMD4由md4Initialize决定
766258d8 8b45fc           mov     eax,[ebp-0x4]
766258db ff5010           call    dword ptr [eax+0x10] ;md4Sum(pMD4,PassLeng,pPassword)
766258de ff750c           push    dword ptr [ebp+0xc]
766258e1 8b45fc           mov     eax,[ebp-0x4]
766258e4 ff75f8           push    dword ptr [ebp-0x8]
766258e7 ff5014           call    dword ptr [eax+0x14] ;md4Finalize(pMD4,pHash)
766258ea 8d45f8           lea     eax,[ebp-0x8]
766258ed 50               push    eax
766258ee 8b45fc           mov     eax,[ebp-0x4]
766258f1 ff5018           call    dword ptr [eax+0x18] ;md4Finish(&pMD4)
766258f4 33c0             xor     eax,eax
766258f6 c9               leave
766258f7 c20800           ret     0x8
766258fa 90               nop
766258fb 90               nop
766258fc 90               nop
766258fd 90               nop
766258fe 90               nop
766258ff 90               nop
76625900 90               nop
76625901 90               nop
76625902 90               nop
76625903 90               nop
76625904 90               nop

再看md4Initialize:

kd> u cryptdll!md4Initialize L10
cryptdll!md4Initialize:
76624cf0 56               push    esi
76624cf1 6a68             push    0x68
76624cf3 ff156c106276     call   dword ptr [cryptdll!_imp__malloc (7662106c)]
76624cf9 8bf0             mov     esi,eax              ;pMD4=malloc(0x68)
76624cfb 85f6             test    esi,esi
76624cfd 59               pop     ecx
76624cfe 7507             jnz     cryptdll!md4Initialize+0x17 (76624d07)
76624d00 b8170000c0       mov     eax,0xc0000017
76624d05 eb0e             jmp     cryptdll!md4Initialize+0x25 (76624d15)
76624d07 56               push    esi
76624d08 e8d3100000       call    cryptdll!MD4Init (76625de0) ;MD4Init(pMD4)
76624d0d 8b44240c         mov     eax,[esp+0xc]
76624d11 8930             mov     [eax],esi
76624d13 33c0             xor     eax,eax
76624d15 5e               pop     esi
76624d16 c20800           ret     0x8

搞了半天,pMD4(包括其中的密码缓存)是由malloc分配的。这下没戏了,根本无法确定它的位置。
这一点其实用脚趾头想想也知道,但非要亲眼所见才死心。 ^_^

还有最后一线“希望”——希望lsass把密码留着别释放。

md4Initialize(0,&pMD4);
md4Sum(pMD4,PassLeng,pPassword);
之后是
md4Finalize(pMD4,pHash);

kd> u cryptdll!md4Finalize L10
cryptdll!md4Finalize:
76624d80 56               push    esi
76624d81 8b742408         mov     esi,[esp+0x8]
76624d85 57               push    edi
76624d86 56               push    esi
76624d87 e894100000       call    cryptdll!MD4Final (76625e20) ;MD4Final(pMD4)
76624d8c 8b7c2410         mov     edi,[esp+0x10]
76624d90 83c658           add     esi,0x58        ;esi=pMD4->Hash
76624d93 a5               movsd                   ;memcpy(pHash,pMD4->Hash,16)
76624d94 a5               movsd
76624d95 a5               movsd
76624d96 a5               movsd
76624d97 5f               pop     edi
76624d98 33c0             xor     eax,eax
76624d9a 5e               pop     esi
76624d9b c20800           ret     0x8
cryptdll!md4Finish:
76624d9e 90            &nbs, p;  nop

md4Finalize调用MD4Final:

kd> u cryptdll!MD4Final
cryptdll!MD4Final:
76625e20 ff2550106276     jmp  dword ptr [cryptdll!_imp__MD4Final (76621050)]

转到_imp__MD4Final:

kd> dd cryptdll!_imp__MD4Final L1
76621050  77d7d3f3
kd> u 77d7d3f3 L35
ADVAPI32!MD4Final:
77d7d3f3 83ec48           sub     esp,0x48
77d7d3f6 53               push    ebx
77d7d3f7 55               push    ebp
77d7d3f8 56               push    esi
77d7d3f9 8b742458         mov     esi,[esp+0x58]        ;esi=pMD4
77d7d3fd 8b4610           mov     eax,[esi+0x10]        ;eax=pMD4->Count.LowPart
77d7d400 8b4e14           mov     ecx,[esi+0x14]        ;ecx=pMD4->Count.HighPart
77d7d403 8d5e58           lea   &, nbsp; ebx,[esi+0x58]        ;ebx=pMD4->Hash
77d7d406 8903             mov     [ebx],eax             ;下面是一些算法的操作,不关心
77d7d408 c1e803           shr     eax,0x3               ;盯紧esi(=pMD4)就是了
77d7d40b 83e03f           and     eax,0x3f
77d7d40e 83f838           cmp     eax,0x38
77d7d411 57               push    edi
77d7d412 894e5c           mov     [esi+0x5c],ecx
77d7d415 ba38000000       mov     edx,0x38
77d7d41a 0f83d3830100     jnb     ADVAPI32!MD4Final+0x29 (77d957f3)
77d7d420 2bd0             sub     edx,eax
77d7d422 8bca             mov     ecx,edx
77d7d424 8be9             mov     ebp,ecx
77d7d426 c1e902           shr     ecx,0x2
77d7d429 33c0             xor     eax,eax
77d7d42b 8d7c2410         lea     edi,[esp+0x10]
77d7d42f f3ab             rep     stosd
77d7d431 52               push    edx
77d7d432 8d542414         lea     edx,[esp+0x14]
77d7d436 8bcd             mov     ecx,ebp
77d7d438 83e103           and     ecx,0x3
77d7d43b 52               push    edx
77d7d43c f3aa             rep     stosb
77d7d43e 56               push    esi
77d7d43f c644241c80       mov     byte ptr [esp+0x1c],0x80
77d7d444 e8f1040000       call    ADVAPI32!MD4Update (77d7d93a)
77d7d449 6a08             push    0x8
77d7d44b 53               push    ebx
77d7d44c 56               push    esi
77d7d44d e8e8040000       call    ADVAPI32!MD4Update (77d7d93a)
77d7d452 8b06             mov     eax,[esi]
77d7d454 8b4e04           mov     ecx,[esi+0x4]
77d7d457 8b5608           mov     edx,[esi+0x8]
77d7d45a 8903             mov     [ebx],eax
77d7d45c 8b460c           mov     eax,[esi+0xc]
77d7d45f 5f               pop     edi
77d7d460 894e5c           mov     [esi+0x5c],ecx
77d7d463 895660           mov     [esi+0x60],edx
77d7d466 894664           mov     [esi+0x64],eax
77d7d469 5e               pop     esi
77d7d46a 5d               pop     ebp
77d7d46b 5b               pop     ebx
77d7d46c 83c448           add     esp,0x48
77d7d46f c20400           ret     0x4
ADVAPI32!MD5Init:
77d7d472 90               nop
77d7d473 90               nop
77d7d474 90               nop

咦,怎么没释放?别高兴的太早,下面还有md4Finish呢!
md4Finish(&pMD4);

kd> u cryptdll!md4Finish L10
cryptdll!md4Finish:
766211f0 56               push    esi
766211f1 8b742408         mov     esi,[esp+0x8]
766211f5 ff36             push    dword ptr [esi]
766211f7 ff1570106276     call    dword ptr [cryptdll!_imp__free (76621070)] ;free(pMD4)
766211fd 832600           and     dword ptr [esi],0x0        ;pMD4=NULL
76621200 59               pop     ecx
76621201 33c0             xor     eax,eax
76621203 5e               pop     esi
76621204 c20400           ret     0x4
cryptdll!md5Initialize:
76621207 90               nop
76621208 90               nop
76621209 90               nop
7662120a 90               nop
7662120b 90               nop
7662120c 90               nop
7662120d 90               nop

彻底没戏啦。pMD4连同里面的明文密码被free了。

我不知道关于lsass留着明文密码备用的说法究竟是不是可靠的,至少我们看到的那个确实被free了。
如果说,前面提到的多个“源密码”中的某一个被lsass留着,那就不应该搜索不到。要么密码不是明文的。

结论很明显,在lsass里找明文密码属于RP问题。如果它已经被覆盖,就算你用什么模拟登陆的方法确定它的位置,也是回天乏术。如果没有被覆盖,用WinEggDrop的办法找密码已经足够了。

我再加一个判断条件:密码前面的2个ULONG总是0x00000200和0x00000000。它们就是pMD4->Count的LowPart和HighPart,用于保存被加密数据的bit数。MD4加密以64字节(0x200 bit)为最小单位,密码不超过32个字符的话,一轮加密就完了,所以它们总是0x200和0x0。这个条件可以在密码被覆盖时减少误报。


原文地址:https://www.cnblogs.com/rainbowzc/p/1252393.html