原文来自:Tracy'Blog——【
pppoe破解整理篇】
第三篇:再窥pppoe拨号客户端1.2.9
如有转载,请注明出处!
如有疑问,请先看【
pppoe破解整理篇】的前面两篇。
这些天看《加密解密3》,时不时的总会想起校园网的pppoe拨号客户端。虽然,自己已经成功的破解了它,也知道它的原理与实现方法,但总觉得,从pppoe.cfg中读取内容还原出真实密码那一块还没搞清楚它的算法,还原到的密码到得到真实密码的算法,虽然自己知道了转换过程,但是还没有真正的跟踪出这段算法(看雪有大牛老早就贴出来是3DES的变形加密。不过我天资愚钝,硬是没跟出来。。。那个帖子地址是:http://bbs.pediy.com/showthread.php?t=144811)。
于是,今天又翻出以前的笔记,重新开始跟踪。
拿出od,载入客户端主程序。我们先来分析一下程序运行的过程。
1.启动
2. →判断主程序文件夹下是否存在pppoe.cfg
3. →存在则检测是否勾选保存密码
4. →是 则从文件中读取内容还原手动输入的账号密码
5. →否 跳过
6. →不存在则显示空白。跳过读文件代码段
7. →检测是否勾选自动拨号
8. →是 则将用户名密码读取到缓冲区,将密码加密得到真实密码之后拨号
9. →否 无任何动作,等待用户操作
由上可以得出,如果我们要跟踪从pppoe.cfg还原密码过程,我们应该在第四步下断点,在他还原前,断下,然后再跟踪。如果我们要跟踪从输入密码到真实密码加密的过程,我们应该在第八步下断点进行跟踪。
我们以前是在读取文件的时候下断点进行跟踪,现在我们换一种思路,在保存的时候来跟踪试试。另外,虽然知道了怎么进行密码到真实密码的过程,但是我们还是来跟踪一下,所以,我们先下拨号断点,在拨号时他调用的是RasDialA,所以我们来下端断点bp RasDialA。
先还是来补充一下这个对这个函数的了解吧。
函数原型:
DWORD RasDial(
LPRASDIALEXTENSIONS dialExtensions,
LPTSTR phoneBookPath ,
LPRASDIALPARAMS rasDialParam ,
DWORD NotifierType,
LPVOID notifier,
LPHRASCONN pRasConn );
[SIZE="4"]参数[/SIZE]
[COLOR="Red"]dialExtensions
本参数可以被忽略并且可以设置为NULL,在Windows CE,RasDial 总是使用RASDIALEXTENSIONS 当做默认选项。
[COLOR="red"]phoneBookPath
本参数可以被忽略并且可以设置为NULL,通过电话簿存储进行拨号上网注册,而不是通过电话簿文件。
[COLOR="red"]rasDialParam
一个指向RASDIALPARAMS 结构的指针,用来描述 RAS连接的调用参数。调用者必须设置RASDIALPARAMS 结构的 dwSize成员(即结构大小),用sizeof(RASDIALPARAMS)取得大小,防止不同版本的系统取得的大小不同
[COLOR="red"]NotifierType
描述通告程序的参数性质。如果通告程序为NULL,本参数可以忽略,如果非空,则设置本参数0xFFFFFFFF 通过程序是一个句柄,是窗体接收通告程序消息用的。在通告程序进行中,wParam参数指示 RAS连接将要进入的连接状态。当发生错误时lParam里存储错误信息。
[COLOR="red"]notifier
一个指针,指向窗体句柄,用来接收RasDial的通告事件。如果本参数非空,RasDial为每一个通告事件发送一个windows消息。RasDial调用异步操作:在建立连接之前RasDial立即返回,使用窗体进行进程通信。
如果本参数被设置为:NULL,RasDial 调用同步操作:RasDial不立即返回,直到连接成功或者连接失败。
如果本参数非空,在调用RasDial之后,窗体通知会在任何时候出现。当下列事件发生时通知结束:
连接被建立,换句话说,RAS的连接状态是RASCS_Connected
连接失败,换句话说,dwError 非零
RasHangUp在连接时被调用
在RasDial最初被调用时,在线程获取上下文时产生回调通知
pRasConn
一个HRASCONN类型的指针,必须设置HRASCONN 类型变量为空在调用RasDial前。如果RasDial成功,本函数存储一个RAS连接句柄在本参数中。
返回值:
0表示成功。而且本函数存储一个RAS连接的句柄的指针在pRasConn中
非0值表示错误。
我们在od中下好断点后,直接F9运行。发现,程序被断下来了。Alt+F9返回程序领空。然后网上翻,找到00401CC6下断点。重新载入,运行,在00401CC6断下,之后我们可以在堆栈窗口中看到这些:
也就是说,在调用rasdial时的参数是这六个。而且我们知道,C++是使用_Cdel规范,
即:传递的参数是从右到左依次入栈的。也就是说,Arg1就是第一个参数dialExtensions的值。依次类推。
我们来看看第三个参数吧。0012E314,在数据窗口中跟随
它就是我们的RASDIALPARAMSA结构如下:
typedef struct _RASDIALPARAMS {
DWORD dwSize;
TCHAR szEntryName[RAS_MaxEntryName + 1];
TCHAR szPhoneNumber[RAS_MaxPhoneNumber + 1];
TCHAR szCallbackNumber[RAS_MaxCallbackNumber + 1];
TCHAR szUserName[UNLEN + 1];
TCHAR szPassword[PWLEN + 1];
TCHAR szDomain[DNLEN + 1];
DWORD dwSubEntry;
ULONG_PTR dwCallbackId;
DWORD dwIfIndex;
} RASDIALPARAMS, *PRASDIALPARAMS;
其中szUserName和szPassword为ADSL账号和密码,也是我们需要截获的数据。
那么,怎么得到这个信息呢?前面我用dll钩子实现了,今天我们来找找其他的方法。从这个结构体中直接还原出账号密码。好像有点不可能。
那么,我们就跟进这个call 看看会发生什么。
来到系统领空。之后,一直单步,在走过的第五个call的地方,发现了我们输入的用户名:
那么,密码是不是就在后面呢?
终于,在76E0C0BA E8 BCF7FFFF call rasapi32.RasDialW处,我们发现,它又调用了RasDialW。至于这个W和A有什么区别,大家自己百度吧。
看堆栈窗口。
也有六个参数,我们进数据窗口跟随看看。
在0012D008 0012DEA4 UNICODE "c8c8c8c8c8c8"
我们看到了真实密码,这个为从输入密码到真实密码加密之后的密码。实际上的真实密码后面还有两个特殊字符,如果继续跟踪会看到的。所以,可用内存注册机得到真实密码。
上面个的过程我们就可以得到我们想要的真实密码了。
===================从文件还原账号密码问题:=====================
我们在来跟一下我们说的另一个问题吧,就是如何从文件还原处账号密码的问题。下CreateFileA断点。然后跟踪
在
0041CC24 FF15 C4014300 call dword ptr ds:[<&kernel32.CreateFileA>] ;
创建一个新的pppoe.cfg,但是里面没有数据
0041CC45 FF15 64014300 call dword ptr ds:[<&kernel32.GetFileType>] ;
得到文件类型
0041CC4B 85C0 test eax,eax ;
判断返回值是否为0
0041CC4D 75 09 jnz short 11.0041CC58
0041CC4F 56 push esi
0041CC50 FF15 BC014300 call dword ptr ds:[<&kernel32.CloseHandle>] ;
kernel32.CloseHandle
0041CC56 ^ EB D8 jmp short 11.0041CC30
0041CC58 83F8 02 cmp eax,2 ;
返回值为2为正常磁盘文件
0041CC6E E8 96FBFFFF call 11.0041C809 ;
可能为密码算法段
继续跟踪
00402B82 |83C4 08 add esp,8
00402B85 |43 inc ebx
00402B86 |3BDE cmp ebx,esi
00402B88 ^|7C E9 jl short 11.00402B73
00402B8A \57 push edi
00402B8B E8 E40D0100 call 11.00413974 ;
这个call里面有写文件的过程
可以跟踪得到如下数据,可以证明已经将明文转化为密文了。这段循环,是将密文的每
两个数据取出来,然后进行一系列的算法,或者是,以某一特定的结构转化存放在内存
中。然后调用后面这个call将数据写入pppoe.cfg中。
004139B5 E8 B14B0000 call 11.0041856B ;
这个call中已经写入文件
00418587 8B46 08 mov eax,dword ptr ds:[esi+8] ;
加密后的数据起始段放入eax中
0041858A 8B3E mov edi,dword ptr ds:[esi] ;
加密后的数据结束段放入edi中
0041BD3A FF15 18024300 call dword ptr ds:[<&kernel32.WriteFile>]
; kernel32.WriteFile
调用writefile文件,实现写文件。参数如下:
0012EDB4 00000224 $[1].. |hFile = 00000224 (window)
0012EDB8 00D5A1D8 亍? |Buffer = 00D5A1D8
0012EDBC 00000027 '... |nBytesToWrite = 27 (39.)
0012EDC0 0012F1DC 荞. |pBytesWritten = 0012F1DC
0012EDC4 00000000 .... \pOverlapped = NULL
可以看出,要写入文件的信息存放在00D5A1D8这个缓冲区中。
我在回到前面那个27次循环的地方,看看它是否往这个缓冲区中写入了数据。
可以知道前面的27次循环,实际上就是搬运的过程。从0012F360搬运到00D5A1D8缓冲。
那么他的转换过程应该在从窗体读取文本到00402B71之间。我们再下GetWindowTextA来
试试。
0042A004 FF15 3C054300 call dword ptr ds:[<&USER32.GetWindowTextA>]
; 得到用户名
0042A00A 8B4D 10 mov ecx,dword ptr ss:[ebp+10]
; ecx中放入输入的用户名和密码的地址、
到00402B71
0012FD6C 00E98520 ASCII "Tracy"
0012FD70 00E98570 ASCII "111111"
00407530 E8 EBABFFFF call 1_2_9破?00402120
0012F184 2B66A6DC
0012F188 A7D8AFEE
0012F18C AE09957F
0012F190 C011BA0D
0012F194 83E1753A
0012F198 04CA2608
0012F19C 86154D09
0012F1A0 EC8AA5E5
0012F1A4 3A790D3F
0012F1A8 0C5467B8
每次存放密文的地方都是这两个地方。。。我们接着看。
0012F360 2B66A6DC
0012F364 A7D8AFEE
0012F368 AE09957F
0012F36C C011BA0D
0012F370 83E1753A
0012F374 04CA2608
0012F378 86154D09
0012F37C EC8AA5E5
0012F380 3A790D3F
0012F384 0C5467B8
终于,在这个call里面。往那两个地址的一个里面写入的数据,到
00402A42 BF A0284400 mov edi,1_2_9破?004428A0
; ASCII "Tracy"
的时候
0012F360附近出现了数据说明。算法就在这个附近
在这个call里面已经全部生成了。
00402B44 E8 A70D0000 call 1_2_9破?004038F0
所以,我们的跟踪有目的性了。
1.下面这段代码实现的功能是:
在0012F18C中存放用户名长度。然后在012F190中存放用户名各字母的ascii码。
00402A42 BF A0284400 mov edi,1_2_9破?004428A0
; ASCII "Tracy"
00402A47 83C9 FF or ecx,FFFFFFFF
00402A4A 33C0 xor eax,eax
00402A4C BE A0284400 mov esi,1_2_9破?004428A0
; ASCII "Tracy"
00402A51 F2:AE repne scas byte ptr es:[edi]
00402A53 F7D1 not ecx
; 得到用户名长度
00402A55 49 dec ecx
00402A56 BF A0284400 mov edi,1_2_9破?004428A0
; ASCII "Tracy"
00402A5B 898C24 18010000 mov dword ptr ss:[esp+118],ecx
00402A62 83C9 FF or ecx,FFFFFFFF
00402A65 F2:AE repne scas byte ptr es:[edi]
00402A67 F7D1 not ecx
00402A69 49 dec ecx
00402A6A 8DBC24 1C010000 lea edi,dword ptr ss:[esp+11C]
00402A71 8BD1 mov edx,ecx
00402A73 C1E9 02 shr ecx,2
00402A76 F3:A5 rep movs dword ptr es:[edi],dword ptr ds:[esi]
00402A78 8BCA mov ecx,edx
00402A7A 83E1 03 and ecx,3
00402A7D F3:A4 rep movs byte ptr es:[edi],byte ptr ds:[esi]
00402A7F BF A0284400 mov edi,1_2_9破?004428A0
; ASCII "Tracy"
00402A84 83C9 FF or ecx,FFFFFFFF
00402A87 F2:AE repne scas byte ptr es:[edi]
00402A89 F7D1 not ecx
00402A8B 49 dec ecx
00402A8C BF A0284400 mov edi,1_2_9破?004428A0
; ASCII "Tracy"
00402A91 8D940C 1C010000 lea edx,dword ptr ss:[esp+ecx+11C]
00402A98 83C9 FF or ecx,FFFFFFFF
00402A9B F2:AE repne scas byte ptr es:[edi]
00402A9D F7D1 not ecx
00402A9F 49 dec ecx
2.下面这段代码实现的功能是:
在0012F195中放入密码长度,然后在0012F199中放入密码的ascii码。
00402AA0 BF 98254400 mov edi,1_2_9破?00442598
; ASCII "111111"
00402AA5 8BD9 mov ebx,ecx
00402AA7 83C9 FF or ecx,FFFFFFFF
00402AAA 83C3 0C add ebx,0C
00402AAD F2:AE repne scas byte ptr es:[edi]
00402AAF F7D1 not ecx
00402AB1 49 dec ecx
00402AB2 BF 98254400 mov edi,1_2_9破?00442598
; ASCII "111111"
00402AB7 890A mov dword ptr ds:[edx],ecx
00402AB9 83C9 FF or ecx,FFFFFFFF
00402ABC 83C2 04 add edx,4
00402ABF BE 98254400 mov esi,1_2_9破?00442598
; ASCII "111111"
00402AC4 F2:AE repne scas byte ptr es:[edi]
00402AC6 F7D1 not ecx
00402AC8 49 dec ecx
00402AC9 8BFA mov edi,edx
00402ACB 8BC1 mov eax,ecx
00402ACD 6A 00 push 0
00402ACF ,, ; &nb, sp; C1E9 02 shr ecx,2
00402AD2 F3:A5 rep movs dword ptr es:[edi],dword ptr ds:[esi]
00402AD4 8BC8 mov ecx,eax
00402AD6 33C0 xor eax,eax
00402AD8 83E1 03 and ecx,3
00402ADB 6A 0F push 0F
00402ADD F3:A4 rep movs byte ptr es:[edi],byte ptr ds:[esi]
00402ADF BF 98254400 mov edi,1_2_9破?00442598
; ASCII "111111"
00402AE4 83C9 FF or ecx,FFFFFFFF
00402AE7 F2:AE repne scas byte ptr es:[edi]
00402AE9 F7D1 not ecx
00402AEB 49 dec ecx
00402AEC BF 98254400 mov edi,1_2_9破?00442598
; ASCII "111111"
00402AF1 03D1 add edx,ecx
00402AF3 83C9 FF or ecx,FFFFFFFF
00402AF6 F2:AE repne scas byte ptr es:[edi]
00402AF8 F7D1 not ecx
00402AFA 49 dec ecx
3.判断是否选上保存密码。如果是,则在0012F19F处附上01,没有则填充00
00402AFB 68 E0B04300 push 1_2_9破?0043B0E0
; ASCII "0herolibamtium0"
00402B00 8D740B 04 lea esi,dword ptr ds:[ebx+ecx+4]
; 将长度放入esi中
00402B04 8A1D 642F4400 mov bl,byte ptr ds:[442F64]
00402B0A 84DB test bl,bl
00402B0C 0F95C0 setne al
00402B0F 8902 mov dword ptr ds:[edx],eax
4.判断是否选上自动连接。如果是,则在0012F1A3处写入01.没有则填充00
00402B11 8A1D 652F4400 mov bl,byte ptr ds:[442F65]
00402B17 83C2 04 add edx,4
00402B1A 83C6 04 add esi,4
00402B1D 33C0 xor eax,eax
00402B1F 8D8C24 1C010000 lea ecx,dword ptr ss:[esp+11C]
00402B26 84DB test bl,bl
00402B28 0F95C0 setne al
00402B2B 8902 mov dword ptr ds:[edx],eax
5.为0012F1A7赋值为00
00402B30 8942 04 mov dword ptr ds:[edx+4],eax
6.将长度密文长度放入0012F184中。此例中为27.
00402B3D 89B424 28010000 mov dword ptr ss:[esp+128],esi
7.算法部分在这个call里面
00402B44 E8 A70D0000 call 1_2_9破?004038F0
这个为算法循环。
00403DFD 8BC8 mov ecx,eax
00403DFF 8A1C38 mov bl,byte ptr ds:[eax+edi]
00403E02 C1F9 03 sar ecx,3
00403E05 8D3411 lea esi,dword ptr ds:[ecx+edx]
00403E08 8BC8 mov ecx,eax
00403E0A 83E1 07 and ecx,7
00403E0D D2E3 shl bl,cl
00403E0F 8A0E mov cl,byte ptr ds:[esi]
00403E11 0ACB or cl,bl
00403E13 40 inc eax
00403E14 3BC5 cmp eax,ebp
00403E16 880E mov byte ptr ds:[esi],cl
00403E18 ^ 7C E3 jl short 1_2_9破?00403DFD
得到每一小段的算法,
这个循环得到整个一大段的算法:
0040397E 53 push ebx
0040397F 68 6C314400 push 1_2_9破?0044316C
; 看看每次的是否一样
00403984 57 push edi
00403985 56 push esi
00403986 E8 C5000000 call 1_2_9破?00403A50
; 前面一段密文的第一次加密
0040398B 8B4424 38 mov eax,dword ptr ss:[esp+38]
0040398F 50 push eax
00403990 68 6C344400 push 1_2_9破?0044346C
00403995 56 push esi
00403996 56 push esi
00403997 E8 B4000000 call 1_2_9破?00403A50
; 前面一段密文的第二次加密
0040399C 53 push ebx
0040399D 68 6C314400 push 1_2_9破?0044316C
004039A2 56 push esi
004039A3 56 push esi
004039A4 E8 A7000000 call 1_2_9破?00403A50
; 前面一段密文的第三次加密
004039A9 83C4 30 add esp,30
004039AC 83C6 08 add esi,8
004039AF 83C7 08 add edi,8
004039B2 4D dec ebp
004039B3 ^ 75 C9 jnz short 1_2_9破?0040397E &nb, sp; <, /o:p>
; 循环直到八个地址段全部加密完成
但是分析不出来,也没能写出解密程序。所以,希望大牛们给点提示啊。
马上就要准备考研了。得放下技术一段时间了。
加油,努力!
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)