惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

S
SegmentFault 最新的问题
Google DeepMind News
Google DeepMind News
G
Google Developers Blog
Martin Fowler
Martin Fowler
MongoDB | Blog
MongoDB | Blog
月光博客
月光博客
Jina AI
Jina AI
宝玉的分享
宝玉的分享
人人都是产品经理
人人都是产品经理
D
DataBreaches.Net
V
V2EX
WordPress大学
WordPress大学
T
The Blog of Author Tim Ferriss
Last Week in AI
Last Week in AI
B
Blog
博客园 - 叶小钗
小众软件
小众软件
Stack Overflow Blog
Stack Overflow Blog
P
Proofpoint News Feed
A
About on SuperTechFans
J
Java Code Geeks
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
Y
Y Combinator Blog
Microsoft Security Blog
Microsoft Security Blog

方永、南天紫雲

linux透明代理 技术的边界 停机问题通俗说明 Rust与oracle、redis集群的纠结 学习能力的增长 微信调试的原语 mac外接移动硬盘安装ArchLinux linux的死机问题 linux下全键盘操作 漂亮又好用的bspwm linux本机透明代理 ssh、mosh、autossh linux不能待机又一例 从WordPress切换到Hugo 折腾电脑开机 linux系统QQ新思路 Arch Linux的字体渲染 svn管理之submin OpenWrt自动fucking墙記錄 二维码(qrcode)名片的一些事儿 用lua nginx module搭建一个二维码(qr code)生成器 CentOS 7 安裝註記 php程序連接MySQL时只能127.0.0.1而localhost無法連接的問題 ThinkPad之FAN_ERROR、hi fi聲卡 jetty多端口部署 OpenWrt簡單暴力限網 linux中cron之PATH变量 OpenWrt的WDS無線橋接模式 再敘OpenWrt下的rtl8187無線中繼 sed之模擬tail
dllcall ,用批处理做程序
zola · 2011-08-25 · via 方永、南天紫雲

dllcall ,用批处理做程序

dllcall 类似于 rundll32 的程序,但是调用很明了,实现方式也和 rundll32 不同,rundll32 可以为一些API提供窗口,dllcall 只是一个命令行(console pro)程序,所以 rundll32 应用最广的调用.cpl文件的执行在dllcall中不能实现。dllcall 主要用来扩展批处理,可以用批处理做一些简单的程序。

dllcall 的制作缘起主要是由于作者以前一直在用 NSIS 将一些工具打包放在移动设备中用来部署工作环境,但移动设备中的exe文件经常会被病毒感染,而且当工具包有一点小改动时也要重新编译整个包,当这个包很大时就很耗费时间,所以改用批处理加 7za 加单独压缩的包的形式,shorcut 也是这个原因制作的。

dllcall的特点:

  1. 使用汇编语言编写,fasm汇编器汇编,指令级优化,极小的体积(目前版本4KB),超快的速度,真正意义上的绿色软件。
  2. 开源软件,使用MIT开源协议发布,可以了解程序的每一个细节,放心地使用(可惜360之类的很多杀毒软件会报毒,汇编语言写的程序命运大都如此吧)。
  3. 提供的只是一种机制,而非功能,从而随心所欲地实现自己想要的功能,而且仅仅是通过批处理。
  4. 由于涉及到Windows API的调用,可能有些拗,其实编程很简单,很多人大抵是被课堂里的C语言给震慑了吧,用dllcall做程序,首先需知道自己想要什么功能,批处理能否实现(GUI界面当然不是命令行程序所擅长的),涉及到複雜結構體的填充,多個指針的調用,及上下函數傳遞參數非常複雜的情況不宜用dllcall。其实通过调用一两个API函数实现的功能最适合dllcall了,一些例子就在下面。

dllcall使用教程看这里

dllcall,call functions in DLL. 用法: dllcall {函数名} [参数]… {动态链接库名称}

Usage: dllcall {function name} [parameter(s)]... {DLL name}
Examples:
dllcall Sleep,1000,kernel32.dll
dllcall MessageBoxA,,"Hello world!",Prompt,1,user32.dll
dllcall MessageBoxW,,"%uAdd prefix %u indicate unicode s ",%ua,1,user32.dll
dllcall ShellExecuteA,,,"http://www.google.com",,,,shell32.dll
dllcall 61,,,,,,shell32.dll
dllcall 61,,,,%uRun,"%u Only Demo",,shell32.dll
dllcall SendMessageA,0x0FFFF,0x00112,0x0F170,1,user32.dll
the display is going to low power.
dllcall SHChangeNotify,0x8000000,0x1000,,,shell32.dll
refresh desktop items.

使用%u前缀指明后面跟的是unicode字符串,原因是所有与字符有关的API都有ansi及unicode两种版本,其函数后面有A及W字符,如MessageBoxA和MessageBoxW,而系统默认的字符编码是unicode,所以在给一些用序号导出的API传递字符串参数时只能在调用时实现编码的转换,shell32.dll中序号为61的函数就是这种情况。

