首页
社区
课程
招聘
[转帖]关于定位lsass内存中的明文密码
发表于: 2007-5-20 14:16 6385

[转帖]关于定位lsass内存中的明文密码

2007-5-20 14:16
6385
来源: 幻影旅团

作者:zzzevazzz

Codz:

所谓“不到黄河心不死”、“不撞南墙不回头”、“不见棺材不掉泪”,人是不容易死心的。在没搞清楚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           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   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。这个条件可以在密码被覆盖时减少误报。

PS:本文简直是笨笨熊前辈的windbg教程的活生生的例子! 学习!

[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!

收藏
免费 0
支持
分享
最新回复 (1)
雪    币: 250
活跃值: (16)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
希望各位大牛多发一些类似的内核态调试过程啊--windbg kernel local debug......
2007-5-21 09:48
0
游客
登录 | 注册 方可回帖
返回
//