2015年1月24日

HPLIP bb-soap解析 - 1

前言:
接續前面2篇網樂通Scanner Server文章
這篇開始,會將我針對HP Color LaserJet CM1015掃描功能的Close Code解析,
因為目前尚未破解,所以這系列會不會有完結篇不確定,
解析到哪,又剛好有空、有興趣,就po到哪。

背景資訊:
HP Color LaserJet CM1015這台HP事務機功能如下:
  • USB介面
  • 有彩色掃描功能
  • 有彩色列印功能
  • 是雷射列印
這台事務機在Linux上,從HPLIP 2.8.10開始支援,Linux上提供
  • 黑白、彩色掃描
  • 黑白、彩色列印
  • 印表機狀態資訊
相關資訊可以參考:
HP Color LaserJet cm1015 Multifunction Printer

而根據HPLIP 2.8.10的Release時間回推,這台應該是在2008年的款式,
我的購買時間我已經忘了。

HPLIP本身雖然是OpenSource的專案,但HP並沒有將全部的檔案都OpenSource,
HPLIP在大部分機種上要求要安裝Driver Plugin,
這個Driver Plugin提供了這些機種需要的Close Code Library,
它們包括:
  • bb_marvell-x86_32.so
  • bb_marvell-x86_64.so
  • bb_soapht-x86_32.so
  • bb_soapht-x86_64.so
  • bb_soap-x86_32.so
  • bb_soap-x86_64.so
  • fax_marvell-x86_32.so
  • fax_marvell-x86_64.so
  • hbpl1-x86_32.so
  • hbpl1-x86_64.so
  • lj-x86_32.so
  • lj-x86_64.so
以CM1015來說,CM1015需要bb_soap。

從程式碼看起來,
HP不同機種使用了不同的Protocol,不同Protocol使用不同的Close Code Library檔案,
CM1015的Protocol看起來只是單純的SOAP,使用的是bb_soap。

怎麼知道是用bb_soap?
因為我在網樂通上手動把Driver Plugin放上去,執行scanimage後,
在/var/log/syslog出現類似如下:
scanimage: common/utils.c 188: unable to load library /usr/local/hplip/share/hplip/scan/plugins/bb_soap.so: /usr/local/hplip/share/hplip/scan/plugins/bb_soap.so: wrong ELF class: ELFCLASS32

當然,這是示意訊息,原始錯誤訊息印象中是Segfault,但它的確標明了使用的library。

因為HP只將Protocol Close,掃描的上層是SANE,下層是HPMUD + libusb,示意圖如下:
所以透過修改程式,
可以在HPMUD和sane-hpaio backend加上printf,
就能知道它的呼叫flow,儘管它沒有Open,
也能知道傳遞什麼給事務機,步驟是什麼。

解析開始:
建立環境:
建個VM,USB map進去,按網樂通類似步驟把HPLIP、SANE、CUPS build出來。

找程式起始點:
首先先找hpaio backend的呼叫點,找到是在
hplip-3.14.10/scan/sane/

繼續找bb_soap的呼叫點,找到主要是
hplip-3.14.10/scan/sane/soap.c

再來找hpmud,找到是
hplip-3.14.10/io/hpmud/

主要應該是
hplip-3.14.10/io/hpmud/hpmud.c

至於hplip-3.14.10/io/mudext不用管它,看裡面有註明是給python用的library。

查看程式,修改程式碼,加printf:
看soap.c,看到
  • ps->bb_open = get_library_symbol(ps->bb_handle, "bb_open")
  • ps->bb_close = get_library_symbol(ps->bb_handle, "bb_close")
  • ps->bb_get_parameters = get_library_symbol(ps->bb_handle, "bb_get_parameters")
  • ps->bb_is_paper_in_adf = get_library_symbol(ps->bb_handle, "bb_is_paper_in_adf")
  • ps->bb_start_scan = get_library_symbol(ps->bb_handle, "bb_start_scan")
  • ps->bb_end_scan = get_library_symbol(ps->bb_handle, "bb_end_scan")
  • ps->bb_get_image_data = get_library_symbol(ps->bb_handle, "bb_get_image_data")
  • ps->bb_end_page = get_library_symbol(ps->bb_handle, "bb_end_page")
非常好,它將bb_soap的所有function都列好了,不用辛苦找。
接著在這些呼叫點前後加上printf,把原本的DBG改成printf。

hpmud裡面也是,把DBG都改成printf,看到open、close、read、write,都加上printf。

重新編譯後,執行
scanimage > AAA