用%o使用一个输出型参数,每次调用只能有一个%o出现,%o输出的结果在返回值之后,如dllcall GetWindowsDirectoryA,%o,4096,kernel32.dll ,输出结果为 10 C:\WINDOWS 。%od将输出转换为一整数 调用函数的返回值回显为一个十进制的数。

2009-12-29 更新:

考虑再三,还是加上了多个函数的调用,如 dllcall OpenClipboard,,user32.dll;GetClipboardData,1,user32.dll\*;CloseClipboard,user32.dll- 这样的格式,”*”指明返回值是一个指向字符串的指针。

文档及源码整理得不太好,故文档不再放出,源码暂不放出。

每个DLL导出的函数可用 gie 获取,在这里下载,如用 gie -e shell32.dll 得到 shell32.dll 的所有导出函数,API的调用说明参见MSDN,也可在这里找到相关的资源。

  • “,”(即英文逗号)分隔传入的参数,在参数中使用英文逗号,用”\”转义,如”Hello,i am tom.”这样的参数。逗号会被空格代替,参数可以是纯数字,如1234或”1234″,表示一个十进制的数; 0x 开头的参数表示一个十六进制的数,如0x1234或”0x1234″;
  • %u 开头的参数表示这是一个unicode 类型的字符串,如%uHello,man 或”%uHello,i am tom.”;
  • %o 开头的参数表示将这个参数中的结果打印出来,如%o 或”%o”,
  • %od 开头的参数与 %o 开头的参数类似,也是打印结果,但是会认为参数是一个整数,用法与%o 相同,如果不全是数字而且没有
  • 0x %u 等参数说明符,参数会被认为是一个字符串。 参数中有空格或中文字符,用引号将参数引起来,0x %u 等参数说明符号应在引号内,如果传入的参数值为0,可用,,这样的写法。

1.0.3.0版本加入功能说明: 加入%r参数说明符,加入符号”;”、”*”、”-“,增加了多个函数的调用。 %r用来引用一次dllcall使用中上一个函数的返回值; “;”用来分隔调用的函数,在函数中使用”;”用”;”转义,”*”及”-“放在一个函数调用语句的后面,”*”用来指示函数的返回值是一个字符串的地址,”-“用来指示函数的返回值不再打印出来。例如:

dllcall OpenClipboard,,user32.dll;GetClipboardData,1,user32.dll\*;CloseClipboard,user32.dll-

这个语句中有3个函数,第一个函数打开剪切板,第二个函数取得其中的数据,第三个函数关闭了剪切板。

2010-6-21 更新:

1.0.3.3版本加入功能说明: 加入%p参数说明符。 %p指出这个参数是一个指针,其后可跟一个十进制或十六进制的数对其赋初值。例如:

dllcall IsNetworkAlive,%p0x1,sensapi.dll

这个语句中传入IsNetworkAlive的参数是一个指针(即地址值),这个地址中的内容是1 。

2010-7-17 更新:

1.0.3.4版本加入功能说明: 加入”-r”选项。 由于Windows的命令解释器cmd.exe的for命令中使用dllcall会出错,所以”-r”选项的作用就是给出一个替代”,”的字符,如:

for /f %i in ('dllcall -r# MessageBoxA##"test"#p#0x24#user32.dll') do set return=%i

2013-3-17 更新:

修正了文檔及示例的多個錯誤。

TODO: 抽點時間,完善一下,包括文檔,多做幾個示例。

2013-3-30 更新:

源代碼中添加一些簡單的註釋並釋出。

更新到1.0.3.5,並將調用的最後一個函數的返回值作爲dllcall的退出碼。

源码及可執行文件下載: http://files.vinoca.org

示例1: 对 notepad 操作

@echo off
start "" notepad.exe
dllcall Sleep,700,kernel32.dll-
dllcall FindWindowA,"Notepad",,user32.dll-
set hPad=%errorlevel%
dllcall ShowWindow,%hPad%,3,user32.dll-
dllcall FindWindowExA,%hPad%,,"Edit",,user32.dll-
set hEdit=%errorlevel%
call:putchar 0x4F
call:putchar 0x6E
call:putchar 0x6C
call:putchar 0x79
call:putchar 0x2C 900
call:putchar 0x08
call:putchar 0x20
call:putchar 0x44
call:putchar 0x45
call:putchar 0x4D
call:putchar 0x4F
call:putchar 0x2E
call:sleep 1500
rem 发送 WM_QUIT(0x12) 消息到notepad的主窗体,关闭notpead .
dllcall PostMessageA,%hPad%,0x12,,,user32.dll-
pause
 
:putchar <char>
setlocal
set arg2=%2&&if "%2" == "" set arg2=300
rem 发送WM_CHAR(0x102)消息到notepad的子窗体edit ,输入一个字符,wParam 参数为字符
dllcall SendMessageA,%hEdit%,0x102,%1,,user32.dll-
call:sleep %arg2%
exit /b
 
:sleep <ms>
dllcall Sleep,%1,kernel32.dll-
exit /b

示例2: 关闭显示器 。

