语音合成可将文字信息转化为声音信息,适用于手机APP、儿童故事机、智能机器人等多种应用场景。本汇编代码是调用 百度AI开放平台提供的在线语音合成 API 实现的。

首先需要到百度AI开放平台 申请账号,并开通需要的功能。

64位汇编语言之语音合成(64位汇编语言之语音合成)(1)

64位汇编语言之语音合成(64位汇编语言之语音合成)(2)

f需要API Key 和Secret Key

然后用API Key 和Secret Key 向授权服务地址 获取 Access Token ,之后 用Access Token和文本信息向短文本在线合成网站“发送信息以获取对应的语音信息。本代码使用了64位汇编语言之HTTPS连接技术,以及网页gzip、deflate 解压缩技术。

64位汇编语言之语音合成(64位汇编语言之语音合成)(3)

option casemap:none OPTION DOTNAME include baidu_text2voice.inc .code asciitoint_decimal proc uses rbx rsi rdi r10 r11 _pbuffer:qword,_buffer_length:qword LOCAL @data_num:qword mov r8,_buffer_length and @data_num,0 mov rbx,0 mov rsi,_pbuffer .while sdword ptr r8d } 0 mov al, byte ptr [rsi rbx] .if al }= "0" && al {= "9" sub al,"0" .elseif al }= "A" && al {= "F" sub al,"0" sub al,7 .elseif al }= "a" && al {= "f" sub al,"0" sub al,27h .elseif al == 0 ; invoke MessageBox,0,addr compcheck1,0,MB_OK ; mov rax,-1 ; ret .else mov rax,-1 ret .endif mov rcx,0 mov r9,0 mov r9b,al mov rax,@data_num mov rcx,10 mul rcx mov @data_num,rax add @data_num,r9 inc rbx dec r8d .endw mov rax,@data_num ret asciitoint_decimal endp ASCII2HEX proc uses rbx rsi rdi pret_buffer:qword,size_ret_buffer:qword,pascii_buffer:qword,size_ascii_buffer:qword mov rcx,size_ascii_buffer mov rsi,pascii_buffer mov rdx,size_ret_buffer mov rdi,pret_buffer mov rbx,0 mov r8,0 .while sqword ptr rcx } 0 && sqword ptr rdx } 0 and rax,0 mov al, byte ptr [rsi] .if al }= "0" && al {= "9" sub al,"0" .elseif al }= "A" && al {= "F" sub al,"0" sub al,7 .elseif al }= "a" && al {= "f" sub al,"0" sub al,27h .elseif al == 0 ; invoke MessageBox,0,addr compcheck1,0,MB_OK ; mov rax,-1 ; ret .else mov rax,-1 ret .endif shl rbx,4 or bl,al dec rcx dec rdx inc rsi inc r8 mov [rdi],rbx .if r8 == 16 mov rbx,0 mov r8,0 add rdi,8 .else .endif .endw ret ASCII2HEX endp HostnameToIP proc _lpszHostName:qword local @szBuffer[256]:byte local @dwIP:qword invoke inet_addr,_lpszHostName .if eax {} INADDR_NONE;nozero? ;******************************************************************** ; 输入的是IP地址 ;******************************************************************** mov @dwIP,rax .else ;******************************************************************** ; 输入的是主机名称 ;******************************************************************** invoke gethostbyname,_lpszHostName .if sqword ptr rax } 0 ;greater? mov rax,[rax hostent.h_list] .while sqword ptr [rax]{}0;nozero? mov rbx,[rax] mov ecx, dword ptr [rbx] mov dword ptr @dwIP,ecx add rax,8 .break .endw .else xor eax,eax ret .endif .endif mov rax,@dwIP ret HostnameToIP endp _recv proc _hSOCKET:qword,_ipbuffer:qword,_size:qword,_flags:qword LOCAL @sizecount:qword LOCAL @ipbuffer:qword mov rax,_ipbuffer mov @ipbuffer,rax mov rax,_size mov @sizecount,rax zzzz: @@: .while sqword ptr @sizecount } 0 invoke recv ,_hSocket,@ipbuffer,@sizecount,_flags; 包含flags标志位的结构数据发送服务器 若无错误发生,recv()返回读入的字节数。如果连接已中止,返回0。否则的话,返回SOCKET_ERROR错误,应用程序可通过WSAGetLastError()获取相应错误代码。 ; 在阻塞模式下recv,recvfrom操作将会阻塞到缓冲区里有至少一个字节(TCP)或者一个完整的UDP数据报才返回。 ;在没有数据到来时,对它们的调用都将处于睡眠状态,不会返回。 .if eax ==SOCKET_ERROR;>>,zero? mov eax,0 ret .elseif eax == 0 ;如果连接已中止,返回0 ret .else ;若无错误发生,recv()返回读入的字节数 add @ipbuffer,rax sub @sizecount,rax .endif .endw mov eax,1 ret _recv endp _send proc _hSocket:qword,_ipbuffer:qword,_size:qword,_flags:qword LOCAL @sizecount:qword LOCAL @ipbuffer:qword LOCAL @timecount:qword mov @timecount,0 mov rax,_ipbuffer mov @ipbuffer,rax mov rax,_size mov @sizecount,rax @@: .while sqword ptr @sizecount } 0 invoke send ,_hSocket,@ipbuffer,@sizecount,_flags; 包含flags标志位的结构数据发送服务器 若无错误发生,send()返回所发送数据的总数 .if eax ==SOCKET_ERROR;>>,zero? invoke WSAGetLastError .if eax == WSAEWOULDBLOCK;>>,zero? invoke Sleep,64h ;延时重发 .if @timecount } 30;,greater? mov eax,0 ret .else inc @timecount .endif .continue .else mov eax,0 ret .endif .elseif eax == 0;,zero? ret .else add @ipbuffer,rax sub @sizecount,rax mov @timecount,0 .endif .endw mov eax,1 ret _send endp wait_and_recv_data_with_https proc uses rbx rsi rdi _hSocket:qword,_pCtxtHandle:qword,_precv_buffer:qword,_precv_buffer_length:qword,_return_lehgth:qword ;_;https解密后的缓冲区,原则上讲包含压缩数据 ;_precv_buffer:body 数据 (如果压缩的话,那么是解压缩数据)_pdecrypt_buffer:原始html数据 LOCAL @readcount:qword ;每次接收到的数据 LOCAL @retry_count:qword ;接收等待次数 LOCAL @hSocket:qword LOCAL @ptemp_ip_buffer:qword LOCAL @return_count:qword LOCAL @temp_buffer_for_list [5]:_SecBuffer LOCAL @EncryptMessage_SecBufferDesc_buffer :_SecBufferDesc LOCAL @heap_mid_buffer:qword LOCAL @flags:qword LOCAL @deccount:qword mov rcx,_hSocket mov @hSocket,rcx invoke GetProcessHeap invoke HeapAlloc,rax,HEAP_ZERO_MEMORY,heap_recv__size .if rax == 0;,zero? mov rax,0 ret .endif mov @heap_mid_buffer,rax and @deccount,0 mov rdi,_precv_buffer mov @ptemp_ip_buffer,rdi and @readcount,0 and @retry_count,0 and @flags,0 .while TRUE;第一次接收,给服务端3秒的等待时间 and @readcount,0 invoke ioctlsocket,@hSocket,FIONREAD,addr @readcount ;阻塞模式 在一次recv()中所接收的所有数据量。 ; 这通常与套接口中排队的数据总量相同。 ; 如果S是SOCK_DGRAM 型,则FIONREAD ; 返回套接口上排队的第一个数据报大小。 ;正常返回0 .if rax ==0 .if @readcount ==0 ;客户端connect 之后 如果没有发送数据 就会 @readcount== 0 invoke Sleep,100 inc @retry_count .if sqword ptr @retry_count { 10;给服务端3秒的等待时间;如果三次都读不到数据,说明数据已经读完了,或者服务端已经关闭了 .continue .else mov rax,0 jmp @exit .endif .else ; int 3 mov rcx,@ptemp_ip_buffer ;lea rdi,EncryptMessage_recv_buffer mov rdi,_precv_buffer sub rcx,rdi ;缓冲区已经占用的空间 .if sqword ptr rcx }= _precv_buffer_length;>>,noless? mov rcx,0 mov @ptemp_ip_buffer,rdi .endif mov rax,_precv_buffer_length sub rax,rcx;剩余可用的空间 .if sqword ptr @readcount } rax ;单次接收的数据不能超过缓冲区剩余空间容量 mov @readcount,rax .endif mov rdi,@ptemp_ip_buffer ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; invoke _recv,@hSocket,rdi,@readcount,0 ; .if rax ==0;,zero? mov rax,0 jmp @exit .endif mov rcx,@readcount add @ptemp_ip_buffer,rcx and @readcount,0 and @retry_count,0 .endif .else mov rax,0 jmp @exit .endif @again_: mov @EncryptMessage_SecBufferDesc_buffer.ulVersion,SECBUFFER_VERSION;== 0 mov @EncryptMessage_SecBufferDesc_buffer.cBuffers,4 lea rax,@temp_buffer_for_list mov @EncryptMessage_SecBufferDesc_buffer.pBuffers,rax mov rcx,@ptemp_ip_buffer ;lea rdi,EncryptMessage_recv_buffer mov rdi,_precv_buffer sub rcx,rdi mov [rax _SecBuffer.cbBuffer],ecx mov [rax _SecBuffer.BufferType],SECBUFFER_DATA ;==1 ;lea rsi,EncryptMessage_recv_buffer mov rsi,_precv_buffer mov [rax _SecBuffer.pvBuffer],rsi add rsi,rcx add rax,sizeof _SecBuffer mov [rax _SecBuffer.cbBuffer],0 mov [rax _SecBuffer.BufferType],0 mov [rax _SecBuffer.pvBuffer],0 add rsi,rcx add rax,sizeof _SecBuffer mov [rax _SecBuffer.cbBuffer],0 mov [rax _SecBuffer.BufferType],0 mov [rax _SecBuffer.pvBuffer],0 add rax,sizeof _SecBuffer mov [rax _SecBuffer.cbBuffer],0 mov [rax _SecBuffer.BufferType],0 mov [rax _SecBuffer.pvBuffer],0 invoke DecryptMessage,_pCtxtHandle,addr @EncryptMessage_SecBufferDesc_buffer,0,0 .if eax == SEC_I_CONTEXT_EXPIRED;>>,zero? ;发送方已经关闭了,可能已经发送完成了The message sender has finished using the connection and has initiated a shutdown. lea rax,@temp_buffer_for_list .break .elseif eax == SEC_E_OUT_OF_SEQUENCE;>>,zero? ;该消息没有按正确的顺序接收。 lea rax,@temp_buffer_for_list nop .break .elseif eax == SEC_E_INVALID_HANDLE;>>,zero? ;在phContext参数中指定了一个无效的上下文句柄 .break .elseif eax == SEC_E_INVALID_TOKEN;>>,zero? ;缓冲区类型错误或没有找到类型为SECBUFFER_DATA的缓冲区 .break .elseif eax == SEC_E_MESSAGE_ALTERED;>>,zero? ;消息已被更改 .break .elseif eax == SEC_E_OK;>>,zero? ;正常完成没有关闭 or @flags,1 lea rax,@temp_buffer_for_list mov rdi,@heap_mid_buffer mov r8,4 add rdi,@deccount .while sqword ptr r8 } 0 ;解密的数据保存到备用缓冲区 .if [rax _SecBuffer.BufferType] == SECBUFFER_DATA;>>,zero? mov ecx,[rax _SecBuffer.cbBuffer] mov rsi,[rax _SecBuffer.pvBuffer] add @deccount,rcx rep movsb .endif add rax,sizeof _SecBuffer dec r8 .endw mov rdi,_precv_buffer mov @ptemp_ip_buffer,rdi lea rax,@temp_buffer_for_list mov r8,4 ;lea rdi,EncryptMessage_recv_buffer mov rdi,_precv_buffer .while sqword ptr r8 } 0;,GREATER? ;未解密的数据下次一并解密,需要调整接收指针,也可以再解密一次 .if [rax _SecBuffer.BufferType] == SECBUFFER_EXTRA;>>,zero? ;The security package uses this value to indicate the number of extra or unprocessed bytes in a message. mov ecx,[rax _SecBuffer.cbBuffer] mov rsi,[rax _SecBuffer.pvBuffer] rep movsb mov @EncryptMessage_SecBufferDesc_buffer.ulVersion,SECBUFFER_VERSION;== 0 mov @EncryptMessage_SecBufferDesc_buffer.cBuffers,4 lea rax,@temp_buffer_for_list mov @EncryptMessage_SecBufferDesc_buffer.pBuffers,rax mov @ptemp_ip_buffer,rdi .endif add rax,sizeof _SecBuffer dec r8 .endw ;;;;;;;;;是否还有未解密的数据 ; int 3 mov rdi,_precv_buffer .if rdi {} @ptemp_ip_buffer jmp @again_ .endif .elseif eax == SEC_E_INCOMPLETE_MESSAGE;>>,zero? ;equ 80090318hhe data in the input buffer is incomplete. The application needs to read more data ;from the server and call DecryptMessage (Schannel) again. ;输入缓冲区中的数据不完整。应用程序需要读取更多数据;然后再次调用DecryptMessage(Schannel)。 ;个人理解::仅仅为了避免多次调用这个函数 ;这里有个问题:如果服务端一直返回0数据,那么就会死循环 .if sqword ptr @retry_count }10 ;我们的处理就是忽略这个请求!也就是说已经连续10次没有读到数据了,那就别指望服务端会提供数据了 ; invoke logout,addr atext (_error_server,"服务端一直返回0数据,虽然连接没有断开,我们主动断开吧!"),sizeof _error_server int 3 and @flags,0 jmp @exit .endif ; lea rax,@temp_buffer_for_list ; mov ecx,[rax _SecBuffer.cbBuffer] ;这里应该是总共需要这么多的数据才能完成本次请求。 ; lea rdi,EncryptMessage_recv_buffer ; mov rdi,_precv_buffer ; add rdi,rcx ; mov @ptemp_ip_buffer,rdi .elseif eax == SEC_I_RENEGOTIATE;需要重新初始化 and @flags,0 .break ; InitializeSecurityContext .else nop nop nop nop nop and @flags,0 .break .endif .endw @exit: mov rsi,@heap_mid_buffer mov rcx,@deccount mov rax,_return_lehgth mov [rax],rcx mov rdi,_precv_buffer cld rep movsb invoke GetProcessHeap invoke HeapFree,rax,0,@heap_mid_buffer mov rax,@flags ret wait_and_recv_data_with_https endp connect_SecurityContext proc uses rbx rsi rdi _hSocket:qword,_pTargetName:qword,_pCredHandle:qword,_pCtxtHandle:qword,_ptimstamp:qword LOCAL @hSocket:qword LOCAL @readcount:qword LOCAL @retry_count:qword LOCAL @ptemp_ip_buffer:qword ;缓冲区指针 LOCAL @ppChainContext :qword LOCAL @pCertContext:qword LOCAL @pbcomputedhsah [20h]:byte LOCAL @pcbcomputedhash:qword LOCAL @psz [100h]:byte LOCAL @count_input :qword LOCAL @temp_ip_buffer_input:qword LOCAL @SecPkgContext_StreamSizes_buffer :SecPkgContext_StreamSizes LOCAL @SCHANNEL_CRED_buffer :_SCHANNEL_CRED LOCAL @flags:qword LOCAL @pfContextAttr :qword;指向变量的指针,用于接收一组指示已建立上下文属性的位标志: flages 设置情况的反馈 LOCAL @output_SecBufferDesc_buffer :_SecBufferDesc LOCAL @output_SecBuffer_buffer :_SecBuffer LOCAL @input_SecBufferDesc_buffer :_SecBufferDesc LOCAL @input_SecBuffer_buffer :_SecBuffer LOCAL @temp_buffer_for_list [5]:_SecBuffer; 5 dup (<?> LOCAL @temp_buffer_for_list_input [5]:_SecBuffer;_SecBuffer 5 dup (<?>) sub rsp,100h and @flags,0 mov @hSocket,rcx invoke RtlZeroMemory,addr @SCHANNEL_CRED_buffer,sizeof @SCHANNEL_CRED_buffer mov @SCHANNEL_CRED_buffer.dwVersion,SCHANNEL_CRED_VERSION SP_PROT_SSL3_CLIENT equ 20h SP_PROT_SSL2_CLIENT equ 8 mov @SCHANNEL_CRED_buffer.grbitEnabledProtocols,SP_PROT_TLS1_CLIENT SP_PROT_TLS1_1_CLIENT SP_PROT_TLS1_2_CLIENT ;mov @SCHANNEL_CRED_buffer.dwFlags,40h;SCH_CRED_MANUAL_CRED_VALIDATION SCH_CRED_NO_DEFAULT_CREDS;SCH_CRED_USE_DEFAULT_CREDS == 40 invoke AcquireCredentialsHandleA,0,addr sspi,SECPKG_CRED_OUTBOUND,0,addr @SCHANNEL_CRED_buffer,0,0,_pCredHandle,_ptimstamp .if rax {} SEC_E_OK;>>,nozero? jmp exit .endif mov @output_SecBufferDesc_buffer.ulVersion,SECBUFFER_VERSION;== 0 mov @output_SecBufferDesc_buffer.cBuffers,1 ;缓冲区个数 lea rax,@output_SecBuffer_buffer mov @output_SecBufferDesc_buffer.pBuffers,rax mov @output_SecBuffer_buffer.cbBuffer,0;sizeof output_pOutput_buffer ;函数返回后,这里会有接收的数据大小 mov @output_SecBuffer_buffer.BufferType,SECBUFFER_EMPTY;SECBUFFER_TOKEN;== 2 ;lea rax,@output_pOutput_buffer mov @output_SecBuffer_buffer.pvBuffer,0;rax flages equ ISC_REQ_SEQUENCE_DETECT ISC_REQ_REPLAY_DETECT ISC_REQ_CONFIDENTIALITY ISC_REQ_STREAM ISC_REQ_ALLOCATE_MEMORY ISC_REQ_USE_SUPPLIED_CREDS ;flages equ ISC_REQ_STREAM ISC_REQ_CONFIDENTIALITY ISC_REQ_REPLAY_DETECT ISC_REQ_SEQUENCE_DETECT ISC_REQ_INTEGRITY ISC_REQ_MUTUAL_AUTH;ISC_REQ_ALLOCATE_MEMORY ;ISC_REQ_USE_SUPPLIED_CREDS:Schannel不得尝试自动为客户端提供凭据 ;ISC_REQ_CONFIDENTIALITY 使用EncryptMessage函数加密消息。ISC_REQ_REPLAY_DETECT:检测使用EncryptMessage或MakeSignature函数编码的重播消息。ISC_REQ_SEQUENCE_DETECT:检测不按顺序接收的消息。 ;invoke InitializeSecurityContextA,addr CredHandle,addr CtxtHandle,0,ISC_REQ_STREAM ISC_REQ_USE_SUPPLIED_CREDS ISC_REQ_CONFIDENTIALITY ISC_REQ_REPLAY_DETECT ISC_REQ_SEQUENCE_DETECT,\ invoke InitializeSecurityContextA,_pCredHandle,0,_pTargetName,flages,\ 0,0,0,0, _pCtxtHandle,addr @output_SecBufferDesc_buffer,addr @pfContextAttr,0 .if rax {} SEC_I_CONTINUE_NEEDED;第一次调用应该是必需返回SEC_I_CONTINUE_NEEDED ;/* send initial handshake data which is now stored in output buffer */ ;The client must send the output token to the server and wait for a return token. The returned token is then passed in another call to InitializeSecurityContext (Schannel). The output token can be empty. jmp exit .endif ;this function only if the InitializeSecurityContext (Digest) call returned SEC_I_COMPLETE_NEEDED or SEC_I_COMPLETE_AND_CONTINUE. ;This function is supported only by the Digest security support provider (SSP). lea rbx,@output_SecBuffer_buffer mov r8d,@output_SecBuffer_buffer.cbBuffer mov rax,@output_SecBuffer_buffer.pvBuffer nop invoke _send,@hSocket, @output_SecBuffer_buffer.pvBuffer,r8,0 .if rax == 0;,zero? .if @output_SecBuffer_buffer.pvBuffer {} 0;>>,nozero? invoke FreeContextBuffer,@output_SecBuffer_buffer.pvBuffer .endif jmp exit .endif invoke FreeContextBuffer,@output_SecBuffer_buffer.pvBuffer and @readcount,0 and @retry_count,0 lea rdi,recv_token_buffer;这个缓冲接收服务器返回的令牌 mov @ptemp_ip_buffer,rdi .while TRUE again_: invoke ioctlsocket,@hSocket,FIONREAD,addr @readcount ;阻塞模式 在一次recv()中所接收的所有数据量。 ; 这通常与套接口中排队的数据总量相同。 ; 如果S是SOCK_DGRAM 型,则FIONREAD ; 返回套接口上排队的第一个数据报大小。 ;正常返回0 .if eax == 0;>>,zero? .if @readcount == 0;>>,zero? ;客户端connect 之后 如果没有发送数据 就会 @readcount== 0 invoke Sleep,100 inc @retry_count .if sqword ptr @retry_count {=30;>>,less?||equal? ; .continue ;jmp again_ .endif .else mov rdi,@ptemp_ip_buffer mov rax,@readcount invoke _recv,@hSocket,rdi,@readcount,0 .if rax == 0;,zero? jmp exit .endif mov rdi,@ptemp_ip_buffer add rdi,@readcount mov @ptemp_ip_buffer,rdi and @retry_count,0 and @readcount,0 .endif .else mov rax,0 jmp exit .endif mov @input_SecBufferDesc_buffer.ulVersion,SECBUFFER_VERSION;== 0 mov @input_SecBufferDesc_buffer.cBuffers,2 ;缓冲区个数 lea rax,@temp_buffer_for_list mov @input_SecBufferDesc_buffer.pBuffers,rax lea rdi,recv_token_buffer mov rcx,@ptemp_ip_buffer sub rcx,rdi mov [rax _SecBuffer.cbBuffer],ecx;recv接收的数据 mov [rax _SecBuffer.BufferType],SECBUFFER_TOKEN;== 2 ;On calls to this function after the initial call, there must be two buffers. The first has type SECBUFFER_TOKEN ; and contains the token received from the server. The second buffer has type SECBUFFER_EMPTY; set both the pvBuffer and cbBuffer members to zero. lea rsi,recv_token_buffer mov [rax _SecBuffer.pvBuffer],rsi add rax,sizeof _SecBuffer mov [rax _SecBuffer.cbBuffer],0;SECBUFFER_EMPTY 0 mov [rax _SecBuffer.BufferType],0 mov [rax _SecBuffer.pvBuffer],0 mov @output_SecBufferDesc_buffer.ulVersion,SECBUFFER_VERSION;== 0 mov @output_SecBufferDesc_buffer.cBuffers,3 ;缓冲区个数 lea rax,@temp_buffer_for_list_input mov @output_SecBufferDesc_buffer.pBuffers,rax mov [rax _SecBuffer.cbBuffer],0;SECBUFFER_EMPTY 0 mov [rax _SecBuffer.BufferType],0 mov [rax _SecBuffer.pvBuffer],0 add rax,sizeof _SecBuffer mov [rax _SecBuffer.cbBuffer],0;SECBUFFER_EMPTY 0 mov [rax _SecBuffer.BufferType],0 mov [rax _SecBuffer.pvBuffer],0 add rax,sizeof _SecBuffer mov [rax _SecBuffer.cbBuffer],0;SECBUFFER_EMPTY 0 mov [rax _SecBuffer.BufferType],0 mov [rax _SecBuffer.pvBuffer],0 add rax,sizeof _SecBuffer mov [rax _SecBuffer.cbBuffer],0;SECBUFFER_EMPTY 0 mov [rax _SecBuffer.BufferType],0 mov [rax _SecBuffer.pvBuffer],0 add rax,sizeof _SecBuffer mov [rax _SecBuffer.cbBuffer],0;SECBUFFER_EMPTY 0 mov [rax _SecBuffer.BufferType],0 mov [rax _SecBuffer.pvBuffer],0 invoke InitializeSecurityContextA,_pCredHandle,_pCtxtHandle,0,flages,\ 0,0,addr @input_SecBufferDesc_buffer,0,\ 0,addr @output_SecBufferDesc_buffer,addr @pfContextAttr,0 .if eax == SEC_E_OK;>>,zero? ;成功了 ;The security context was successfully initialized. There is no need for another InitializeSecurityContext (Schannel) call. ; If the function returns an output token, that is, if the SECBUFFER_TOKEN in pOutput is of nonzero length, that token must be sent to the server. ;https://docs.microsoft.com/en-us/windows/win32/secauthn/initializesecuritycontext--schannel lea rax,@pfContextAttr mov rax,_pCtxtHandle lea rsi,@temp_buffer_for_list_input nop mov @count_input,3 .while sqword ptr @count_input } 0;>>,greater? .if [rsi _SecBuffer.BufferType] == SECBUFFER_TOKEN;>>,zero? .if sqword ptr [rsi _SecBuffer.cbBuffer] } 0;>>,greater? .if sqword ptr [rsi _SecBuffer.pvBuffer] } 0;>>,greater? mov r8d,[rsi _SecBuffer.cbBuffer] mov rdx,[rsi _SecBuffer.pvBuffer] nop invoke _send,@hSocket, rdx,r8,0 .if rax == 0;zero? .break .endif ; mov rax,@temp_ip_buffer_input ; mov rcx,[rax _SecBuffer.pvBuffer] ; invoke FreeContextBuffer,rcx .endif .endif .endif add rsi,sizeof _SecBuffer dec @count_input .endw mov @flags,1 .break .elseif eax == SEC_E_INCOMPLETE_MESSAGE;>>,zero? ;Data for the whole message was not read from the wire. ;When this value is returned, the pInput buffer contains a SecBuffer structure ;with a BufferType member of SECBUFFER_MISSING. The cbBuffer member of SecBuffer ;contains a value that indicates the number of additional bytes that the function ;must read from the client before this function succeeds. While this number is not always accurate, ;using it can help improve performance by avoiding multiple calls to this function. ;"schannel: received incomplete message, need more data\n")); ;未从连线读取整条消息的数据。返回此值时,pInput缓冲区包含一个SecBuffer结构,其中缺少SecBuffer_的BufferType成员。SecBuffer的cbBuffer成员包含一个值, ;该值指示在该函数成功之前该函数必须从客户端读取的附加字节数。虽然这个数字并不总是准确的,但是使用它可以避免多次调用这个函数,从而帮助提高性能。 ;个人理解::仅仅为了避免多次调用这个函数 .if sqword ptr @retry_count } 10;>>,greater? ;我们的处理就是忽略这个请求!也就是说已经连续10次没有读到数据了,那就别指望服务端会提供数据了 ; invoke logout,addr atext (_error_connect_SecurityContext,"证书生成过程中服务端一直返回0数据,虽然连接没有断开,我们主动断开吧!"),sizeof _error_connect_SecurityContext .break .endif ; lea rax,@temp_buffer_for_list_input ; mov ecx,[rax _SecBuffer.cbBuffer] ;这里应该是总共需要这么多的数据才能完成本次请求。 ;.elseif <<cmp eax,SEC_I_INCOMPLETE_CREDENTIALS>>,zero? ; nop .elseif eax == SEC_I_COMPLETE_NEEDED;>>,zero? nop ; InitializeSecurityContext (Digest) 时有这种情况 .elseif eax == SEC_I_CONTINUE_NEEDED;>>,zero? ;这种情况是我们的input_SecBufferDesc_buffer 指定的缓冲区里的数据没有处理完,需要继续处理 ;send handshake token to server */ lea rsi,@temp_buffer_for_list_input mov @count_input,3 .while sqword ptr @count_input } 0;greater? .if [rsi _SecBuffer.BufferType] == SECBUFFER_TOKEN;>>,zero? .if sqword ptr [rsi _SecBuffer.cbBuffer] } 0;>>,greater? .if [rsi _SecBuffer.pvBuffer] } 0;>>,greater? mov r8d,[rsi _SecBuffer.cbBuffer] mov rdx,[rsi _SecBuffer.pvBuffer] nop invoke _send,@hSocket, rdx,r8,0 .if rax == 0 ;>>,zero? .break .endif .endif .endif .endif add rsi,sizeof _SecBuffer dec @count_input .endw lea rax,@temp_buffer_for_list_input add rax,sizeof _SecBuffer mov ecx,[rax _SecBuffer.BufferType] .if ecx == SECBUFFER_EXTRA;>>,zero?;The security package uses this value to ; indicate the number of extra or ; unprocessed bytes in a message. mov ecx,[rax _SecBuffer.cbBuffer];未处理的数据大小 mov rsi,[rax _SecBuffer.pvBuffer] ; int 3 add rsi,rcx lea rdi,recv_token_buffer rep movsb and @readcount,0 and @retry_count,0 mov @ptemp_ip_buffer,rdi .else and @readcount,0 and @retry_count,0 lea rdi,recv_token_buffer mov @ptemp_ip_buffer,rdi .endif .else .break .endif mov @count_input,3 lea rax,@temp_buffer_for_list_input mov @temp_ip_buffer_input,rax .while sqword ptr @count_input } 0;greater? .if sqword ptr [rax _SecBuffer.cbBuffer] } 0;>>,greater? .if sqword ptr [rax _SecBuffer.pvBuffer] } 0;>>,greater? mov rax,@temp_ip_buffer_input mov rcx,[rax _SecBuffer.pvBuffer] invoke FreeContextBuffer,rcx .endif .endif add @temp_ip_buffer_input,sizeof _SecBuffer mov rax,@temp_ip_buffer_input dec @count_input .endw .endw mov @count_input,3 lea rax,@temp_buffer_for_list_input mov @temp_ip_buffer_input,rax .while sqword ptr @count_input } 0;,greater? .if sqword ptr [rax _SecBuffer.cbBuffer] } 0;>>,greater? .if sqword ptr [rax _SecBuffer.pvBuffer] } 0;>>,greater? mov rax,@temp_ip_buffer_input mov rcx,[rax _SecBuffer.pvBuffer] invoke FreeContextBuffer,rcx .endif .endif add @temp_ip_buffer_input,sizeof _SecBuffer mov rax,@temp_ip_buffer_input dec @count_input .endw jmp exit mov rax,1 exit: mov rax,@flags add rsp,100h ret connect_SecurityContext endp disconnect_server_for_https proc _hSocket:qword,_pCredHandle:qword,_pCtxtHandle:qword LOCAL @hheap:qword invoke FreeCredentialsHandle,_pCredHandle invoke DeleteSecurityContext,_pCtxtHandle invoke shutdown,_hSocket,2;0 不能再读,1不能再写,2 读写都不能。 invoke closesocket,_hSocket ret disconnect_server_for_https endp connect_server_for_https proc uses rbx rsi rdi _ipaddress:PVOID,_TargetName:PVOID,_pCredHandle:PVOID,_pCtxtHandle:PVOID,_ptimstamp:PVOID LOCAL @stWsa:WSADATA LOCAL @stSin:sockaddr_in LOCAL @hSocket:qword LOCAL @hheap:qword LOCAL @SecPkgContext_StreamSizes_buffer :SecPkgContext_StreamSizes invoke WSAStartup,0202h,addr @stWsa invoke socket,AF_INET,SOCK_STREAM,IPPROTO_TCP .if rax ==INVALID_SOCKET ; invoke logout,addr atext (https_WSAStartup_error,"htttps_WSAStartup_error"),sizeof https_WSAStartup_error ; invoke MessageBoxA,hWnd,addr errorsocket,addr errorsocket,MB_OK mov rax,0 jmp exit .endif mov @hSocket,rax invoke HostnameToIP,_ipaddress .if eax == 0 invoke MessageBox,hWnd,addr error_ip,addr error_ip,MB_OK ret .endif mov @stSin.sin_addr,eax mov @stSin.sin_family,AF_INET mov eax,443 invoke htons,rax ;htonl()表示将32位的主机字节顺序转化为32位的网络字节顺序 htons()表示将16位的主机字节顺序转化为16位的网络字节顺序(ip地址是32位的端口号是16位的 ) mov @stSin.sin_port,ax @@: invoke connect,@hSocket,addr @stSin,sizeof @stSin .if eax == SOCKET_ERROR invoke WSAGetLastError .if eax {} WSAEWOULDBLOCK;nozero? invoke Sleep,1000 ; invoke logout,addr atext (_https_again_connect,"https_again_connect?"),sizeof _https_again_connect ; invoke MessageBoxA,hWnd,addr again_connect,addr again_connect,MB_OKCANCEL;;"确定== 1" "取消== 2" mov rax,0 jmp exit .endif .endif invoke connect_SecurityContext,@hSocket,_TargetName,_pCredHandle,_pCtxtHandle,_ptimstamp .if rax == 0;zero? ; invoke logout,addr atext(error_SecurityContext,"connect_SecurityContext调用失败"),sizeof error_SecurityContext jmp exit .endif invoke QueryContextAttributesA,_pCtxtHandle,SECPKG_ATTR_STREAM_SIZES,addr @SecPkgContext_StreamSizes_buffer .if rax {} 0;nozero? ; invoke logout,addr atext(_QueryContextAttributes,"QueryContextAttributes调用失败"),sizeof _QueryContextAttributes mov rax,0 jmp exit .endif mov eax,@SecPkgContext_StreamSizes_buffer.cbMaximumMessage .if eax == 0;zero? ; invoke logout,addr atext(_cbMaximumMessage,"cbMaximumMessage为空值"),sizeof _cbMaximumMessage jmp exit .endif mov eax,@SecPkgContext_StreamSizes_buffer.cBuffers .if eax { 4;>>,above? ; invoke logout,addr atext(_StreamSizes_buffer_cBuffers,"没有加解密缓冲区"),sizeof _StreamSizes_buffer_cBuffers mov rax,0 jmp exit .endif mov rax,@hSocket ret exit: mov rax,0 ret connect_server_for_https endp WinMain proc hInst:qword,hPrevInst:qword,CmdLine:qword,CmdShow:qword LOCAL wc:WNDCLASSEX LOCAL msg:MSG LOCAL @hInst:qword LOCAL @hPrevInst:qword LOCAL @CmdLine:qword LOCAL @CmdShow:qword LOCAL icex:INITCOMMONCONTROLSEX invoke GetModuleHandle,0 mov hInstance,rax invoke GetCommandLine mov CommandLine,rax invoke RtlZeroMemory,addr wc,sizeof wc invoke InitCommonControls mov icex.dwSize,sizeof INITCOMMONCONTROLSEX mov icex.dwICC,ICC_TAB_CLASSES invoke InitCommonControlsEx,addr icex mov wc.cbSize,sizeof WNDCLASSEX mov wc.style,CS_HREDRAW or CS_VREDRAW lea rax,WndProc mov wc.lpfnWndProc,rax;offset WndProc mov wc.cbClsExtra,NULL mov wc.cbWndExtra,DLGWINDOWEXTRA push hInstance pop wc.hInstance mov wc.hbrBackground,COLOR_BTNFACE 1 mov wc.lpszMenuName,IDM_MENU;0 lea rax, ClassName mov wc.lpszClassName,rax;offset ClassName invoke LoadIcon,NULL,IDI_APPLICATION mov wc.hIcon,rax mov wc.hIconSm,rax invoke LoadCursor,0,IDC_ARROW mov wc.hCursor,rax invoke RegisterClassEx,addr wc invoke CreateDialogParam,hInstance,IDD_DIALOG,NULL,addr WndProc,NULL invoke ShowWindow,hWnd,SW_SHOWNORMAL invoke UpdateWindow,hWnd .while TRUE invoke GetMessage,addr msg,0,0,0 .if eax == 0 .break .endif invoke TranslateMessage,addr msg invoke DispatchMessage,addr msg .endw invoke ExitProcess,0 mov rax,msg.wParam ret WinMain endp EncryptMessage_and_send proc uses rbx rsi rdi _hSocket:qword,_pCtxtHandle:qword,_pget_or_post_buffer:qword,_pget_or_post_buffer_length:qword LOCAL @hSocket:qword LOCAL @buffer_for_https_count:qword LOCAL @pget_or_post_buffer:qword LOCAL @_pget_or_post_buffer_length:qword LOCAL @thistimelength:qword;本次参与运算的长度 LOCAL @hheap:qword LOCAL @SecPkgContext_StreamSizes_buffer :SecPkgContext_StreamSizes LOCAL @temp_buffer_for_list [5]:_SecBuffer; 5 dup (<?> LOCAL @EncryptMessage_SecBufferDesc_buffer :_SecBufferDesc LOCAL @pheap_send_buffer:qword LOCAL @heap_send_buffer_size:qword mov @hSocket,rcx mov @pget_or_post_buffer,r8 mov @_pget_or_post_buffer_length,r9 mov @buffer_for_https_count,r9 invoke HeapAlloc,rbx,HEAP_ZERO_MEMORY,heap_send__size .if rax {} 0 mov @pheap_send_buffer,rax .else ret .endif mov @heap_send_buffer_size,heap_send__size .while sqword ptr @buffer_for_https_count }0;>>,greater? invoke RtlZeroMemory,@pheap_send_buffer,@heap_send_buffer_size invoke QueryContextAttributesA,_pCtxtHandle,SECPKG_ATTR_STREAM_SIZES,addr @SecPkgContext_StreamSizes_buffer .if rax {} 0;>>,nozero? jmp exit .endif mov rcx,@buffer_for_https_count mov ebx,@SecPkgContext_StreamSizes_buffer.cbMaximumMessage ;最大单次可以处理的内存空间(数据大小) .if sqword ptr rcx } rbx;>>,GREATER? ;谁的空间小就用谁 mov @thistimelength,rbx sub @buffer_for_https_count,rbx .else mov @thistimelength,rcx mov @buffer_for_https_count,0 .endif mov @EncryptMessage_SecBufferDesc_buffer.ulVersion,SECBUFFER_VERSION;== 0 mov @EncryptMessage_SecBufferDesc_buffer.cBuffers,4 lea rax,@temp_buffer_for_list mov @EncryptMessage_SecBufferDesc_buffer.pBuffers,rax mov ecx,@SecPkgContext_StreamSizes_buffer.cbHeader mov [rax _SecBuffer.cbBuffer],ecx mov [rax _SecBuffer.BufferType],SECBUFFER_STREAM_HEADER ;==7 mov rsi,@pheap_send_buffer mov [rax _SecBuffer.pvBuffer],rsi add rsi,rcx add rax,sizeof _SecBuffer mov rcx,@thistimelength mov [rax _SecBuffer.cbBuffer],ecx mov [rax _SecBuffer.BufferType],SECBUFFER_DATA ;==1 mov [rax _SecBuffer.pvBuffer],rsi mov rdi,rsi mov rsi,_pget_or_post_buffer mov rcx,@thistimelength shr rcx,3 ;/8 cld rep movsq mov rcx,@thistimelength and rcx,111b rep movsb add rax,sizeof _SecBuffer mov ecx,@SecPkgContext_StreamSizes_buffer.cbTrailer mov [rax _SecBuffer.cbBuffer],ecx mov [rax _SecBuffer.BufferType],SECBUFFER_STREAM_TRAILER ;==6 mov [rax _SecBuffer.pvBuffer],rdi add rax,sizeof _SecBuffer mov [rax _SecBuffer.cbBuffer],0 mov [rax _SecBuffer.BufferType],0 mov [rax _SecBuffer.pvBuffer],0 ;These buffers must be supplied in the order shown. ; Buffer type Description ; SECBUFFER_STREAM_HEADER Used internally. No initialization required. ; SECBUFFER_DATA Contains the plaintext message to be encrypted. ; SECBUFFER_STREAM_TRAILER Used internally. No initialization required. ; SECBUFFER_EMPTY Used internally. No initialization required. Size can be zero. ;https://docs.microsoft.com/en-us/windows/win32/secauthn/encryptmessage--schannel mov ecx,@SecPkgContext_StreamSizes_buffer.cbHeader mov @EncryptMessage_SecBufferDesc_buffer.ulVersion,SECBUFFER_VERSION;== 0 mov @EncryptMessage_SecBufferDesc_buffer.cBuffers,4 lea rax,@temp_buffer_for_list mov @EncryptMessage_SecBufferDesc_buffer.pBuffers,rax invoke EncryptMessage,_pCtxtHandle,0,addr @EncryptMessage_SecBufferDesc_buffer,0 .if rax{} SEC_E_OK;,nozero? jmp exit .endif lea rax,@temp_buffer_for_list mov ecx,@SecPkgContext_StreamSizes_buffer.cbHeader mov edx,[rax _SecBuffer.cbBuffer] .if ecx {} edx;>>,nozero? ;也就是说加密的数据大小发生了变化,需要调整一下每个类型的位置 ; int 3 nop mov rdi,[rax _SecBuffer.pvBuffer] add rdi,rdx add rax,sizeof _SecBuffer;第一个尾部 mov rsi,[rax _SecBuffer.pvBuffer];第二个 mov ecx,[rax _SecBuffer.cbBuffer] cld rep movsb add rax,sizeof _SecBuffer mov rsi,[rax _SecBuffer.pvBuffer];第三个 mov ecx,[rax _SecBuffer.cbBuffer] cld rep movsb .endif lea rax,@temp_buffer_for_list mov ecx,[rax _SecBuffer.cbBuffer] add rax,sizeof _SecBuffer add ecx,[rax _SecBuffer.cbBuffer] add rax,sizeof _SecBuffer add ecx,[rax _SecBuffer.cbBuffer] mov rax,rcx invoke _send,@hSocket, @pheap_send_buffer,rax,0 .if eax == 0;,zero? jmp exit .endif mov rcx,@thistimelength add @pget_or_post_buffer,rcx .endw invoke GetProcessHeap invoke HeapFree,rax,0,@pheap_send_buffer mov rax,1 exit: ret ;返回0 是发送错误,1是正确,其余值是EncryptMessage 错误 EncryptMessage_and_send endp uncompress_gzip proc uses rbx rsi rdi ungzipbuffer:qword,pungzipbuflength:qword,orgbuffer:qword,orgbuflength:qword mov z_stream_buffer.zalloc,0 mov z_stream_buffer.zfree,0 mov z_stream_buffer.opaque,0 mov z_stream_buffer.next_in,r8;原数据 mov z_stream_buffer.avail_in,r9d;原数据长度 mov z_stream_buffer.next_out,rcx ;解压缩后的数据缓冲区 mov rdx,[rdx] mov z_stream_buffer.avail_out,edx ;compression levels :Z_DEFAULT_STRATEGY == 0 FLAGS_GZIP EQU MAX_WBITS 16 invoke inflateInit2_,addr z_stream_buffer,FLAGS_GZIP,addr zlib_ver,sizeof z_stream_buffer;解压缩 .if eax == Z_OK .while TRUE invoke inflate ,addr z_stream_buffer,Z_NO_FLUSH;==0 .if eax == Z_STREAM_END .break .elseif eax {} Z_OK;>>,nozero? mov rcx,0 mov rdi,pungzipbuflength mov [rdi],rcx ret .endif .endw invoke inflateEnd,addr z_stream_buffer .if eax == Z_OK;>>,zero? mov ecx,z_stream_buffer.total_out .else mov rcx,0 .endif .endif mov rdi,pungzipbuflength mov [rdi],rcx ret uncompress_gzip endp parsehttp_for_chunked proc uses rbx rsi rdi r12 r13 _preturn_buffer:qword, _return_buffer_size:qword,_phttp_buffer:qword,_http_buffer_size:qword,_preturn_size:qword LOCAL @data_size:qword LOCAL @hex_buffer:qword LOCAL @current_size:qword LOCAL @flags_deflate:qword LOCAL @flags_gzip:qword LOCAL @uncompress_count:qword LOCAL @flags_chunked:qword and @flags_chunked,0 and @data_size,0 mov rbx,_http_buffer_size sub rbx,sizeof Transfer_Encoding-1 mov rax,0 ; int 3 .while sdword ptr ebx } 0 mov rdi,_phttp_buffer lea rdi,[rdi rax] lea rsi,Transfer_Encoding mov rcx,Transfer_Encoding_size cld repz cmpsb .if ZERO? ;找到chunked块 mov rcx,rdi sub rcx,_phttp_buffer ;先前查找占用的字节长度 mov rbx,_http_buffer_size sub rbx,rcx mov r13,0 mov r12,0 ;chunked块 合并到别一内存的指针 .while sdword ptr ebx } 0 .if word ptr [rdi r13] == 0a0dh ;这里是16进制表示长度的 有多个块 and @hex_buffer,0 invoke ASCII2HEX,addr @hex_buffer,sizeof @hex_buffer,rdi,r13 mov rax,@hex_buffer add @data_size,rax mov @current_size,rax sub rbx,r13 ;减少计数0d0ah sub rbx,2 ;减少计数0d0ah lea rdi,[rdi r13] add rdi,2 ;跳过0d0ah ;;;;保存数据 mov rsi,rdi mov rdi,_preturn_buffer lea rdi,[rdi r12] mov rcx,@current_size add r12,rcx cld rep movsb .if word ptr [rsi] {} 0a0dh ;块结构以0d0ah 结束 ret .endif add rsi,2;跳过0d0ah mov rdi,rsi or @flags_chunked,1 sub rbx,@current_size sub rbx,2 ;减少计数0d0ah mov r13,0 .continue .endif inc r13 dec rbx .endw .break .endif inc rax dec rbx .endw .if @flags_chunked == 0 ;没有找到chunked 类型 继续查找 mov rbx,_http_buffer_size sub rbx,sizeof Content_Length-1 mov rax,0 .while sdword ptr ebx } 0 mov rdi,_phttp_buffer lea rdi,[rdi rax] lea rsi,Content_Length mov rcx,sizeof Content_Length-1 cld repz cmpsb .if ZERO? mov rcx,rdi sub rcx,_phttp_buffer ;先前查找占用的字节长度 mov rbx,_http_buffer_size sub rbx,rcx sub rbx,2 ;0d0a0d0a ;剩余可查找的空间字节长度 mov rcx,0 .while sdword ptr ebx } 0 .if word ptr [rdi rcx] == 0a0dh ;;这里是10进制表示的 invoke asciitoint_decimal,rdi,rcx ;返回rax add @data_size,rax mov rcx,rdi sub rcx,_phttp_buffer ;先前查找占用的字节长度 mov rbx,_http_buffer_size sub rbx,rcx sub rbx,2 ;0d0a0d0a ;剩余可查找的空间字节长度 mov rcx,0 .while sdword ptr ebx } 0 .if dword ptr [rdi rcx] == 0a0d0a0dh ;http头部结束标志 lea rsi, [rdi rcx 4] mov rdi,_preturn_buffer mov rcx,@data_size cld rep movsb .break .endif inc rcx .endw .break .endif inc rcx dec rbx .endw .break .endif inc rax dec rbx .endw .endif and @flags_deflate,0 and @flags_gzip,0 mov rbx,_http_buffer_size sub rbx,sizeof Content_Encoding_gzip-1 mov rax,0 .while sdword ptr ebx } 0 mov rsi,_phttp_buffer lea rsi,[rsi rax] lea rdi,Content_Encoding_gzip mov rcx,sizeof Content_Encoding_gzip-1 cld repz cmpsb .if ZERO? ;找到 or @flags_gzip,1 .break .endif inc rax dec ebx .endw ; int 3 .if @flags_gzip == 0 mov rbx,_http_buffer_size sub rbx,sizeof Content_Encoding_deflate-1 mov rax,0 .while sdword ptr ebx } 0 mov rsi,_phttp_buffer lea rsi,[rsi rax] lea rdi,Content_Encoding_deflate mov rcx,sizeof Content_Encoding_deflate-1 cld repz cmpsb .if ZERO? ;找到 or @flags_deflate,1 .break .endif inc rax dec ebx .endw .endif .if @flags_gzip == 1 ;用gzip解压 mov rax,_preturn_size mov rax,[rax] mov @uncompress_count,rax invoke uncompress_gzip,_phttp_buffer,addr @uncompress_count,_preturn_buffer,@data_size mov rsi,_phttp_buffer mov rcx,@uncompress_count mov rdi,_preturn_buffer cld rep movsb mov rcx,@uncompress_count mov rdx,_preturn_size mov [rdx],rcx .elseif @flags_deflate ==1 mov rax,_preturn_size mov rax,[rax] mov @uncompress_count,rax invoke uncompress,_phttp_buffer,addr @uncompress_count,_preturn_buffer,@data_size mov rsi,_phttp_buffer mov rcx,@uncompress_count mov rdi,_preturn_buffer cld rep movsb mov rcx,@uncompress_count mov rdx,_preturn_size mov [rdx],rcx .else mov rcx,@data_size mov rdx,_preturn_size mov [rdx],rcx mov rax,0 .endif ret parsehttp_for_chunked endp WndProc proc uses rbx rsi rdi r12 r13 hWin:HWND,uMsg:UINT64,wParam:WPARAM,lParam:LPARAM LOCAL @PpcPackages:qword LOCAL @ppPackageInfo:qword LOCAL @pheap_send_buffer:qword LOCAL @heap_send_buffer_size:qword LOCAL @pheap_recv_buffer:qword LOCAL @heap_recv_buffer_size:qword LOCAL @return_count:qword LOCAL @data_size:qword LOCAL @hex_buffer:qword LOCAL @current_size:qword LOCAL @uncompress_count:qword LOCAL @pheap_send_text2voice_with_send:qword ;要转换语音的文字缓冲区 LOCAL @pheap_send_text2voice_with_EncryptMessage:qword ;要发送到服务端的缓冲区数据 local @instrbuffer [4]:qword mov rax,uMsg .if eax==WM_INITDIALOG push hWin pop hWnd mov WAVEFORMATEX_out_buffer.wFormatTag,WAVE_FORMAT_PCM mov WAVEFORMATEX_out_buffer.nChannels,1;2 mov WAVEFORMATEX_out_buffer.nSamplesPerSec,16000;44100 mov WAVEFORMATEX_out_buffer.nAvgBytesPerSec,16*1/8*16000;16*2/8*44100 ;waveForm.nBlockAlign * waveForm.nSamplesPerSec mov WAVEFORMATEX_out_buffer.nBlockAlign,16*1/8;16*1/8;16*2/8 ;waveForm.wBitsPerSample * waveForm.nChannels) >> 3; mov WAVEFORMATEX_out_buffer.wBitsPerSample,16 mov WAVEFORMATEX_out_buffer.cbSize,0 invoke waveOutOpen,addr hwaveout,WAVE_MAPPER,addr WAVEFORMATEX_out_buffer,hWin,0,CALLBACK_WINDOW;CALLBACK_EVENT;,hInstance,CALLBACK_WINDOW; .if rax {} MMSYSERR_NOERROR;>>,nozero? invoke MessageBoxA,hWin,addr atext(_waveOut,"waveOut打开 错误"),0,MB_OK .endif .elseif eax == MM_WOM_DONE;>>,EQUAL? ; The MM_WOM_DONE message is sent to a window when the given output buffer is being returned to the application. ;Buffers are returned to the application when they have been played, or as the result of a call to the waveOutReset function. mov rax,lParam mov rax,lParam mov rcx,[rax WAVEHDR.dwUser] .if rcx == 1;>>,zero? invoke waveOutUnprepareHeader,hwaveout, addr WAVEHDR_out_buffer,sizeof WAVEHDR_out_buffer; invoke GetProcessHeap invoke HeapFree,rax,0,WAVEHDR_out_buffer.lpData .endif .elseif eax==WM_COMMAND mov rax,wParam and rax,0FFFFh .if rax==IDM_FILE_EXIT invoke SendMessage,hWin,WM_CLOSE,0,0 .elseif rax==IDM_HELP_ABOUT invoke ShellAbout,hWin,addr AppName,addr AboutMsg,NULL .elseif rax == 1002 ;连接百度智能云 鉴权认证 and @data_size,0 invoke GetProcessHeap mov rbx,rax invoke HeapAlloc,rbx,HEAP_ZERO_MEMORY,heap_send__size .if rax {} 0 mov @pheap_send_buffer,rax .else ret .endif mov @heap_send_buffer_size,heap_send__size invoke HeapAlloc,rbx,HEAP_ZERO_MEMORY,heap_recv__size .if rax {} 0 mov @pheap_recv_buffer,rax .else invoke HeapFree,rbx,0,@pheap_send_buffer ret .endif mov @heap_recv_buffer_size,heap_recv__size invoke connect_server_for_https,addr ipbaidu_voice_token,addr TargetName_voiceandtext_token,addr CredHandle,addr CtxtHandle,addr timstamp ; .if rax {} 0 mov hSocket,rax mov r9d,https_for_baidu_voice_token_size invoke EncryptMessage_and_send,hSocket,addr CtxtHandle,addr https_for_baidu_voice_token,r9 .if rax == 1;;返回0 是send()错误,1是正确,其余值是EncryptMessage() 错误 invoke wait_and_recv_data_with_https,hSocket,addr CtxtHandle,@pheap_recv_buffer,@heap_recv_buffer_size,addr @return_count .if rax {}0 invoke parsehttp_for_chunked,@pheap_send_buffer,@heap_send_buffer_size,@pheap_recv_buffer,@return_count,addr @uncompress_count ; int 3 mov rbx,@uncompress_count sub rbx,sizeof access_token_token-1 mov rax,0 .while sdword ptr ebx } 0 lea rsi,access_token_token mov rdi,@pheap_send_buffer lea rdi,[rdi rax] mov rcx,sizeof access_token_token-1 cld repz cmpsb .if ZERO? ;找到 mov rcx,rdi mov r8,rdi sub rcx,@pheap_send_buffer ;先前查找占用的字节长度 mov rbx,@uncompress_count sub rbx,rcx mov rax,0 .while sdword ptr ebx } 0 .if byte ptr [rdi rax]== '"'; mov rdi,r8 and byte ptr [rdi rax],0 invoke SetDlgItemText,hWin,1001,r8 .break .endif inc rax dec ebx .endw .break .endif inc rax dec ebx .endw .endif .endif .endif invoke GetProcessHeap mov rbx,rax invoke HeapFree,rbx,0,@pheap_send_buffer invoke HeapFree,rbx,0,@pheap_recv_buffer invoke disconnect_server_for_https , hSocket,addr CredHandle,addr CtxtHandle .elseif rax == 1003 ;文字转语音 invoke GetProcessHeap mov rbx,rax invoke HeapAlloc,rbx,HEAP_ZERO_MEMORY,heap_send__size .if rax {} 0 mov @pheap_send_text2voice_with_send,rax .else ret .endif mov @heap_send_buffer_size,heap_send__size invoke HeapAlloc,rbx,HEAP_ZERO_MEMORY,heap_recv__size .if rax {} 0 mov @pheap_send_text2voice_with_EncryptMessage,rax .else ret .endif mov @heap_recv_buffer_size,heap_recv__size ; int 3 invoke connect_server_for_https,addr ipbaidu_text2voice,addr TargetName_text2voice,addr CredHandle_text2voice,addr CtxtHandle_text2voice,addr timstamp_text2voice ; .if rax == 0 ret .endif mov hSocket_text2voice,rax invoke GetDlgItemText,hWin,1001,addr token_buffer_text2voice,sizeof token_buffer_text2voice invoke GetDlgItemText,hWin,1004,@pheap_send_text2voice_with_send,heap_send__size;要转换语音的文字 invoke MultiByteToWideChar,CP_ACP, 0,@pheap_send_text2voice_with_send,-1,@pheap_send_text2voice_with_EncryptMessage,heap_send__size ;return:Number of wide characters invoke WideCharToMultiByte,CP_UTF8,0,@pheap_send_text2voice_with_EncryptMessage,-1,@pheap_send_text2voice_with_send,heap_send__size,0,0 ;return:The number of bytes mov rdi,@pheap_send_text2voice_with_send mov dword ptr [rdi rax],0 lea rdi,@instrbuffer lea rcx, token_buffer_text2voice mov [rdi],rcx mov rax,@pheap_send_text2voice_with_send mov [rdi 8],rax invoke wvsprintfA,@pheap_send_text2voice_with_EncryptMessage,addr format_https_for_baidu_text2voice_body,rdi invoke lstrlenA,@pheap_send_text2voice_with_EncryptMessage lea rdi,@instrbuffer mov [rdi],rax invoke wvsprintfA,@pheap_send_text2voice_with_send,addr format_https_for_baidu_text2voice,rdi invoke lstrcatA,@pheap_send_text2voice_with_send,@pheap_send_text2voice_with_EncryptMessage invoke lstrlenA,@pheap_send_text2voice_with_send mov r9,rax ; int 3 mov rax,@pheap_send_text2voice_with_send invoke EncryptMessage_and_send,hSocket_text2voice,addr CtxtHandle_text2voice, @pheap_send_text2voice_with_send,r9 invoke wait_and_recv_data_with_https,hSocket_text2voice,addr CtxtHandle_text2voice,@pheap_send_text2voice_with_EncryptMessage,heap_recv__size,addr @return_count .if rax {}0 invoke parsehttp_for_chunked,@pheap_send_text2voice_with_send,heap_send__size,@pheap_send_text2voice_with_EncryptMessage,heap_send__size,addr @uncompress_count .if rax == 0 ; int 3 invoke GetProcessHeap invoke HeapAlloc,rax,HEAP_ZERO_MEMORY,@uncompress_count .if rax {} 0 mov rsi,@pheap_send_text2voice_with_send mov rdi,rax mov rbx,rax mov rcx,@uncompress_count cld rep movsb mov WAVEHDR_out_buffer.lpData,rbx mov rax,@uncompress_count mov WAVEHDR_out_buffer.dwBufferLength,eax mov WAVEHDR_out_buffer.dwBytesRecorded,0 mov WAVEHDR_out_buffer.dwUser,1 mov WAVEHDR_out_buffer.dwFlags,0 mov WAVEHDR_out_buffer.dwLoops,0 mov WAVEHDR_out_buffer.lpNext,0 mov WAVEHDR_out_buffer.Reserved,0 invoke waveOutPrepareHeader,hwaveout, addr WAVEHDR_out_buffer,sizeof WAVEHDR_out_buffer;//准备一个波形数据块头用于录音 invoke waveOutWrite,hwaveout, addr WAVEHDR_out_buffer,sizeof WAVEHDR_out_buffer .endif .endif .endif invoke GetProcessHeap mov rbx,rax invoke HeapFree,rbx,0,@pheap_send_text2voice_with_send invoke HeapFree,rbx,0,@pheap_send_text2voice_with_EncryptMessage invoke disconnect_server_for_https,hSocket_text2voice,addr CredHandle,addr CtxtHandle .endif ; int 3 ; invoke EnumerateSecurityPackagesA,addr @PpcPackages,addr @ppPackageInfo ; .elseif eax==WM_SIZE .elseif eax==WM_CLOSE invoke DestroyWindow,hWin .elseif uMsg==WM_DESTROY invoke PostQuitMessage,NULL .else invoke DefWindowProc,hWin,uMsg,wParam,lParam ret .endif xor rax,rax ret WndProc endp end

include win64.inc include ksamd64.inc include Macros\x64macros.inc include Macros\x64calling.inc include Macros\vasily.inc include user32.inc include kernel32.inc include shell32.inc include comctl32.inc include comdlg32.inc include secur32.inc include zlibstat.inc include ws2_32.inc include winmm.inc ;wave includelib user32.lib includelib kernel32.lib includelib shell32.lib includelib comctl32.lib includelib comdlg32.lib includelib Secur32.Lib includelib ws2_32.lib includelib winmm.lib includelib zlibstat.lib WinMain PROTO :QWORD,:QWORD,:QWORD,:QWORD WndProc PROTO :QWORD,:QWORD,:QWORD,:QWORD IDD_DIALOG equ 1000 IDM_MENU equ 10000 IDM_FILE_EXIT equ 10001 IDM_HELP_ABOUT equ 10101 atext macro _name:REQ,_text:REQ,other:VARARG .data align 8 _name db _text for _ehter:REQ,<other> db _ehter ;0d0ah endm db 0 .code exitm <_name> endm ifndef _SecHandle _SecHandle struct dwLower ULONG_PTR ? dwUpper ULONG_PTR ? SecHandle ends endif heap_send__size equ 1024*1024*8 heap_recv__size equ 1024*1024*8 .const ClassName db 'DLGCLASS',0 AppName db 'Dialog as main',0 AboutMsg db 'MASM64 RadASM Dialog as main',13,10,'Copyright ? masm64 2001',0 ;sspi db "Microsoft Unified Security Protocol Provider",0 ;Schannel Security Package 微软提供了10几种安全包类型,这只是其中之一 sspi db "Default TLS SSP",0 error_ip db "IP地址错误",0 zlib_ver db "1.2.11",0 ipbaidu_text2voice db "tsn.baidu.com",0;tsn.baidu.com汉字转语音 TargetName_text2voice db "tsn.baidu.com",0 ;ipbaidu db "aip.baidubce.com",0;"39.156.66.110",0;aip.baidubce.com 令牌 ;TargetName db "aip.baidubce.com",0 ipbaidu_voice_token db "openapi.baidu.com",0;openapi.baidu.com 语音识别令牌;39.156.66.111 TargetName_voiceandtext_token db "openapi.baidu.com",0 .data? hInstance dq ? CommandLine dq ? hWnd dq ? hSocket dq ? hSocket_text2voice dq ? token_buffer_text2voice dq 100h dup (?) recv_token_buffer dd 4000h dup (?);这个缓冲接收服务器返回的令牌 z_stream_buffer z_stream <?> CERT_CHAIN_ENGINE_CONFIG_buffer CERT_CHAIN_ENGINE_CONFIG <?> CERT_CHAIN_PARA_buffer CERT_CHAIN_PARA <?> CERT_CHAIN_POLICY_PARA_buffer CERT_CHAIN_POLICY_PARA <?> HTTPSPolicyCallbackData_buffer HTTPSPolicyCallbackData <> CERT_CHAIN_POLICY_STATUS_buffer CERT_CHAIN_POLICY_STATUS <?> CredHandle _SecHandle <> CtxtHandle _SecHandle <> timstamp FILETIME <> CredHandle_text2voice _SecHandle <> CtxtHandle_text2voice _SecHandle <> timstamp_text2voice FILETIME <> hwaveout dq ?;波形输出句柄 WAVEFORMATEX_out_buffer WAVEFORMATEX <?> WAVEHDR_out_buffer WAVEHDR <?> ifndef atext atext macro _name:REQ,_text:REQ,other:VARARG .data align 8 _name db _text for _ehter:REQ,<other> db _ehter ;0d0ah endm db 0 .code exitm <_name> endm endif .data status_code db "HTTP/1.1 200 OK",0 Content_Length db "Content-Length: ",0 Content_Encoding db "Content-Encoding: ",0 Transfer_Encoding db "Transfer-Encoding: chunked" dw 0a0dh dw 0a0dh Transfer_Encoding_size EQU $ -Transfer_Encoding chunked_end db "0" dw 0a0dh dw 0a0dh chunked_end_size EQU $ -chunked_end access_token_token db '"access_token":"',0;{"refresh_token":"25.9891901e2e1e7ac6d676322886ab3a83.314320000.1869313674.282335-15834333","expires_in":2592000,"session_ke access_token_token_end_flags db '"',0 ;未使用 Content_Encoding_gzip db "Content-Encoding: gzip",0 Content_Encoding_deflate db "Content-Encoding: deflate",0 https_for_baidu_voice_token db 'POST /oauth/2.0/token?grant_type=client_credentials&client_id=你申请的账号&client_secret=你申请的密码 HTTP/1.1' dw 0a0dh db 'Host: openapi.baidu.com' dw 0a0dh db 'Accept: text/html, application/xhtml xml, */*';告诉WEB服务器自己接受什么介质类型,/ 表示任何类型 dw 0a0dh db 'User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko';AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.110 Safari/537.36';该头域的内容包含发出请求的用户信息。 dw 0a0dh ; db "Content-Type: application/json" ; dw 0a0dh db 'Accept-Encoding: gzip, deflate' ;表示客户端支持gzip ; db 'Accept-Encoding: deflate' dw 0a0dh db 'Accept-Language: zh-CN' dw 0a0dh db 'Connection: Keep-Alive' dw 0a0dh dw 0a0dh https_for_baidu_voice_token_size EQU $ -https_for_baidu_voice_token format_https_for_baidu_text2voice db "POST /text2audio HTTP/1.1" dw 0a0dh db 'Host: tsn.baidu.com' dw 0a0dh db 'User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN; rv:1.9.2.3) Gecko/20100401 firefox/3.6.3' dw 0a0dh db "Content-Type: audio/pcm; rate=16000" ; db "Content-Type: application/x-www-form-urlencoded" dw 0a0dh db 'Accept-Encoding: gzip' ;表示客户端支持gzip ; db 'Accept-Encoding: deflate' ;表示客户端支持gzip dw 0a0dh ; db "Transfer-Encoding: chunked" ; dw 0a0dh ; db "Vary: Accept-Encoding" ; dw 0a0dh ; db "cache-control: no-cache" ; dw 0a0dh db "Content-Encoding: gzip";指文档的编码(Encode)方法。 dw 0a0dh db 'Connection: Keep-Alive' dw 0a0dh ; db 'Accept: */*' ; dw 0a0dh ; db 'Connection:close' db 'content-Length: %d' dw 0a0dh dw 0a0dh db 0 format_https_for_baidu_text2voice_body db "per=106&aue=4&lan=zh&ctp=1&cuid=123456789asdfgh&tok=%s&tex=%s",0 ;https://ai.baidu.com/ai-doc/SPEECH/Qk38y8lrl ;per(基础音库)度小宇=1,度小美=0,度逍遥(基础)=3,度丫丫=4度逍遥(精品)=5003,度小鹿=5118,度博文=106,度小童=110,度小萌=111,度米朵=103,度小娇=5 ;aue 3为mp3格式(默认); 4为pcm-16k;5为pcm-8k;6为wav(内容同pcm-16k); 注意aue=4或者6是语音识别要求的格式,但是音频内容不是语音识别要求的自然人发音,所以识别效果会受影响。 ;vol 音量,取值0-15,默认为5中音量(取值为0时为音量最小值,并非为无声) ;pit 音调,取值0-15,默认为5中语调 ;spd 语速,取值0-15,默认为5中语速 ;lan 固定值zh。语言选择,目前只有中英文混合模式,填写固定值zh ;cpt 客户端类型选择,web端填写固定值1 ;cuid 用户唯一标识,用来计算UV值。建议填写能区分用户的机器 MAC 地址或 IMEI 码,长度为60字符以内 ;tok 开放平台获取到的开发者access_token(见上面的“鉴权认证机制”段落) ;tex 合成的文本,使用UTF-8编码。不超过60个汉字或者字母数字。文本在百度服务器内转换为GBK后,长度必须小于120字节。如需合成更长文本,推荐使用长文本在线合成 ;

,