看看AAA裡面有什麼,如下:
[10153] hpmud_probe_devices() bus=3
sane_hpaio_open(/usb/HP_Color_LaserJet_CM1015?serial=00CNF87DL012)
io_mode = 1
[10153,13449,0,0,0,0] hpmud_device_open() uri=hp:/usb/HP_Color_LaserJet_CM1015?serial=00CNF87DL012 iomode=1
bb_open(session)
[10153] hpmud_channel_open() dd=1 name=HP-SOAP-SCAN
[10153] hpmud_channel_write() dd=1 cd=19 buf=0x7fca030fd380 size=154 sectime=45
[10153] hpmud_channel_write() dd=1 cd=19 buf=0x7fca030fd458 size=426 sectime=45
[10153] hpmud_channel_read() dd=1 cd=19 buf=0xd7b5c8 size=4096 sectime=45
[10153] hpmud_channel_read() dd=1 cd=19 buf=0xd7b5c8 size=4096 sectime=45
[10153] hpmud_channel_read() dd=1 cd=19 buf=0xd7b5c8 size=4096 sectime=45
[10153] hpmud_channel_read() dd=1 cd=19 buf=0xd7b5c8 size=4096 sectime=45
[10153] hpmud_channel_close() dd=1 cd=19
bb_open end
sane_hpaio_control_option (option=mode action=auto value=na)
sane_hpaio_control_option (option=resolution action=auto value=na)
sane_hpaio_control_option (option=brightness action=auto value=na)
sane_hpaio_control_option (option=contrast action=auto value=na)
sane_hpaio_control_option (option=compression action=auto value=na)
sane_hpaio_control_option (option=jpeg-quality action=auto value=na)
sane_hpaio_control_option (option=tl-x action=auto value=na)
sane_hpaio_control_option (option=tl-y action=auto value=na)
sane_hpaio_control_option (option=br-x action=auto value=na)
sane_hpaio_control_option (option=br-y action=auto value=na)
sane_hpaio_get_option_descriptor(option=option-cnt)
sane_hpaio_control_option (option=option-cnt action=get value=14)
sane_hpaio_get_option_descriptor(option=option-cnt)
sane_hpaio_control_option (option=option-cnt action=get value=14)
sane_hpaio_get_option_descriptor(option=mode-group)
sane_hpaio_get_option_descriptor(option=mode)
sane_hpaio_get_option_descriptor(option=resolution)
sane_hpaio_get_option_descriptor(option=advanced-group)
sane_hpaio_get_option_descriptor(option=brightness)
sane_hpaio_get_option_descriptor(option=contrast)
sane_hpaio_get_option_descriptor(option=compression)
sane_hpaio_get_option_descriptor(option=jpeg-quality)
sane_hpaio_get_option_descriptor(option=geometry-group)
sane_hpaio_get_option_descriptor(option=tl-x)
sane_hpaio_get_option_descriptor(option=tl-y)
sane_hpaio_get_option_descriptor(option=br-x)
sane_hpaio_get_option_descriptor(option=br-y)
sane_hpaio_control_option (option=br-x action=get value=14149222)
sane_hpaio_control_option (option=tl-x action=get value=0)
sane_hpaio_control_option (option=br-y action=get value=19459342)
sane_hpaio_control_option (option=tl-y action=get value=0)
sane_hpaio_control_option (option=tl-x action=get value=0)
sane_hpaio_get_option_descriptor(option=br-x)
sane_hpaio_control_option (option=br-x action=set value=14149222)
sane_hpaio_control_option (option=tl-y action=get value=0)
sane_hpaio_get_option_descriptor(option=br-y)
sane_hpaio_control_option (option=br-y action=set value=19459342)
sane_hpaio_start()
bb_start_scan
[10153] hpmud_channel_open() dd=1 name=HP-SOAP-SCAN
[10153] hpmud_channel_write() dd=1 cd=19 buf=0x7fca030fd380 size=154 sectime=45
[10153] hpmud_channel_write() dd=1 cd=19 buf=0x7fffda5ca8f0 size=5 sectime=1
[10153] hpmud_channel_write() dd=1 cd=19 buf=0x7fffda5ca910 size=1359 sectime=1
[10153] hpmud_channel_write() dd=1 cd=19 buf=0x7fca030fd8a9 size=7 sectime=1
[10153] hpmud_channel_read() dd=1 cd=19 buf=0xd83a68 size=4096 sectime=45
[10153] hpmud_channel_read() dd=1 cd=19 buf=0xd83a68 size=4096 sectime=45
[10153] hpmud_channel_read() dd=1 cd=19 buf=0xd83a68 size=4096 sectime=45
bb_start_scan end
bb_get_parameters
bb_get_parameters end
set traits iPixelsPerRow=632 iBitsPerPixel=8 lNumRows=876 iComponentsPerPixel=1
cnt=1024 index=0 input=0xd68e10 inputAvail=1024 inputUsed=1024 inputNextPos=1024 output=(nil) outputAvail=0 outputThisPos=0
[10153] hpmud_channel_read() dd=1 cd=19 buf=0xd83a68 size=4096 sectime=45
cnt=1024 index=0 input=0xd68e10 inputAvail=1024 inputUsed=1024 inputNextPos=2048 output=(nil) outputAvail=0 outputThisPos=0
cnt=1024 index=0 input=0xd68e10 inputAvail=1024 inputUsed=1024 inputNextPos=3072 output=(nil) outputAvail=0 outputThisPos=0
[10153] hpmud_channel_read() dd=1 cd=19 buf=0xd83a68 size=4096 sectime=45
[10153] hpmud_channel_read() dd=1 cd=19 buf=0xd83a68 size=4096 sectime=45
cnt=1024 index=0 input=0xd68e10 inputAvail=1024 inputUsed=1024 inputNextPos=4096 output=(nil) outputAvail=0 outputThisPos=0
cnt=1024 index=0 input=0xd68e10 inputAvail=1024 inputUsed=1024 inputNextPos=5120 output=(nil) outputAvail=0 outputThisPos=0
[10153] hpmud_channel_read() dd=1 cd=19 buf=0xd83a68 size=4096 sectime=45
cnt=1024 index=0 input=0xd68e10 inputAvail=1024 inputUsed=1024 inputNextPos=6144 output=(nil) outputAvail=0 outputThisPos=0
cnt=1024 index=0 input=0xd68e10 inputAvail=1024 inputUsed=1024 inputNextPos=7168 output=(nil) outputAvail=0 outputThisPos=0
[10153] hpmud_channel_read() dd=1 cd=19 buf=0xd83a68 size=4096 sectime=45
[10153] hpmud_channel_read() dd=1 cd=19 buf=0xd83a68 size=4096 sectime=45
cnt=1024 index=0 input=0xd68e10 inputAvail=1024 inputUsed=1024 inputNextPos=8192 output=(nil) outputAvail=0 outputThisPos=0
cnt=1024 index=0 input=0xd68e10 inputAvail=1024 inputUsed=1024 inputNextPos=9216 output=(nil) outputAvail=0 outputThisPos=0
[10153] hpmud_channel_read() dd=1 cd=19 buf=0xd83a68 size=4096 sectime=45
cnt=1024 index=0 input=0xd68e10 inputAvail=1024 inputUsed=1024 inputNextPos=10240 output=(nil) outputAvail=0 outputThisPos=0
[10153] hpmud_channel_read() dd=1 cd=19 buf=0xd83a68 size=4096 sectime=45
cnt=450 index=0 input=0xd68e10 inputAvail=450 inputUsed=450 inputNextPos=10690 output=(nil) outputAvail=0 outputThisPos=0
cnt=0 index=0 input=(nil) inputAvail=0 inputUsed=0 inputNextPos=10690 output=(nil) outputAvail=0 outputThisPos=0
act traits iPixelsPerRow=637 iBitsPerPixel=1 lNumRows=876 iComponentsPerPixel=1
bb_getparameters
bb_getparameters end
sane_hpaio_get_parameters(): format=0, last_frame=1, lines=876, depth=1, pixels_per_line=637, bytes_per_line=80
P4
# SANE data follows
637 876
sane_hpaio_read() handle=0xd68720 data=0xd7ba40 maxLength=32768
cnt=0 index=0 input=(nil) inputAvail=0 inputUsed=0 inputNextPos=10690 output=0xd7ba40 outputAvail=32768 outputThisPos=0
-sane_hpaio_read() output=0xd7ba40 bytes_read=32768 maxLength=32768 status=0
▒       ▒sane_hpaio_read() handle=0xd68720 data=0xd7ba40 maxLength=32768
cnt=0 index=0 input=(nil) inputAvail=0 inputUsed=0 inputNextPos=10690 output=0xd7ba40 outputAvail=32768 outputThisPos=32768
-sane_hpaio_read() output=0xd7ba40 bytes_read=32768 maxLength=32768 status=0
▒       ▒sane_hpaio_read() handle=0xd68720 data=0xd7ba40 maxLength=32768
cnt=0 index=0 input=(nil) inputAvail=0 inputUsed=0 inputNextPos=10690 output=0xd7ba40 outputAvail=32768 outputThisPos=65536
-sane_hpaio_read() output=0xd7ba40 bytes_read=4544 maxLength=32768 status=0
sane_hpaio_read() handle=0xd68720 data=0xd7ba40 maxLength=32768
cnt=0 index=0 input=(nil) inputAvail=0 inputUsed=0 inputNextPos=10690 output=0xd7ba40 outputAvail=32768 outputThisPos=70080
bb_end_page
[10153] hpmud_channel_close() dd=1 cd=19
bb_end_page end
-sane_hpaio_read() output=0xd7ba40 bytes_read=0 maxLength=32768 status=5
sane_hpaio_cancel()
bb_end_scan
[10153] hpmud_channel_open() dd=1 name=HP-SOAP-SCAN
[10153] hpmud_channel_write() dd=1 cd=19 buf=0x7fca030fd380 size=154 sectime=45
[10153] hpmud_channel_write() dd=1 cd=19 buf=0x7fffda5cb190 size=5 sectime=1
[10153] hpmud_channel_write() dd=1 cd=19 buf=0x7fffda5cb1b0 size=442 sectime=1
[10153] hpmud_channel_write() dd=1 cd=19 buf=0x7fca030fd8a9 size=7 sectime=1
[10153] hpmud_channel_read() dd=1 cd=19 buf=0xd83a68 size=4096 sectime=45
[10153] hpmud_channel_read() dd=1 cd=19 buf=0xd83a68 size=4096 sectime=45
[10153] hpmud_channel_read() dd=1 cd=19 buf=0xd83a68 size=4096 sectime=45
[10153] hpmud_channel_read() dd=1 cd=19 buf=0xd83a68 size=4096 sectime=45
[10153] hpmud_channel_close() dd=1 cd=19
bb_end_scan end
sane_hpaio_close()
bb_close
close end bb_unload
bb unload end
[10153] hpmud_device_close() dd=1
這整段一長串,會注意到:
1. bb_soap function呼叫流程,如下:
  • bb_open
  • bb_start_scan
  • bb_get_parameters
  • bb_get_parameters
  • bb_end_page
  • bb_end_scan
  • bb_close
  • bb_unload
