语音合成可将文字信息转化为声音信息,适用于手机APP、儿童故事机、智能机器人等多种应用场景。本汇编代码是调用百度AI开放平台提供的在线语音合成API实现的。首先需要到百度AI开放平台申请账号,并开通需要的功能。
语音合成可将文字信息转化为声音信息,适用于手机APP、儿童故事机、智能机器人等多种应用场景。本汇编代码是调用 百度AI开放平台提供的在线语音合成 API 实现的。
首先需要到百度AI开放平台 申请账号,并开通需要的功能。
f需要API Key 和Secret Key
然后用API Key 和Secret Key 向授权服务地址 获取 Access Token ,之后 用Access Token和文本信息向短文本在线合成网站“发送信息以获取对应的语音信息。本代码使用了64位汇编语言之HTTPS连接技术,以及网页gzip、deflate 解压缩技术。
option casemap:noneOPTION DOTNAMEinclude baidu_text2voice.inc.codeasciitoint_decimal procuses rbx rsi rdi r10 r11 _pbuffer:qword,_buffer_length:qwordLOCAL @data_num:qwordmov r8,_buffer_lengthand @data_num,0mov rbx,0mov rsi,_pbuffer.while sdword ptrr8d } 0mov 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.elsemov rax,-1ret.endifmov rcx,0mov r9,0mov r9b,almov rax,@data_nummov rcx,10mul rcxmov @data_num,raxadd @data_num,r9inc rbxdec r8d.endwmov rax,@data_numretasciitoint_decimal endpASCII2HEX proc uses rbx rsi rdi pret_buffer:qword,size_ret_buffer:qword,pascii_buffer:qword,size_ascii_buffer:qwordmov rcx,size_ascii_buffermov rsi,pascii_buffermov rdx,size_ret_buffermov rdi,pret_buffermov rbx,0mov r8,0.while sqword ptr rcx } 0 && sqword ptr rdx } 0and rax,0mov 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.elsemov rax,-1ret.endifshl rbx,4or bl,al dec rcxdec rdxinc rsiinc r8mov [rdi],rbx.if r8 == 16mov rbx,0mov r8,0add rdi,8.else.endif.endwretASCII2HEX endpHostnameToIPproc_lpszHostName:qwordlocal@szBuffer[256]:bytelocal@dwIP:qwordinvokeinet_addr,_lpszHostName.if eax {} INADDR_NONE;nozero?;********************************************************************; 输入的是IP地址;********************************************************************mov@dwIP,rax.else;********************************************************************; 输入的是主机名称;********************************************************************invokegethostbyname,_lpszHostName.if sqword ptr rax } 0;greater?movrax,[rax hostent.h_list].whilesqword ptr [rax]{}0;nozero?movrbx,[rax]mov ecx, dword ptr [rbx]mov dword ptr @dwIP,ecxaddrax,8.break.endw.elsexoreax,eaxret.endif.endifmovrax,@dwIPretHostnameToIPendp_recv proc _hSOCKET:qword,_ipbuffer:qword,_size:qword,_flags:qwordLOCAL @sizecount:qwordLOCAL @ipbuffer:qword mov rax,_ipbuffermov @ipbuffer,raxmov rax,_sizemov @sizecount,raxzzzz:@@:.whilesqword ptr @sizecount } 0invoke recv ,_hSocket,@ipbuffer,@sizecount,_flags; 包含flags标志位的结构数据发送服务器若无错误发生,recv()返回读入的字节数。如果连接已中止,返回0。否则的话,返回SOCKET_ERROR错误,应用程序可通过WSAGetLastError()获取相应错误代码。; 在阻塞模式下recv,recvfrom操作将会阻塞到缓冲区里有至少一个字节(TCP)或者一个完整的UDP数据报才返回。;在没有数据到来时,对它们的调用都将处于睡眠状态,不会返回。.if eax ==SOCKET_ERROR;>>,zero?mov eax,0ret.elseif eax == 0 ;如果连接已中止,返回0ret.else;若无错误发生,recv()返回读入的字节数add @ipbuffer,raxsub @sizecount,rax.endif.endwmov eax,1ret_recv endp_send proc _hSocket:qword,_ipbuffer:qword,_size:qword,_flags:qwordLOCAL @sizecount:qwordLOCAL @ipbuffer:qword LOCAL @timecount:qwordmov @timecount,0mov rax,_ipbuffermov @ipbuffer,raxmov rax,_sizemov @sizecount,rax@@:.whilesqword ptr @sizecount } 0invoke send ,_hSocket,@ipbuffer,@sizecount,_flags; 包含flags标志位的结构数据发送服务器若无错误发生,send()返回所发送数据的总数.if eax ==SOCKET_ERROR;>>,zero?invokeWSAGetLastError.if eax == WSAEWOULDBLOCK;>>,zero?invoke Sleep,64h ;延时重发.if @timecount } 30;,greater?mov eax,0ret.else inc @timecount.endif.continue.elsemov eax,0ret.endif.elseif eax == 0;,zero?ret.elseadd @ipbuffer,raxsub @sizecount,raxmov @timecount,0.endif.endwmov eax,1ret_send endpwait_and_recv_data_with_https procuses 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:qwordLOCAL @ptemp_ip_buffer:qwordLOCAL @return_count:qwordLOCAL @temp_buffer_for_list [5]:_SecBufferLOCAL @EncryptMessage_SecBufferDesc_buffer :_SecBufferDescLOCAL@heap_mid_buffer:qwordLOCAL @flags:qwordLOCAL @deccount:qwordmov rcx,_hSocketmov @hSocket,rcxinvoke GetProcessHeapinvoke HeapAlloc,rax,HEAP_ZERO_MEMORY,heap_recv__size.if rax == 0;,zero?mov rax,0ret.endifmov @heap_mid_buffer,raxand @deccount,0mov rdi,_precv_buffermov @ptemp_ip_buffer,rdiand @readcount,0and @retry_count,0and @flags,0.while TRUE;第一次接收,给服务端3秒的等待时间and @readcount,0invoke ioctlsocket,@hSocket,FIONREAD,addr @readcount ;阻塞模式 在一次recv()中所接收的所有数据量。; 这通常与套接口中排队的数据总量相同。; 如果S是SOCK_DGRAM 型,则FIONREAD; 返回套接口上排队的第一个数据报大小。;正常返回0.if rax ==0.if@readcount ==0;客户端connect 之后 如果没有发送数据 就会 @readcount== 0 invoke Sleep,100inc @retry_count.ifsqword ptr @retry_count { 10;给服务端3秒的等待时间;如果三次都读不到数据,说明数据已经读完了,或者服务端已经关闭了.continue.elsemov rax,0jmp @exit.endif.else;int 3mov rcx,@ptemp_ip_buffer;lea rdi,EncryptMessage_recv_buffermov rdi,_precv_buffersub rcx,rdi;缓冲区已经占用的空间.if sqword ptr rcx }= _precv_buffer_length;>>,noless?mov rcx,0mov @ptemp_ip_buffer,rdi.endifmov rax,_precv_buffer_lengthsub rax,rcx;剩余可用的空间.if sqword ptr@readcount } rax;单次接收的数据不能超过缓冲区剩余空间容量mov @readcount,rax.endifmov rdi,@ptemp_ip_buffer;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;invoke _recv,@hSocket,rdi,@readcount,0;.ifrax ==0;,zero?mov rax,0jmp @exit.endifmov rcx,@readcountadd@ptemp_ip_buffer,rcxand @readcount,0and @retry_count,0.endif.elsemov rax,0jmp @exit.endif@again_:mov @EncryptMessage_SecBufferDesc_buffer.ulVersion,SECBUFFER_VERSION;== 0mov @EncryptMessage_SecBufferDesc_buffer.cBuffers,4lea rax,@temp_buffer_for_listmov @EncryptMessage_SecBufferDesc_buffer.pBuffers,raxmov rcx,@ptemp_ip_buffer;lea rdi,EncryptMessage_recv_buffermov rdi,_precv_buffersub rcx,rdimov [rax _SecBuffer.cbBuffer],ecxmov [rax _SecBuffer.BufferType],SECBUFFER_DATA ;==1;lea rsi,EncryptMessage_recv_buffermov rsi,_precv_buffermov [rax _SecBuffer.pvBuffer],rsiadd rsi,rcxadd rax,sizeof _SecBuffermov [rax _SecBuffer.cbBuffer],0mov [rax _SecBuffer.BufferType],0mov [rax _SecBuffer.pvBuffer],0add rsi,rcxadd rax,sizeof _SecBuffermov [rax _SecBuffer.cbBuffer],0mov [rax _SecBuffer.BufferType],0mov [rax _SecBuffer.pvBuffer],0add rax,sizeof _SecBuffermov [rax _SecBuffer.cbBuffer],0mov [rax _SecBuffer.BufferType],0mov [rax _SecBuffer.pvBuffer],0invoke 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.elseifeax == SEC_E_OUT_OF_SEQUENCE;>>,zero? ;该消息没有按正确的顺序接收。lea rax,@temp_buffer_for_listnop.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,1lea rax,@temp_buffer_for_listmov rdi,@heap_mid_buffermov r8,4add 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,rcxrep movsb.endifadd rax,sizeof _SecBufferdec r8.endwmov rdi,_precv_buffermov @ptemp_ip_buffer,rdilea rax,@temp_buffer_for_listmov r8,4;lea rdi,EncryptMessage_recv_buffermov rdi,_precv_buffer.whilesqword 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 movsbmov @EncryptMessage_SecBufferDesc_buffer.ulVersion,SECBUFFER_VERSION;== 0mov @EncryptMessage_SecBufferDesc_buffer.cBuffers,4lea rax,@temp_buffer_for_listmov @EncryptMessage_SecBufferDesc_buffer.pBuffers,raxmov @ptemp_ip_buffer,rdi.endifadd rax,sizeof _SecBufferdec r8.endw;;;;;;;;;是否还有未解密的数据;int 3mov rdi,_precv_buffer.if rdi {}@ptemp_ip_bufferjmp @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数据,那么就会死循环.ifsqword ptr @retry_count }10;我们的处理就是忽略这个请求!也就是说已经连续10次没有读到数据了,那就别指望服务端会提供数据了;invoke logout,addr atext (_error_server,"服务端一直返回0数据,虽然连接没有断开,我们主动断开吧!"),sizeof _error_serverint 3and @flags,0jmp @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 .elsenopnopnopnopnopand @flags,0.break.endif.endw@exit:mov rsi,@heap_mid_buffermov rcx,@deccountmov rax,_return_lehgthmov [rax],rcxmov rdi,_precv_buffercld rep movsbinvoke GetProcessHeapinvoke HeapFree,rax,0,@heap_mid_buffermov rax,@flagsretwait_and_recv_data_with_https endpconnect_SecurityContextproc uses rbx rsi rdi _hSocket:qword,_pTargetName:qword,_pCredHandle:qword,_pCtxtHandle:qword,_ptimstamp:qwordLOCAL @hSocket:qwordLOCAL @readcount:qwordLOCAL @retry_count:qwordLOCAL @ptemp_ip_buffer:qword;缓冲区指针LOCAL @ppChainContext :qwordLOCAL @pCertContext:qwordLOCAL @pbcomputedhsah [20h]:byteLOCAL @pcbcomputedhash:qwordLOCAL @psz[100h]:byteLOCAL @count_input:qwordLOCAL@temp_ip_buffer_input:qwordLOCAL @SecPkgContext_StreamSizes_buffer :SecPkgContext_StreamSizesLOCAL @SCHANNEL_CRED_buffer :_SCHANNEL_CREDLOCAL @flags:qwordLOCAL @pfContextAttr :qword;指向变量的指针,用于接收一组指示已建立上下文属性的位标志: flages 设置情况的反馈LOCAL @output_SecBufferDesc_buffer:_SecBufferDescLOCAL @output_SecBuffer_buffer:_SecBufferLOCAL @input_SecBufferDesc_buffer:_SecBufferDescLOCAL @input_SecBuffer_buffer:_SecBufferLOCAL @temp_buffer_for_list[5]:_SecBuffer; 5 dup (<?>LOCAL @temp_buffer_for_list_input[5]:_SecBuffer;_SecBuffer 5 dup (<?>)sub rsp,100hand @flags,0mov @hSocket,rcxinvoke RtlZeroMemory,addr @SCHANNEL_CRED_buffer,sizeof @SCHANNEL_CRED_buffermov @SCHANNEL_CRED_buffer.dwVersion,SCHANNEL_CRED_VERSIONSP_PROT_SSL3_CLIENT equ 20hSP_PROT_SSL2_CLIENT equ8mov @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 == 40invoke AcquireCredentialsHandleA,0,addr sspi,SECPKG_CRED_OUTBOUND,0,addr@SCHANNEL_CRED_buffer,0,0,_pCredHandle,_ptimstamp.ifrax {} SEC_E_OK;>>,nozero?jmp exit.endifmov @output_SecBufferDesc_buffer.ulVersion,SECBUFFER_VERSION;== 0mov @output_SecBufferDesc_buffer.cBuffers,1;缓冲区个数lea rax,@output_SecBuffer_buffermov @output_SecBufferDesc_buffer.pBuffers,raxmov @output_SecBuffer_buffer.cbBuffer,0;sizeof output_pOutput_buffer;函数返回后,这里会有接收的数据大小mov @output_SecBuffer_buffer.BufferType,SECBUFFER_EMPTY;SECBUFFER_TOKEN;== 2;lea rax,@output_pOutput_buffermov @output_SecBuffer_buffer.pvBuffer,0;raxflages equISC_REQ_SEQUENCE_DETECTISC_REQ_REPLAY_DETECTISC_REQ_CONFIDENTIALITYISC_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.ifrax {} 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_buffermov r8d,@output_SecBuffer_buffer.cbBuffermov rax,@output_SecBuffer_buffer.pvBuffernopinvoke _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.endifjmp exit.endifinvoke FreeContextBuffer,@output_SecBuffer_buffer.pvBufferand @readcount,0and @retry_count,0lea rdi,recv_token_buffer;这个缓冲接收服务器返回的令牌mov @ptemp_ip_buffer,rdi.while TRUEagain_: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,100inc @retry_count.if sqword ptr@retry_count {=30;>>,less?||equal?;.continue;jmp again_.endif.elsemov rdi,@ptemp_ip_buffermov rax,@readcountinvoke _recv,@hSocket,rdi,@readcount,0.if rax == 0;,zero?jmp exit.endifmov rdi,@ptemp_ip_bufferadd rdi,@readcountmov @ptemp_ip_buffer,rdiand @retry_count,0and @readcount,0.endif.elsemov rax,0jmp exit.endifmov @input_SecBufferDesc_buffer.ulVersion,SECBUFFER_VERSION;== 0mov @input_SecBufferDesc_buffer.cBuffers,2;缓冲区个数lea rax,@temp_buffer_for_listmov @input_SecBufferDesc_buffer.pBuffers,raxlea rdi,recv_token_buffermov rcx,@ptemp_ip_buffersub rcx,rdimov [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_buffermov [rax _SecBuffer.pvBuffer],rsiadd rax,sizeof _SecBuffermov [rax _SecBuffer.cbBuffer],0;SECBUFFER_EMPTY0mov [rax _SecBuffer.BufferType],0mov [rax _SecBuffer.pvBuffer],0mov @output_SecBufferDesc_buffer.ulVersion,SECBUFFER_VERSION;== 0mov @output_SecBufferDesc_buffer.cBuffers,3;缓冲区个数lea rax,@temp_buffer_for_list_inputmov @output_SecBufferDesc_buffer.pBuffers,raxmov [rax _SecBuffer.cbBuffer],0;SECBUFFER_EMPTY0mov [rax _SecBuffer.BufferType],0mov [rax _SecBuffer.pvBuffer],0add rax,sizeof _SecBuffermov [rax _SecBuffer.cbBuffer],0;SECBUFFER_EMPTY0mov [rax _SecBuffer.BufferType],0mov [rax _SecBuffer.pvBuffer],0add rax,sizeof _SecBuffermov [rax _SecBuffer.cbBuffer],0;SECBUFFER_EMPTY0mov [rax _SecBuffer.BufferType],0mov [rax _SecBuffer.pvBuffer],0add rax,sizeof _SecBuffermov [rax _SecBuffer.cbBuffer],0;SECBUFFER_EMPTY0mov [rax _SecBuffer.BufferType],0mov [rax _SecBuffer.pvBuffer],0add rax,sizeof _SecBuffermov [rax _SecBuffer.cbBuffer],0;SECBUFFER_EMPTY0mov [rax _SecBuffer.BufferType],0mov [rax _SecBuffer.pvBuffer],0invoke 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--schannellea rax,@pfContextAttrmov rax,_pCtxtHandlelea rsi,@temp_buffer_for_list_inputnopmov @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]nopinvoke _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.endifadd rsi,sizeof _SecBufferdec @count_input.endwmov @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成员包含一个值,;该值指示在该函数成功之前该函数必须从客户端读取的附加字节数。虽然这个数字并不总是准确的,但是使用它可以避免多次调用这个函数,从而帮助提高性能。;个人理解::仅仅为了避免多次调用这个函数.ifsqword 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_inputmov @count_input,3.whilesqword 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]nopinvoke _send,@hSocket, rdx,r8,0.if rax == 0 ;>>,zero?.break.endif.endif.endif.endifadd rsi,sizeof _SecBufferdec @count_input.endwlea rax,@temp_buffer_for_list_inputadd rax,sizeof _SecBuffermov 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 3add rsi,rcxlea rdi,recv_token_bufferrep movsband @readcount,0and @retry_count,0mov @ptemp_ip_buffer,rdi.elseand @readcount,0and @retry_count,0lea rdi,recv_token_buffermov @ptemp_ip_buffer,rdi.endif.else.break.endifmov @count_input,3lea rax,@temp_buffer_for_list_inputmov @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_inputmov rcx,[rax _SecBuffer.pvBuffer]invoke FreeContextBuffer,rcx.endif.endifadd @temp_ip_buffer_input,sizeof _SecBuffermov rax,@temp_ip_buffer_inputdec @count_input.endw.endwmov @count_input,3lea rax,@temp_buffer_for_list_inputmov @temp_ip_buffer_input,rax.while sqword ptr @count_input } 0;,greater?.if sqword ptr [rax _SecBuffer.cbBuffer] } 0;>>,greater?.ifsqword ptr [rax _SecBuffer.pvBuffer] } 0;>>,greater?mov rax,@temp_ip_buffer_inputmov rcx,[rax _SecBuffer.pvBuffer]invoke FreeContextBuffer,rcx.endif.endifadd @temp_ip_buffer_input,sizeof _SecBuffermov rax,@temp_ip_buffer_inputdec @count_input.endwjmp exitmov rax,1exit:mov rax,@flagsadd rsp,100hretconnect_SecurityContext endpdisconnect_server_for_https proc _hSocket:qword,_pCredHandle:qword,_pCtxtHandle:qwordLOCAL @hheap:qwordinvoke FreeCredentialsHandle,_pCredHandleinvoke DeleteSecurityContext,_pCtxtHandleinvoke shutdown,_hSocket,2;0 不能再读,1不能再写,2 读写都不能。invoke closesocket,_hSocketretdisconnect_server_for_https endpconnect_server_for_https procuses rbx rsi rdi _ipaddress:PVOID,_TargetName:PVOID,_pCredHandle:PVOID,_pCtxtHandle:PVOID,_ptimstamp:PVOIDLOCAL @stWsa:WSADATALOCAL @stSin:sockaddr_inLOCAL @hSocket:qwordLOCAL @hheap:qwordLOCAL @SecPkgContext_StreamSizes_buffer:SecPkgContext_StreamSizesinvokeWSAStartup,0202h,addr @stWsainvokesocket,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,0jmp exit.endifmov@hSocket,raxinvoke HostnameToIP,_ipaddress.ifeax == 0invoke MessageBox,hWnd,addr error_ip,addr error_ip,MB_OKret.endifmov@stSin.sin_addr,eaxmov@stSin.sin_family,AF_INETmov eax,443invokehtons,rax;htonl()表示将32位的主机字节顺序转化为32位的网络字节顺序 htons()表示将16位的主机字节顺序转化为16位的网络字节顺序(ip地址是32位的端口号是16位的 )mov@stSin.sin_port,ax@@:invokeconnect,@hSocket,addr @stSin,sizeof @stSin.if eax== SOCKET_ERRORinvokeWSAGetLastError.if eax{} WSAEWOULDBLOCK;nozero?invoke Sleep,1000;invokelogout,addratext (_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,0jmp exit.endif.endifinvoke connect_SecurityContext,@hSocket,_TargetName,_pCredHandle,_pCtxtHandle,_ptimstamp.if rax == 0;zero?;invoke logout,addr atext(error_SecurityContext,"connect_SecurityContext调用失败"),sizeof error_SecurityContextjmp exit.endifinvoke QueryContextAttributesA,_pCtxtHandle,SECPKG_ATTR_STREAM_SIZES,addr @SecPkgContext_StreamSizes_buffer.ifrax {} 0;nozero?;invokelogout,addr atext(_QueryContextAttributes,"QueryContextAttributes调用失败"),sizeof _QueryContextAttributesmov rax,0jmp exit.endifmov eax,@SecPkgContext_StreamSizes_buffer.cbMaximumMessage.ifeax == 0;zero?;invokelogout,addr atext(_cbMaximumMessage,"cbMaximumMessage为空值"),sizeof _cbMaximumMessagejmp exit.endifmov eax,@SecPkgContext_StreamSizes_buffer.cBuffers.if eax { 4;>>,above?;invokelogout,addr atext(_StreamSizes_buffer_cBuffers,"没有加解密缓冲区"),sizeof _StreamSizes_buffer_cBuffersmov rax,0jmp exit.endifmov rax,@hSocketretexit:mov rax,0retconnect_server_for_https endpWinMain prochInst:qword,hPrevInst:qword,CmdLine:qword,CmdShow:qwordLOCALwc:WNDCLASSEXLOCALmsg:MSGLOCAL @hInst:qwordLOCAL @hPrevInst:qwordLOCAL @CmdLine:qwordLOCAL @CmdShow:qwordLOCAL icex:INITCOMMONCONTROLSEXinvoke GetModuleHandle,0movhInstance,raxinvoke GetCommandLinemovCommandLine,raxinvoke RtlZeroMemory,addr wc,sizeof wcinvoke InitCommonControls mov icex.dwSize,sizeof INITCOMMONCONTROLSEXmov icex.dwICC,ICC_TAB_CLASSESinvoke InitCommonControlsEx,addr icexmovwc.cbSize,sizeof WNDCLASSEXmovwc.style,CS_HREDRAW or CS_VREDRAWlea rax,WndProcmovwc.lpfnWndProc,rax;offset WndProcmovwc.cbClsExtra,NULLmovwc.cbWndExtra,DLGWINDOWEXTRApushhInstancepopwc.hInstancemovwc.hbrBackground,COLOR_BTNFACE 1movwc.lpszMenuName,IDM_MENU;0lea rax, ClassNamemovwc.lpszClassName,rax;offset ClassNameinvoke LoadIcon,NULL,IDI_APPLICATIONmovwc.hIcon,raxmovwc.hIconSm,raxinvoke LoadCursor,0,IDC_ARROWmovwc.hCursor,raxinvoke RegisterClassEx,addr wcinvoke CreateDialogParam,hInstance,IDD_DIALOG,NULL,addr WndProc,NULLinvoke ShowWindow,hWnd,SW_SHOWNORMALinvoke UpdateWindow,hWnd.while TRUEinvoke GetMessage,addr msg,0,0,0 .if eax == 0.break.endifinvoke TranslateMessage,addr msginvoke DispatchMessage,addr msg.endwinvoke ExitProcess,0mov rax,msg.wParamretWinMain endpEncryptMessage_and_sendproc uses rbx rsi rdi _hSocket:qword,_pCtxtHandle:qword,_pget_or_post_buffer:qword,_pget_or_post_buffer_length:qwordLOCAL @hSocket:qwordLOCAL @buffer_for_https_count:qwordLOCAL @pget_or_post_buffer:qwordLOCAL @_pget_or_post_buffer_length:qwordLOCAL @thistimelength:qword;本次参与运算的长度LOCAL @hheap:qwordLOCAL @SecPkgContext_StreamSizes_buffer :SecPkgContext_StreamSizesLOCAL @temp_buffer_for_list[5]:_SecBuffer; 5 dup (<?>LOCAL @EncryptMessage_SecBufferDesc_buffer:_SecBufferDescLOCAL @pheap_send_buffer:qwordLOCAL @heap_send_buffer_size:qwordmov @hSocket,rcxmov @pget_or_post_buffer,r8mov @_pget_or_post_buffer_length,r9mov @buffer_for_https_count,r9invoke HeapAlloc,rbx,HEAP_ZERO_MEMORY,heap_send__size.if rax {} 0mov @pheap_send_buffer,rax.elseret.endifmov @heap_send_buffer_size,heap_send__size.while sqword ptr @buffer_for_https_count }0;>>,greater?invoke RtlZeroMemory,@pheap_send_buffer,@heap_send_buffer_sizeinvoke QueryContextAttributesA,_pCtxtHandle,SECPKG_ATTR_STREAM_SIZES,addr @SecPkgContext_StreamSizes_buffer.if rax {} 0;>>,nozero?jmp exit.endifmov rcx,@buffer_for_https_countmov ebx,@SecPkgContext_StreamSizes_buffer.cbMaximumMessage;最大单次可以处理的内存空间(数据大小).if sqword ptr rcx } rbx;>>,GREATER?;谁的空间小就用谁mov @thistimelength,rbxsub @buffer_for_https_count,rbx.elsemov @thistimelength,rcxmov @buffer_for_https_count,0.endifmov @EncryptMessage_SecBufferDesc_buffer.ulVersion,SECBUFFER_VERSION;== 0mov @EncryptMessage_SecBufferDesc_buffer.cBuffers,4lea rax,@temp_buffer_for_listmov @EncryptMessage_SecBufferDesc_buffer.pBuffers,raxmov ecx,@SecPkgContext_StreamSizes_buffer.cbHeadermov [rax _SecBuffer.cbBuffer],ecxmov [rax _SecBuffer.BufferType],SECBUFFER_STREAM_HEADER ;==7mov rsi,@pheap_send_buffermov [rax _SecBuffer.pvBuffer],rsiadd rsi,rcxadd rax,sizeof _SecBuffermov rcx,@thistimelengthmov [rax _SecBuffer.cbBuffer],ecxmov [rax _SecBuffer.BufferType],SECBUFFER_DATA ;==1mov [rax _SecBuffer.pvBuffer],rsimov rdi,rsimov rsi,_pget_or_post_buffermov rcx,@thistimelengthshr rcx,3;/8cldrep movsqmov rcx,@thistimelengthand rcx,111brep movsbadd rax,sizeof _SecBuffermov ecx,@SecPkgContext_StreamSizes_buffer.cbTrailermov [rax _SecBuffer.cbBuffer],ecxmov [rax _SecBuffer.BufferType],SECBUFFER_STREAM_TRAILER ;==6mov [rax _SecBuffer.pvBuffer],rdiadd rax,sizeof _SecBuffermov [rax _SecBuffer.cbBuffer],0mov [rax _SecBuffer.BufferType],0mov [rax _SecBuffer.pvBuffer],0;These buffers must be supplied in the order shown.;Buffer typeDescription;SECBUFFER_STREAM_HEADERUsed internally. No initialization required.;SECBUFFER_DATAContains the plaintext message to be encrypted.;SECBUFFER_STREAM_TRAILERUsed internally. No initialization required.;SECBUFFER_EMPTYUsed internally. No initialization required. Size can be zero.;https://docs.microsoft.com/en-us/windows/win32/secauthn/encryptmessage--schannelmov ecx,@SecPkgContext_StreamSizes_buffer.cbHeadermov @EncryptMessage_SecBufferDesc_buffer.ulVersion,SECBUFFER_VERSION;== 0mov @EncryptMessage_SecBufferDesc_buffer.cBuffers,4lea rax,@temp_buffer_for_listmov @EncryptMessage_SecBufferDesc_buffer.pBuffers,raxinvoke EncryptMessage,_pCtxtHandle,0,addr @EncryptMessage_SecBufferDesc_buffer,0.if rax{} SEC_E_OK;,nozero?jmp exit.endiflea rax,@temp_buffer_for_listmov ecx,@SecPkgContext_StreamSizes_buffer.cbHeadermov edx,[rax _SecBuffer.cbBuffer].ifecx {} edx;>>,nozero?;也就是说加密的数据大小发生了变化,需要调整一下每个类型的位置;int 3nopmov rdi,[rax _SecBuffer.pvBuffer]add rdi,rdxadd rax,sizeof _SecBuffer;第一个尾部mov rsi,[rax _SecBuffer.pvBuffer];第二个mov ecx,[rax _SecBuffer.cbBuffer]cldrep movsbadd rax,sizeof _SecBuffermov rsi,[rax _SecBuffer.pvBuffer];第三个mov ecx,[rax _SecBuffer.cbBuffer]cldrep movsb.endiflea rax,@temp_buffer_for_listmov ecx,[rax _SecBuffer.cbBuffer]add rax,sizeof _SecBufferadd ecx,[rax _SecBuffer.cbBuffer]add rax,sizeof _SecBufferadd ecx,[rax _SecBuffer.cbBuffer]mov rax,rcxinvoke _send,@hSocket, @pheap_send_buffer,rax,0.if eax == 0;,zero?jmp exit.endifmov rcx,@thistimelengthadd @pget_or_post_buffer,rcx.endwinvoke GetProcessHeapinvoke HeapFree,rax,0,@pheap_send_buffermov rax,1exit:ret;返回0 是发送错误,1是正确,其余值是EncryptMessage 错误EncryptMessage_and_send endpuncompress_gzip procuses rbx rsi rdi ungzipbuffer:qword,pungzipbuflength:qword,orgbuffer:qword,orgbuflength:qwordmov z_stream_buffer.zalloc,0mov z_stream_buffer.zfree,0mov z_stream_buffer.opaque,0mov 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 == 0FLAGS_GZIP EQU MAX_WBITS16invoke inflateInit2_,addr z_stream_buffer,FLAGS_GZIP,addr zlib_ver,sizeof z_stream_buffer;解压缩.if eax == Z_OK.whileTRUEinvokeinflate ,addr z_stream_buffer,Z_NO_FLUSH;==0.if eax == Z_STREAM_END.break.elseif eax {} Z_OK;>>,nozero?mov rcx,0mov rdi,pungzipbuflengthmov [rdi],rcxret.endif.endwinvoke inflateEnd,addr z_stream_buffer.if eax == Z_OK;>>,zero?mov ecx,z_stream_buffer.total_out.elsemov rcx,0.endif.endifmov rdi,pungzipbuflengthmov [rdi],rcxretuncompress_gzip endpparsehttp_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:qwordLOCAL @data_size:qwordLOCAL @hex_buffer:qwordLOCAL @current_size:qwordLOCAL @flags_deflate:qwordLOCAL @flags_gzip:qwordLOCAL @uncompress_count:qwordLOCAL @flags_chunked:qwordand @flags_chunked,0and @data_size,0mov rbx,_http_buffer_sizesub rbx,sizeof Transfer_Encoding-1mov rax,0;int 3.while sdword ptr ebx } 0mov rdi,_phttp_bufferlea rdi,[rdi rax]lea rsi,Transfer_Encodingmov rcx,Transfer_Encoding_sizecldrepz cmpsb.if ZERO?;找到chunked块mov rcx,rdisub rcx,_phttp_buffer;先前查找占用的字节长度mov rbx,_http_buffer_sizesub rbx,rcxmov r13,0mov r12,0;chunked块 合并到别一内存的指针.while sdword ptr ebx } 0.if word ptr [rdi r13] == 0a0dh;这里是16进制表示长度的 有多个块and @hex_buffer,0invokeASCII2HEX,addr @hex_buffer,sizeof @hex_buffer,rdi,r13mov rax,@hex_bufferadd @data_size,raxmov @current_size,raxsub rbx,r13 ;减少计数0d0ahsub rbx,2;减少计数0d0ahlea rdi,[rdi r13]add rdi,2;跳过0d0ah;;;;保存数据mov rsi,rdimov rdi,_preturn_bufferlea rdi,[rdi r12]mov rcx,@current_sizeadd r12,rcxcldrep movsb.if word ptr [rsi] {} 0a0dh ;块结构以0d0ah 结束ret.endifadd rsi,2;跳过0d0ahmov rdi,rsior @flags_chunked,1sub rbx,@current_sizesub rbx,2 ;减少计数0d0ahmov r13,0.continue.endifinc r13dec rbx.endw.break.endifinc raxdec rbx.endw.if @flags_chunked == 0;没有找到chunked 类型继续查找mov rbx,_http_buffer_sizesub rbx,sizeof Content_Length-1mov rax,0.while sdword ptr ebx } 0mov rdi,_phttp_bufferlea rdi,[rdi rax]lea rsi,Content_Lengthmov rcx,sizeof Content_Length-1cldrepz cmpsb.if ZERO?mov rcx,rdisub rcx,_phttp_buffer;先前查找占用的字节长度mov rbx,_http_buffer_sizesub rbx,rcxsub rbx,2;0d0a0d0a;剩余可查找的空间字节长度mov rcx,0.while sdword ptr ebx } 0.if word ptr [rdi rcx] == 0a0dh;;这里是10进制表示的invokeasciitoint_decimal,rdi,rcx ;返回rax add @data_size,raxmov rcx,rdisub rcx,_phttp_buffer;先前查找占用的字节长度mov rbx,_http_buffer_sizesub rbx,rcxsub 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_buffermov rcx,@data_sizecldrep movsb.break.endifinc rcx.endw.break.endifinc rcxdec rbx.endw.break.endifinc raxdec rbx.endw.endifand @flags_deflate,0and @flags_gzip,0mov rbx,_http_buffer_sizesub rbx,sizeof Content_Encoding_gzip-1mov rax,0.while sdword ptr ebx } 0mov rsi,_phttp_bufferlea rsi,[rsi rax]lea rdi,Content_Encoding_gzipmov rcx,sizeof Content_Encoding_gzip-1cldrepz cmpsb.if ZERO?;找到or @flags_gzip,1.break.endifinc raxdec ebx.endw;int 3.if @flags_gzip == 0mov rbx,_http_buffer_sizesub rbx,sizeof Content_Encoding_deflate-1mov rax,0.while sdword ptr ebx } 0mov rsi,_phttp_bufferlea rsi,[rsi rax]lea rdi,Content_Encoding_deflatemov rcx,sizeof Content_Encoding_deflate-1cldrepz cmpsb.if ZERO?;找到or @flags_deflate,1.break.endifinc raxdec ebx.endw.endif.if @flags_gzip == 1;用gzip解压mov rax,_preturn_sizemov rax,[rax]mov @uncompress_count,raxinvoke uncompress_gzip,_phttp_buffer,addr @uncompress_count,_preturn_buffer,@data_sizemov rsi,_phttp_buffermov rcx,@uncompress_countmov rdi,_preturn_buffercldrep movsbmov rcx,@uncompress_countmov rdx,_preturn_sizemov [rdx],rcx.elseif @flags_deflate ==1mov rax,_preturn_sizemov rax,[rax]mov @uncompress_count,raxinvoke uncompress,_phttp_buffer,addr @uncompress_count,_preturn_buffer,@data_sizemov rsi,_phttp_buffermov rcx,@uncompress_countmov rdi,_preturn_buffercldrep movsbmov rcx,@uncompress_countmov rdx,_preturn_sizemov [rdx],rcx.elsemov rcx,@data_sizemov rdx,_preturn_sizemov [rdx],rcxmov rax,0.endifretparsehttp_for_chunked endpWndProc proc uses rbx rsi rdi r12 r13 hWin:HWND,uMsg:UINT64,wParam:WPARAM,lParam:LPARAM LOCAL @PpcPackages:qwordLOCAL @ppPackageInfo:qwordLOCAL @pheap_send_buffer:qwordLOCAL @heap_send_buffer_size:qwordLOCAL @pheap_recv_buffer:qwordLOCAL @heap_recv_buffer_size:qwordLOCAL @return_count:qwordLOCAL @data_size:qwordLOCAL @hex_buffer:qwordLOCAL @current_size:qwordLOCAL @uncompress_count:qwordLOCAL @pheap_send_text2voice_with_send:qword;要转换语音的文字缓冲区LOCAL @pheap_send_text2voice_with_EncryptMessage:qword;要发送到服务端的缓冲区数据local @instrbuffer [4]:qwordmovrax,uMsg.if eax==WM_INITDIALOGpushhWinpophWndmov WAVEFORMATEX_out_buffer.wFormatTag,WAVE_FORMAT_PCMmov WAVEFORMATEX_out_buffer.nChannels,1;2mov WAVEFORMATEX_out_buffer.nSamplesPerSec,16000;44100mov WAVEFORMATEX_out_buffer.nAvgBytesPerSec,16*1/8*16000;16*2/8*44100;waveForm.nBlockAlign * waveForm.nSamplesPerSecmov WAVEFORMATEX_out_buffer.nBlockAlign,16*1/8;16*1/8;16*2/8;waveForm.wBitsPerSample * waveForm.nChannels) >> 3;mov WAVEFORMATEX_out_buffer.wBitsPerSample,16mov WAVEFORMATEX_out_buffer.cbSize,0invoke waveOutOpen,addr hwaveout,WAVE_MAPPER,addr WAVEFORMATEX_out_buffer,hWin,0,CALLBACK_WINDOW;CALLBACK_EVENT;,hInstance,CALLBACK_WINDOW;.ifrax {} 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,lParammov rax,lParammov rcx,[rax WAVEHDR.dwUser].if rcx == 1;>>,zero?invoke waveOutUnprepareHeader,hwaveout, addrWAVEHDR_out_buffer,sizeofWAVEHDR_out_buffer;invoke GetProcessHeapinvoke HeapFree,rax,0,WAVEHDR_out_buffer.lpData.endif.elseif eax==WM_COMMANDmovrax,wParamandrax,0FFFFh.if rax==IDM_FILE_EXITinvoke SendMessage,hWin,WM_CLOSE,0,0.elseif rax==IDM_HELP_ABOUTinvoke ShellAbout,hWin,addr AppName,addr AboutMsg,NULL.elseif rax == 1002;连接百度智能云 鉴权认证and @data_size,0invoke GetProcessHeapmov rbx,raxinvoke HeapAlloc,rbx,HEAP_ZERO_MEMORY,heap_send__size.if rax {} 0mov @pheap_send_buffer,rax.elseret.endifmov @heap_send_buffer_size,heap_send__sizeinvoke HeapAlloc,rbx,HEAP_ZERO_MEMORY,heap_recv__size.if rax {} 0mov @pheap_recv_buffer,rax.elseinvoke HeapFree,rbx,0,@pheap_send_bufferret.endifmov @heap_recv_buffer_size,heap_recv__sizeinvoke connect_server_for_https,addr ipbaidu_voice_token,addr TargetName_voiceandtext_token,addr CredHandle,addr CtxtHandle,addr timstamp;.if rax {} 0mov hSocket,raxmov r9d,https_for_baidu_voice_token_sizeinvoke 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 {}0invoke parsehttp_for_chunked,@pheap_send_buffer,@heap_send_buffer_size,@pheap_recv_buffer,@return_count,addr @uncompress_count;int 3mov rbx,@uncompress_countsub rbx,sizeof access_token_token-1mov rax,0.while sdword ptr ebx } 0lea rsi,access_token_tokenmov rdi,@pheap_send_bufferlea rdi,[rdi rax]mov rcx,sizeof access_token_token-1cldrepz cmpsb.if ZERO?;找到mov rcx,rdimov r8,rdisub rcx,@pheap_send_buffer;先前查找占用的字节长度mov rbx,@uncompress_countsub rbx,rcxmov rax,0.while sdword ptr ebx } 0.if byte ptr [rdi rax]== '"';mov rdi,r8andbyte ptr [rdi rax],0invoke SetDlgItemText,hWin,1001,r8.break.endifinc raxdec ebx.endw.break.endifinc raxdec ebx.endw.endif.endif.endifinvoke GetProcessHeapmov rbx,raxinvoke HeapFree,rbx,0,@pheap_send_bufferinvoke HeapFree,rbx,0,@pheap_recv_bufferinvoke disconnect_server_for_https , hSocket,addr CredHandle,addr CtxtHandle.elseif rax == 1003;文字转语音invoke GetProcessHeapmov rbx,raxinvoke HeapAlloc,rbx,HEAP_ZERO_MEMORY,heap_send__size.if rax {} 0mov @pheap_send_text2voice_with_send,rax.elseret.endifmov @heap_send_buffer_size,heap_send__sizeinvoke HeapAlloc,rbx,HEAP_ZERO_MEMORY,heap_recv__size.if rax {} 0mov @pheap_send_text2voice_with_EncryptMessage,rax.elseret.endifmov @heap_recv_buffer_size,heap_recv__size;int 3invoke connect_server_for_https,addr ipbaidu_text2voice,addr TargetName_text2voice,addr CredHandle_text2voice,addr CtxtHandle_text2voice,addr timstamp_text2voice;.if rax == 0ret.endifmov hSocket_text2voice,raxinvoke GetDlgItemText,hWin,1001,addr token_buffer_text2voice,sizeof token_buffer_text2voiceinvoke 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 charactersinvoke WideCharToMultiByte,CP_UTF8,0,@pheap_send_text2voice_with_EncryptMessage,-1,@pheap_send_text2voice_with_send,heap_send__size,0,0;return:The number of bytesmov rdi,@pheap_send_text2voice_with_sendmov dword ptr [rdi rax],0lea rdi,@instrbufferlea rcx, token_buffer_text2voicemov [rdi],rcxmov rax,@pheap_send_text2voice_with_sendmov [rdi 8],raxinvoke wvsprintfA,@pheap_send_text2voice_with_EncryptMessage,addr format_https_for_baidu_text2voice_body,rdiinvoke lstrlenA,@pheap_send_text2voice_with_EncryptMessagelea rdi,@instrbuffermov [rdi],raxinvoke wvsprintfA,@pheap_send_text2voice_with_send,addr format_https_for_baidu_text2voice,rdiinvoke lstrcatA,@pheap_send_text2voice_with_send,@pheap_send_text2voice_with_EncryptMessageinvoke lstrlenA,@pheap_send_text2voice_with_sendmov r9,rax;int 3mov rax,@pheap_send_text2voice_with_sendinvoke EncryptMessage_and_send,hSocket_text2voice,addr CtxtHandle_text2voice, @pheap_send_text2voice_with_send,r9invoke wait_and_recv_data_with_https,hSocket_text2voice,addr CtxtHandle_text2voice,@pheap_send_text2voice_with_EncryptMessage,heap_recv__size,addr @return_count.if rax {}0invoke 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 3invoke GetProcessHeapinvoke HeapAlloc,rax,HEAP_ZERO_MEMORY,@uncompress_count.if rax {} 0mov rsi,@pheap_send_text2voice_with_sendmov rdi,raxmov rbx,raxmov rcx,@uncompress_countcld rep movsbmov WAVEHDR_out_buffer.lpData,rbxmov rax,@uncompress_countmov WAVEHDR_out_buffer.dwBufferLength,eaxmov WAVEHDR_out_buffer.dwBytesRecorded,0mov WAVEHDR_out_buffer.dwUser,1mov WAVEHDR_out_buffer.dwFlags,0mov WAVEHDR_out_buffer.dwLoops,0mov WAVEHDR_out_buffer.lpNext,0mov WAVEHDR_out_buffer.Reserved,0invoke waveOutPrepareHeader,hwaveout, addrWAVEHDR_out_buffer,sizeofWAVEHDR_out_buffer;//准备一个波形数据块头用于录音invoke waveOutWrite,hwaveout, addr WAVEHDR_out_buffer,sizeof WAVEHDR_out_buffer.endif.endif.endifinvoke GetProcessHeapmov rbx,raxinvoke HeapFree,rbx,0,@pheap_send_text2voice_with_sendinvoke HeapFree,rbx,0,@pheap_send_text2voice_with_EncryptMessageinvoke 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_CLOSEinvoke DestroyWindow,hWin.elseif uMsg==WM_DESTROYinvoke PostQuitMessage,NULL.elseinvoke DefWindowProc,hWin,uMsg,wParam,lParamret.endifxorrax,raxretWndProc endpend
include win64.incinclude ksamd64.incinclude Macros\x64macros.incinclude Macros\x64calling.incinclude Macros\vasily.incinclude user32.incinclude kernel32.incinclude shell32.incinclude comctl32.incinclude comdlg32.incinclude secur32.incinclude zlibstat.incincludews2_32.incinclude winmm.inc;waveincludelib user32.libincludelib kernel32.libincludelib shell32.libincludelib comctl32.libincludelib comdlg32.libincludelib Secur32.Libincludelibws2_32.libincludelib winmm.libincludelib zlibstat.libWinMainPROTO :QWORD,:QWORD,:QWORD,:QWORDWndProcPROTO :QWORD,:QWORD,:QWORD,:QWORDIDD_DIALOGequ 1000IDM_MENUequ 10000IDM_FILE_EXITequ 10001IDM_HELP_ABOUTequ 10101atext macro _name:REQ,_text:REQ,other:VARARG.dataalign 8_name db _textfor _ehter:REQ,<other>db _ehter;0d0ahendmdb 0.codeexitm <_name>endmifndef _SecHandle _SecHandle structdwLowerULONG_PTR ?dwUpperULONG_PTR? SecHandle endsendifheap_send__sizeequ 1024*1024*8heap_recv__sizeequ 1024*1024*8.constClassNamedb 'DLGCLASS',0AppNamedb 'Dialog as main',0AboutMsgdb 'MASM64 RadASM Dialog as main',13,10,'Copyright ? masm64 2001',0;sspidb "Microsoft Unified Security Protocol Provider",0;Schannel Security Package 微软提供了10几种安全包类型,这只是其中之一sspidb "Default TLS SSP",0error_ipdb "IP地址错误",0zlib_verdb "1.2.11",0ipbaidu_text2voicedb "tsn.baidu.com",0;tsn.baidu.com汉字转语音TargetName_text2voicedb "tsn.baidu.com",0;ipbaidudb "aip.baidubce.com",0;"39.156.66.110",0;aip.baidubce.com 令牌;TargetNamedb "aip.baidubce.com",0ipbaidu_voice_tokendb "openapi.baidu.com",0;openapi.baidu.com 语音识别令牌;39.156.66.111TargetName_voiceandtext_tokendb "openapi.baidu.com",0.data?hInstancedq ?CommandLinedq ?hWnddq ?hSocketdq ?hSocket_text2voicedq ?token_buffer_text2voicedq 100h dup (?)recv_token_bufferdd 4000h dup (?);这个缓冲接收服务器返回的令牌z_stream_bufferz_stream <?>CERT_CHAIN_ENGINE_CONFIG_bufferCERT_CHAIN_ENGINE_CONFIG <?>CERT_CHAIN_PARA_bufferCERT_CHAIN_PARA <?>CERT_CHAIN_POLICY_PARA_bufferCERT_CHAIN_POLICY_PARA <?>HTTPSPolicyCallbackData_bufferHTTPSPolicyCallbackData <>CERT_CHAIN_POLICY_STATUS_bufferCERT_CHAIN_POLICY_STATUS <?>CredHandle_SecHandle <>CtxtHandle_SecHandle <>timstampFILETIME <>CredHandle_text2voice_SecHandle <>CtxtHandle_text2voice_SecHandle <>timstamp_text2voiceFILETIME <>hwaveoutdq ?;波形输出句柄WAVEFORMATEX_out_bufferWAVEFORMATEX <?>WAVEHDR_out_bufferWAVEHDR <?>ifndef atextatext macro _name:REQ,_text:REQ,other:VARARG.dataalign 8_name db _textfor _ehter:REQ,<other>db _ehter;0d0ahendmdb 0.codeexitm <_name>endmendif.datastatus_codedb "HTTP/1.1 200 OK",0Content_Length db "Content-Length: ",0Content_Encodingdb "Content-Encoding: ",0Transfer_Encodingdb "Transfer-Encoding: chunked"dw 0a0dhdw 0a0dhTransfer_Encoding_sizeEQU $ -Transfer_Encodingchunked_enddb "0"dw 0a0dhdw 0a0dhchunked_end_sizeEQU $ -chunked_endaccess_token_tokendb '"access_token":"',0;{"refresh_token":"25.9891901e2e1e7ac6d676322886ab3a83.314320000.1869313674.282335-15834333","expires_in":2592000,"session_keaccess_token_token_end_flagsdb '"',0;未使用Content_Encoding_gzipdb "Content-Encoding: gzip",0Content_Encoding_deflatedb "Content-Encoding: deflate",0https_for_baidu_voice_tokendb 'POST /oauth/2.0/token?grant_type=client_credentials&client_id=你申请的账号&client_secret=你申请的密码 HTTP/1.1'dw 0a0dhdb 'Host: openapi.baidu.com'dw 0a0dhdb 'Accept: text/html, application/xhtml xml, */*';告诉WEB服务器自己接受什么介质类型,/ 表示任何类型dw 0a0dhdb '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 0a0dhdb 'Accept-Encoding: gzip, deflate';表示客户端支持gzip;db 'Accept-Encoding: deflate'dw 0a0dhdb 'Accept-Language: zh-CN'dw 0a0dhdb 'Connection: Keep-Alive'dw 0a0dhdw 0a0dhhttps_for_baidu_voice_token_size EQU $ -https_for_baidu_voice_tokenformat_https_for_baidu_text2voice db "POST /text2audio HTTP/1.1"dw 0a0dhdb 'Host: tsn.baidu.com'dw 0a0dhdb '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 0a0dhdb "Content-Type: audio/pcm; rate=16000" ;db "Content-Type: application/x-www-form-urlencoded"dw 0a0dhdb 'Accept-Encoding: gzip';表示客户端支持gzip;db 'Accept-Encoding: deflate';表示客户端支持gzipdw 0a0dh;db "Transfer-Encoding: chunked";dw 0a0dh;db "Vary: Accept-Encoding";dw 0a0dh;db "cache-control: no-cache";dw 0a0dhdb "Content-Encoding: gzip";指文档的编码(Encode)方法。 dw 0a0dhdb 'Connection: Keep-Alive'dw 0a0dh;db 'Accept: */*';dw 0a0dh;db 'Connection:close'db 'content-Length: %d'dw 0a0dhdw 0a0dhdb 0format_https_for_baidu_text2voice_bodydb "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字节。如需合成更长文本,推荐使用长文本在线合成;