@echo off
rem invoke SendMessage,HWND_BROADCAST,WM_SYSCOMMAND,SC_MONITORPOWER,1
rem 使显示器处于低功耗状态
rem dllcall SendMessageA,0x0FFFF,0x00112,0x0F170,1,user32.dll
rem invoke SendMessage,HWND_BROADCAST,WM_SYSCOMMAND,SC_MONITORPOWER,2
rem 关闭显示器
dllcall SendMessageA,0x0FFFF,0x00112,0x0F170,2,user32.dll

示例3: 修改“开始”按钮的文字(WinXP)

@echo off
for /f %%i in ('dllcall -r# FindWindowA#"Shell_TrayWnd"##user32.dll') do set hWindow=%%i
for /f %%i in ('dllcall -r# FindWindowExA#%hWindow%##"Button"##user32.dll') do set hWindow=%%i
rem 发送WM_SETTEXT 消息到子窗体
dllcall SendMessageA,%hWindow%,0x0C,,"傳送",user32.dll-
exit

示例4: 隐藏批处理窗体及播放wav格式音频文件

@echo off
set cmdTitle=cmd%random%
title %cmdTitle%
for /f %%i in ('dllcall -r# FindWindowA##%cmdTitle%#user32.dll') do set hCmd=%%i
rem 隐藏窗口1秒
dllcall ShowWindow,%hCmd%,,user32.dll-
dllcall Sleep,1000,kernel32.dll-
dllcall ShowWindow,%hCmd%,1,user32.dll-
rem 播放 WAV 格式声音
dllcall PlaySoundA,"type.wav",,0x20000,winmm.dll-
pause

示例4: 打开关闭光驱仓门

@echo off
dllcall mciSendStringA,"open cdaudio",,,,winmm.dll-
::打开光驱仓门
dllcall mciSendStringA,"set cdaudio door open",,,,winmm.dll-
::关闭光驱仓门
::dllcall mciSendStringA,"set cdaudio door closed",,,,winmm.dll-
dllcall mciSendStringA,"close cdaudio",,,,winmm.dll-
pause

示例5: 打开可移动存储设备

@echo off
 
set n=1
:loop
if %n%==12 goto endloop
for /f "tokens=%n% delims=," %%i in ("C:,D:,E:,F:,E:,H:,I:,J:,K:,L:,M:,N:") do set driveString=%%i
for /f %%i in ('dllcall -r# GetDriveTypeA#%driveString%#kernel32.dll') do set driveType=%%i
if %driveType%==2 start "" "%driveString%"
set /a n+=1
goto loop
:endloop
pause

示例6: MessageBox 演示

@echo off
 
set strPrompt=演示
::dllcall Beep,5000,50,kernel32.dll
::dllcall MessageBeep,0x20,user32.dll
dllcall MessageBoxA,,"你好",%strPrompt%,,user32.dll-
for /f %%i in ('dllcall -r# MessageBoxA##"喜欢用批处理吗?"#%strPrompt%#0x24#user32.dll') do set return=%%i
if %return%==6 dllcall MessageBoxA,,"我也很喜欢哦。",%strPrompt%,0x30,user32.dll-
if %return%==7 dllcall MessageBoxA,,"Oh ,No!",%strPrompt%,0x10,user32.dll-
pause

示例7: 命令行显示剪切板中文字数据

dllcall OpenClipboard,,user32.dll;GetClipboardData,1,user32.dll\*;CloseClipboard,user32.dll-

示例8: 设置屏幕字体的边缘平滑

dllcall SystemParametersInfoA,75,1,0,3,user32.dll-
dllcall SystemParametersInfoA,8203,0,2,3,user32.dll-
dllcall SystemParametersInfoA,8211,0,1,3,user32.dll-
dllcall SystemParametersInfoA,8205,0,1000,3,user32.dll-
dllcall RedrawWindow,0,0,0,1927,user32.dll-

示例9: 测试网络是否可用

dllcall IsNetworkAlive,%p0x1,sensapi.dll
:: 返回1可用,返回0不可用。

示例10: 模拟键盘按键

start "" notepad.exe
for /f %%i in ('dllcall -r# FindWindowA#"Notepad"##user32.dll') do set hPad=%%i
dllcall ShowWindow,%hPad%,3,user32.dll-
dllcall Sleep,1000,kernel32.dll-
::发送Alt+H 按键
dllcall PostMessageA,%hPad%,0x104,0x48,0x20210001,user32.dll-
dllcall Sleep,1000,kernel32.dll-
::发送A 按键
dllcall PostMessageA,%hPad%,0x100,0x41,0,user32.dll-
dllcall Sleep,2000,kernel32.dll-
dllcall PostMessageA,%hPad%,0x12,,,user32.dll-
pause

示例11: plink cmd命令提示符窗口隐藏运行

@echo off
set cmdTitle=plink
title %cmdTitle%
for /f %%i in ('dllcall -r# FindWindowA##%cmdTitle%#user32.dll') do set hCmd=%%i
dllcall ShowWindow,%hCmd%,,user32.dll-
:: 443 远程ssh端口, 3390 本地socks端口
plink -v -N ssh.com -l root -pw 123456 -P 443 -D 3390