2. 和USB的動作都使用下列這些function:
  • hpmud_channel_open
  • hpmud_channel_close
  • hpmud_channel_read
  • hpmud_channel_write
查看hpmud傳輸內容:
修改hpmud.c,在hpmud_channel_read和hpmud_channel_write裡面加上寫檔,
讀取和寫入的資料統統寫到檔案中,我是如下:
讀取部份:
hpmud_read_channel_1.dump
hpmud_read_channel_2.dump
.....

寫入部份:
hpmud_write_channel_1.dump
hpmud_write_channel_2.dump
.....

精彩的來了,我們看看它到底傳什麼給事務機,又從事務機收什麼回來?
cat /tmp/hpmud_write_channel_1.dump
POST / HTTP/1.1
Host: http:0
User-Agent: gSOAP/2.7
Content-Type: application/soap+xml; charset=utf-8
Transfer-Encoding: chunked
Connection: close

cat /tmp/hpmud_write_channel_2.dump
19E

<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://www.w3.org/2003/05/soap-envelope" xmlns:SOAP-ENC="http://www.w3.org/2003/05/soap-encoding" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:wscn="http://tempuri.org/wscn.xsd"><SOAP-ENV:Body><wscn:GetScannerElements></wscn:GetScannerElements></SOAP-ENV:Body></SOAP-ENV:Envelope>
0

cat /tmp/hpmud_read_channel_1.dump
HTTP/1.1 200 OK
Server: gSOAP/2.7
Content-Type: application/soap+xml; charset=utf-8
Transfer-Encoding: chunked
Connection: close

cat /tmp/hpmud_read_channel_2.dump
7CB
1.1 200 OK
Server: gSOAP/2.7
Content-Type: application/soap+xml; charset=utf-8
Transfer-Encoding: chunked
Connection: close

dump出來的訊息好面熟,看起來很像是HTTP和XML啊.....
Google soap,查到:
SOAP Wiki

原來SOAP是以HTTP + XML為核心的物件描述語言,
而CM1015以SOAP做為溝通的通訊協定。

這裡有個部份我一開始想不通,它每份資料的斷點在哪裡?為何會出現19E這樣的神秘字串?
後來比對hpmud_read_channel和hpmud_write_channel的動作,搭配神秘數字0,
終於搞清楚它神秘字串是表示後面還有多少字要傳送或接收,
當這段資料後面沒東西時,就傳遞個0,表示後面沒資料,
有資料時,神秘字串是後一筆資料的字串長度的HEX表示。

以19E為例,19E表示:
<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://www.w3.org/2003/05/soap-envelope" xmlns:SOAP-ENC="http://www.w3.org/2003/05/soap-encoding" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:wscn="http://tempuri.org/wscn.xsd"><SOAP-ENV:Body><wscn:GetScannerElements></wscn:GetScannerElements></SOAP-ENV:Body></SOAP-ENV:Envelope>
這整段的資料長度。

本篇先到此為止,後續再戰。

沒有留言: