The 8085 Microprocessor Simulator is a total software solution to replace the microprocessor kit from training and design labs. The Simulator executes the instructions from the user entered program, instruction by instruction showing all the register and flag status at the end of execution of each instruction. Microsoft Visual Basic is probably the fastest and easiest way to create applications for Microsoft Windows. Visual Basic provides you with a complete set of tools to simplify rapid application development. Download from me
The 8085 is a complex IC of sequential circuits. The sequential circuits are designed to do some operation depending on what is the input on their lines. The vital inputs on the lines are what therefore determine what operation will be done by the sequential circuits inside it. The operations can be very complex and therefore this chip is also called a processor. Since we can find a way to put different values of inputs to the input lines of the processor at different times, we can make it execute different operations in a sequence that we desire. Thus, in other words we can make the processor execute a program to do useful things for us. These inputs then could alternately, be called instructions.
The program that we desire to be executed must be loaded into consecutive locations of memory chips. The memory is not part of the 8085 processor. The memory chips are again sequential circuits consisting of flip-flops that are capable of storing digital values. Since we would be interested in storing a huge number of such digital values, a large number of these memories are packed together with a scheme of addresses, so that we can address them individually. Generally the memories a rearranged in large numbers of 8 bit bunches each. The 8085 has a address bus which is 16 bit wide. Therefore it can put 2^16 different digital values on it, and therefore it can address a maximum of 2^16 different address locations. This is called the addressing space and it is 64 kilobyte for the 8085, because 2^16= 65535. And then we ask the processor to execute those instructions from a particular memory location on wards. It goes on executing those instructions one after another. The processor uses certain internal memory locations called Registers in doing all the operations that we ask it to do. The contents of those memory locations can be directly altered by the instructions that we give.
INTEL Corp. is generally recognized as the company that introduced the microprocessor successfully in the market. The first microprocessor, the 4004 was introduced in 1971.It was a central component in the chip set, called MCS-4.The microprocessors introduced between 1971 and 1973 were the first generation systems. They used PMOS technology, which provided low cost, slow speed and low output currents. After 1973-second generation microprocessors such as Motorola 6800 and 6809,Intel 8085 evolved. They were fabricated using NMOS technology. After 1978 The third generation microprocessors were evolved. They were 16 bit wide and included Intel 8086/80186/80286.They were designed using HMOS technology. In 1980 the fourth generation microprocessors evolved. Since 1985,32 bit microprocessors are fabricated using low power version of HMOS technology called HCMOS and they include an on-chip RAM called cache memory to speed up program execution. So extensive research is being carried out for the implementation of more on-chip functions and for the improvement of the speeds of memory and I/O devices. A machine language program consists of either binary or hexadecimal OP(operation)codes. Eight bit microcomputers can be programmed using machine language. A microprocessor has a unique set of machine language instructions defined by the manufacturer. No 2 microprocessors have same machine language instruction set.
Assembly Language
Assembly Language uses semi-English statements for 8 bit microprocessors. Each instruction in an assembly language comprises:(a) Label Field(b) Instruction, Mnemonic or Op-code field(c) Operand field(d) Assembly language basically consists of programs written with the help of mnemonics. Mnemonic is a combination of letters to suggest the operation of an instruction. In general an instruction has 2 components-operation code (OP-code) field and Address field.The OP-code field specifies how data is to be manipulated and the purpose of the address field is to indicate the address of a data item..
High- Level Language
Most 16 and 32 bit microprocessors in addition to assembly and machine language use a more understandable human oriented language called high-level language. High level language programs are composed of English language type statements .a no of high-level language.Regardless of what type of language is used to write a program, the microcomputers understand only binary numbers. So the programs must eventually be translated into their appropriate binary forms. An assembler is one such translator that translates a program written in assembly language to machine language (object code). A compiler/an interpreter converts a high-level language program into a machine language one. A compiler translates the entire source code to object code and then executes it. On the other hand, the interpreter performs line-by-line translation and execution simultaneously like FORTRAN, COBOL, BASIC, C, C++ are widely used these days.
•
DATA TRANSFER GROUPS
These groups include the move, exchange, load, and store operations. Data transfer instructions are among the most widely used of all microprocessor instructions. This group of instructions transfers data to and from registers and memory. None of the instructions of this group are the flag affecting instructions.The instructions included in this group are
For e.g.:MOV (A, B) will move the contents of register B to register A.
•
ARITHEMATIC GROUPS
This group includes the add, add with carry, subtract, subtract with borrow, increment, decrement, and decimal adjust accumulator operations. This group of instructions performs arithmetic operations on data in registers and memory. Unless indicated otherwise all the instructions are flag affecting instructions. All subtraction operations are performed via 2s complement arithmetic and set the carry flag to 1 to indicate a borrow and clear it to indicate no borrow.The instructions included in this group are ADD, ADC, SUB, SBB, DAD, INR, INX, DCR, DCX , and DAA. For e.g.:ADD B will add the contents of register B to the contents of register A and store the result in A.
•
LOGICAL GROUPS
This include AND, OR, XOR, compare, rotate and complement instructions. This group of instructions performs. Logical (Boolean) operations o the data in registers and memory and on flags.The instructions include in the group are
ANA, XRA, ORA, CMP, AI, XRI, ORI,CPI, RLC, RRC, RAR, RAL, CMA, STC and CMC. For e.g.:ANA C will logically AND the contents of register C with the contents of register A and store the result in A.
•
BRANCHING GROUPS
This include jump, call, return and restart instructions. This group of instructions alters the normal sequential program flow. The to types of branch instructions are:-Unconditional-Conditional. Unconditional transfers simply perform the specified operation o the program counter.Conditional transfers examine the status of one of the four MPU flags to determine whether the specified branch is to be executed.The instructions include in the group are Unconditional –
- JNZ, JZ, JNC, JC, JPO, JPE, JP, JM, CNZ, CZ, CNC, CC,CPO, CPE, CP CM, RNZ, RZ, RNC, RC, RPO, RPE, RP, RM. For e.g.:JMP 4000 will transfer the program flow to the memory location 4000.
•
STACK, I/O AND MACHINE CONTROL INSTRUCTIONS
This include push and pop, input and output, exchange, interrupt enables and disables,no operation and halts, and multi – purpose read and set interrupt mask instructions.This group of instructions performs inputs and outputs, manipulates the stack, alters the internal control flags.The instructions included in the group are:Stack operations
–
PUSH, POP, XTHL, and PCHL
Control operations.
–
DI, EI, NOP, HLT, RIM, SIM
Input/output operations
- OUT and IN
For e.g.:PUSH B will push the contents the register B to the stack which is initialized previously.
THE 8085 SIMULATOR
The 8085 Microprocessor Simulator is a total software solution to replace the microprocessor kit from training and design labs. The Simulator executes the instructions from the user entered program, instruction by instruction showing all the register and flag status at the end of execution of each instruction. The input process is fast and efficient, leading to better productivity. The Mnemonic Pad allows the user to enter the code. This makes is impossible to enter a wrong Mnemonic. The Tool tips tell you the syntax along with the operation performed by a particular Mnemonic. The Data Entry Window allows you to enter the Data Segment of your code. This is provided to enable the user to enter the data once and save it along with the code
OptionExplicit ' PublicTypeP_Type XAsDouble' Coordinate x dei punti. YAsDouble' Coordinate y dei punti. 'z As Double ' Coordinate z dei punti. EndType PublicSubBezier_C(Pi()AsP_Type,Pc()AsP_Type) ' ' Ritorna, nel vettore Pc(), i valori della curva di Bezier calcolata ' al valore u (0 <= u <= 1). La curva e' calcolata in modo ' parametrico con il valore 0 di u corrispondente a Pc(0) ' ed il valore 1 corrispondente a Pc(NPC_1). ' Questo algoritmo ricalca la forma classica del polinomio ' di Bernstein. ' DimI&,K&,NPI_1&,NPC_1&,NF&,u#,BF# ' NPI_1=UBound(Pi) NPC_1=UBound(Pc) 'NF = Prodotto(NPI_1) ' ForI=0ToNPC_1 u=CDbl(I)/CDbl(NPC_1) Pc(I).X=0# Pc(I).Y=0# 'Pc(I).z = 0# ForK=0ToNPI_1 'BF = NF * (u ^ K) * ((1 - u) ^ (NPI_1 - K)) _ /(Prodotto(K)*Prodotto(NPI_1-K)) BF=Prodotto(NPI_1,K+1)*(u^K)*((1#-u)^(NPI_1-K))_ /Prodotto(NPI_1-K) Pc(I).X=Pc(I).X+Pi(K).X*BF Pc(I).Y=Pc(I).Y+Pi(K).Y*BF 'Pc(I).z = Pc(I).z + Pi(K).z * BF NextK NextI ' ' ' EndSub PublicSubBezier_1(Pi()AsP_Type,Pc()AsP_Type) ' ' Ritorna, nel vettore Pc(), i valori della curva di Bezier. ' La curva e' calcolata in modo parametrico (0 <= u < 1) ' con il valore 0 di u corrispondente a Pc(0); ' Attenzione: il punto Pc(NPC_1), corrispondente al valore u = 1, ' non puo' essere calcolato. ' ' Parametri: ' Pi(0 to NPI - 1): Vettore dei punti, dati, da ' approssimare. ' Pc(0 to NPC - 1): Vettore dei punti, calcolati, ' della curva approssimante. ' DimI&,K&,NPI_1&,NPC_1& Dimu#,u_1#,ue#,u_1e#,BF# ' NPI_1=UBound(Pi)' N. di punti da approssimare - 1. NPC_1=UBound(Pc)' N. di punti sulla curva - 1. ' ' La curva inizia sempre da Pi(0) -> u = 0: Pc(0).X=Pi(0).X Pc(0).Y=Pi(0).Y 'Pc(0).z = Pi(0).z ' ForI=1ToNPC_1-1 u=CDbl(I)/CDbl(NPC_1) ue=1# u_1=1#-u u_1e=u_1^NPI_1 ' Pc(I).X=0# Pc(I).Y=0# 'Pc(I).z = 0# ForK=0ToNPI_1 BF=Prodotto(NPI_1,K+1)*ue*u_1e/Prodotto(NPI_1-K) Pc(I).X=Pc(I).X+Pi(K).X*BF Pc(I).Y=Pc(I).Y+Pi(K).Y*BF 'Pc(I).z = Pc(I).z + Pi(K).z * BF ' ue=ue*u u_1e=u_1e/u_1 NextK NextI ' ' La curva finisce sempre su Pi(NPI_1) -> u = 1: Pc(NPC_1).X=Pi(NPI_1).X Pc(NPC_1).Y=Pi(NPI_1).Y 'Pc(NPC_1).z = Pi(NPI_1).z ' ' ' EndSub PublicSubBezier(Pi()AsP_Type,Pc()AsP_Type) ' ' Ritorna, nel vettore Pc(), i valori della curva di Bezier. ' La curva e' calcolata in modo parametrico (0 <= u < 1) ' con il valore 0 di u corrispondente a Pc(0); ' ' Questa versione elimina alcuni problemi di "underflow" ' e di "overflow" presentati dalla Bezier_1 e dalla Bezier_C. ' ' Parametri: ' Pi(0 to NPI - 1): Vettore dei punti, dati, da ' approssimare. ' Pc(0 to NPC - 1): Vettore dei punti, calcolati, ' della curva approssimante. ' DimI&,K&,NPI_1&,NPC_1& Dimu#,u_1#,ue#,BF# StaticNPI_1_O&,CB_Tav#() ' NPI_1=UBound(Pi)' N. di punti da approssimare - 1 (deve essere 2 <= NPI_1 <= 1029). NPC_1=UBound(Pc)' N. di punti sulla curva - 1. ' IfNPI_1_O<>NPI_1Then ' Prepara la tavola dei coefficienti binomiali: ReDimCB_Tav#(0ToNPI_1) ForK=0ToNPI_1 CB_Tav(K)=rncr(NPI_1,K) NextK ' NPI_1_O=NPI_1 EndIf ' ' La curva inizia sempre da Pi(0) -> u = 0: Pc(0).X=Pi(0).X Pc(0).Y=Pi(0).Y 'Pc(0).z = Pi(0).z ' ForI=1ToNPC_1-1 u=CDbl(I)/CDbl(NPC_1) ue=1# u_1=1#-u ' Pc(I).X=0# Pc(I).Y=0# 'Pc(I).z = 0# ForK=0ToNPI_1 BF=CB_Tav(K)*ue*u_1^(NPI_1-K) ' Pc(I).X=Pc(I).X+Pi(K).X*BF Pc(I).Y=Pc(I).Y+Pi(K).Y*BF 'Pc(I).z = Pc(I).z + Pi(K).z * BF ' ue=ue*u NextK NextI ' ' La curva finisce sempre su Pi(NPI_1) -> u = 1: Pc(NPC_1).X=Pi(NPI_1).X Pc(NPC_1).Y=Pi(NPI_1).Y 'Pc(NPC_1).z = Pi(NPI_1).z ' ' ' EndSub PublicSubBezier_P(Pi()AsP_Type,Pc()AsP_Type) ' ' Ritorna, nel vettore Pc(), i valori della curva di Bezier calcolata ' al valore u (0 <= u < 1). La curva e' calcolata in modo ' parametrico con il valore 0 di u corrispondente a Pc(0); ' Attenzione: il punto Pc(NPC_1), corrispondente al valore u = 1, ' non puo' essere calcolato. ' ' Questo algoritmo (tratto da una pubblicazione di P. Bourke ' e tradotto dal C) e' particolarmente interessante, in quanto ' evita l' uso dei fattoriali della forma normale. ' DimK&,I&,KN&,NPI_1&,NPC_1&,NN&,NKN& Dimu#,uk#,unk#,Blend# ' NPI_1=UBound(Pi) NPC_1=UBound(Pc) ' ForI=0ToNPC_1-1 u=CDbl(I)/CDbl(NPC_1) uk=1# unk=(1#-u)^NPI_1 ' Pc(I).X=0# Pc(I).Y=0# 'Pc(I).z = 0# ' ForK=0ToNPI_1 NN=NPI_1 KN=K NKN=NPI_1-K Blend=uk*unk uk=uk*u unk=unk/(1#-u) DoWhileNN>=1 Blend=Blend*CDbl(NN) NN=NN-1 IfKN>1Then Blend=Blend/CDbl(KN) KN=KN-1 EndIf IfNKN>1Then Blend=Blend/CDbl(NKN) NKN=NKN-1 EndIf Loop ' Pc(I).X=Pc(I).X+Pi(K).X*Blend Pc(I).Y=Pc(I).Y+Pi(K).Y*Blend 'Pc(I).z = Pc(I).z + Pi(K).z * Blend NextK NextI ' ' La curva finisce sempre su Pi(NPI_1) -> u = 1: Pc(NPC_1).X=Pi(NPI_1).X Pc(NPC_1).Y=Pi(NPI_1).Y 'Pc(NPC_1).z = Pi(NPI_1).z ' ' ' EndSub PrivateFunctionProdotto(ByValN2&,OptionalByValN1&=2)AsDouble ' ' Ritorna il prodotto dei numeri, consecutivi, interi e positivi, ' da N1 a N2 (0 < N1 <= N2). Se N1 > N2 ritorna 1. ' Se N1 manca, ritorna il Fattoriale di N2; in questo caso puo' ' anche essere N2 = 0 perche', per definizione, e' 0! = 1: ' DimPr#,I& ' Pr=1# ForI=N1ToN2 Pr=Pr*CDbl(I) NextI ' Prodotto=Pr ' ' ' EndFunction PublicSubB_Spline(Pi()AsP_Type,ByValNK&,Pc()AsP_Type) ' ' Ritorna, nel vettore Pc(), i valori della curva B-Spline. ' La curva e' calcolata in modo parametrico (0 <= u <= 1) ' con il valore 0 di u corrispondente a Pc(0) ed il valore ' 1 corrispondente a Pc(NPC_1). ' ' Parametri: ' Pi(0 to NPI - 1): Vettore dei punti, dati, da ' approssimare. ' Pc(0 to NPC - 1): Vettore dei punti, calcolati, ' della curva approssimante. ' NK: Numero di nodi della curva ' approssimante: ' NK = 2 -> segmenti di retta. ' NK = 3 -> curve quadratiche. ' .. . .................. ' NK = NPI -> splines di Bezier.
DimNPI_1&,NPC_1&,I&,J&,tmax#,u#,ut#,bn#() ConstEps=0.0000001 ' NPI_1=UBound(Pi)' N. di punti da approssimare - 1. NPC_1=UBound(Pc)' N. di punti sulla curva - 1. tmax=NPI_1-NK+2 ' ' La curva inizia sempre da Pi(0) -> u = 0: Pc(0).X=Pi(0).X Pc(0).Y=Pi(0).Y ' ForI=1ToNPC_1-1 u=CDbl(I)/CDbl(NPC_1) ut=u*tmax IfAbs(ut-CDbl(NPI_1+NK-2))<=EpsThen Pc(I).X=Pi(NPI_1).X Pc(I).Y=Pi(NPI_1).Y Else CallB_Basis(NPI_1,ut,NK,bn()) Pc(I).X=0# Pc(I).Y=0# ForJ=0ToNPI_1 Pc(I).X=Pc(I).X+bn(J)*Pi(J).X Pc(I).Y=Pc(I).Y+bn(J)*Pi(J).Y NextJ EndIf NextI ' ' La curva finisce sempre su Pi(NPI_1) -> u = 1: Pc(NPC_1).X=Pi(NPI_1).X Pc(NPC_1).Y=Pi(NPI_1).Y ' ' ' EndSub PrivateSubB_Basis(ByValNPI_1&,ByValut#,ByValK&,bn#()) ' ' Compute the basis function (also called weight) ' for the B-Spline approximation curve: ' DimNT&,I&,J& Dimb0#,b1#,bl0#,bl1#,bu0#,bu1# ReDimbn#(0ToNPI_1+1),bn0#(0ToNPI_1+1),t#(0ToNPI_1+K+1) ' NT=NPI_1+K+1 ForI=0ToNT If(I<K)Thent(I)=0# If((I>=K)And(I<=NPI_1))Thent(I)=CDbl(I-K+1) If(I>NPI_1)Thent(I)=CDbl(NPI_1-K+2) NextI ForI=0ToNPI_1 bn0(I)=0# If((ut>=t(I))And(ut<t(I+1)))Thenbn0(I)=1# If((t(I)=0#)And(t(I+1)=0#))Thenbn0(I)=0# NextI ' ForJ=2ToK ForI=0ToNPI_1 bu0=(ut-t(I))*bn0(I) bl0=t(I+J-1)-t(I) If(bl0=0#)Then b0=0# Else b0=bu0/bl0 EndIf bu1=(t(I+J)-ut)*bn0(I+1) bl1=t(I+J)-t(I+1) If(bl1=0#)Then b1=0# Else b1=bu1/bl1 EndIf bn(I)=b0+b1 NextI ForI=0ToNPI_1 bn0(I)=bn(I) NextI NextJ ' ' ' EndSub PublicSubC_Spline(Pi()AsP_Type,Pc()AsP_Type) ' ' Ritorna, nel vettore Pc(), i valori della curva C-Spline. ' La curva e' calcolata in modo parametrico (0 <= u <= 1) ' con il valore 0 di u corrispondente a Pc(0) ed il valore ' 1 corrispondente a Pc(NPC_1). ' ' Parametri: ' Pi(0 to NPI - 1): Vettore dei punti, dati, da ' interpolare. ' Pc(0 to NPC - 1): Vettore dei punti, calcolati, ' della curva interpolante. ' DimNPI_1&,NPC_1&,I&,J& Dimu#,ui#,uui# Dimcof()AsP_Type ' NPI_1=UBound(Pi)' N. di punti da interpolare - 1. NPC_1=UBound(Pc)' N. di punti sulla curva - 1. ' CallFind_CCof(Pi(),NPI_1+1,cof()) ' ' La curva inizia sempre da Pi(0) -> u = 0: Pc(0).X=Pi(0).X Pc(0).Y=Pi(0).Y ' ForI=1ToNPC_1-1 u=CDbl(I)/CDbl(NPC_1) J=Int(u*CDbl(NPI_1))+1 If(J>(NPI_1))ThenJ=NPI_1 ' ui=CDbl(J-1)/CDbl(NPI_1) uui=u-ui ' Pc(I).X=cof(4,J).X*uui^3+cof(3,J).X*uui^2+cof(2,J).X*uui+cof(1,J).X Pc(I).Y=cof(4,J).Y*uui^3+cof(3,J).Y*uui^2+cof(2,J).Y*uui+cof(1,J).Y NextI ' ' La curva finisce sempre su Pi(NPI_1) -> u = 1: Pc(NPC_1).X=Pi(NPI_1).X Pc(NPC_1).Y=Pi(NPI_1).Y ' ' ' EndSub PrivateFunctionrncr(ByValN&,ByValK&)AsDouble ' ' Calcola i coefficienti binomiali Cn,k come: ' rncr = N! / (K! * (N - K)!) ' ' Nota: La funzione ha senso solo per 0 < N, K <= N ' e 0 <= K. Nessun errore viene segnalato. ' DimI&,rncr_T# ' If((N<1)Or(K<1)Or(N=K))Then rncr=1# ' Else rncr_T=1# ForI=1ToN-K rncr_T=rncr_T*(1#+CDbl(K)/CDbl(I)) NextI ' rncr=rncr_T EndIf ' ' ' EndFunction PublicSubT_Spline(Pi()AsP_Type,ByValVZ&,Pc()AsP_Type) ' ' Ritorna, nel vettore Pc(), i valori della curva T-Spline. ' La curva e' calcolata in modo parametrico (0 <= u <= 1) ' con il valore 0 di u corrispondente a Pc(0) ed il valore ' 1 corrispondente a Pc(NPC_1). ' ' Parametri: ' Pi(0 to NPI - 1): Vettore dei punti, dati, da ' interpolare. ' Pc(0 to NPC - 1): Vettore dei punti, calcolati, ' della curva interpolante. ' VZ: Valore di tensione della curva ' (1 <= VZ <= 100): valori grandi ' di VZ appiattiscono la curva. ' DimNPI_1&,NPC_1&,I&,J& DimH#,z#,z2i#,szh#,u#,u0#,u1#,du1#,du0# Dims()AsP_Type ' NPI_1=UBound(Pi)' N. di punti da interpolare - 1. NPC_1=UBound(Pc)' N. di punti sulla curva - 1. z=CDbl(VZ) ' CallFind_TCof(Pi(),NPI_1+1,s(),z) ' ' La curva inizia sempre da Pi(0) -> u = 0: Pc(0).X=Pi(0).X Pc(0).Y=Pi(0).Y ' H=1#/CDbl(NPI_1) szh=Sinh(z*H) z2i=1#/z/z ForI=1ToNPC_1-1 u=CDbl(I)/CDbl(NPC_1) J=Int(u*CDbl(NPI_1))+1 If(J>(NPI_1))ThenJ=NPI_1 ' u0=CDbl(J-1)/CDbl(NPI_1) u1=CDbl(J)/CDbl(NPI_1) du1=u1-u du0=u-u0 ' Pc(I).X=s(J).X*z2i*Sinh(z*du1)/szh+(Pi(J-1).X-s(J).X*z2i)*du1/H Pc(I).X=Pc(I).X+s(J+1).X*z2i*Sinh(z*du0)/szh+(Pi(J).X-s(J+1).X*z2i)*du0/H
Pc(I).Y=s(J).Y*z2i*Sinh(z*du1)/szh+(Pi(J-1).Y-s(J).Y*z2i)*du1/H Pc(I).Y=Pc(I).Y+s(J+1).Y*z2i*Sinh(z*du0)/szh+(Pi(J).Y-s(J+1).Y*z2i)*du0/H NextI ' ' La curva finisce sempre su Pi(NPI_1) -> u = 1: Pc(NPC_1).X=Pi(NPI_1).X Pc(NPC_1).Y=Pi(NPI_1).Y ' ' ' EndSub PrivateSubFind_TCof(Pi()AsP_Type,ByValNPI&,s()AsP_Type,ByValz#) ' ' Find the coefficients of the T-Spline ' using constant interval: ' DimI&,H#,a0#,b0#,zh#,z2# ' ReDims(1ToNPI)AsP_Type,f(1ToNPI)AsP_Type ReDima(1ToNPI)AsDouble,B(1ToNPI)AsDouble,C(1ToNPI)AsDouble ' H=1#/CDbl(NPI-1) zh=z*H a0=1#/H-z/Sinh(zh) b0=z*2#*Cosh(zh)/Sinh(zh)-2#/H ForI=1ToNPI-2 a(I)=a0 B(I)=b0 C(I)=a0 NextI ' z2=z*z/H ForI=1ToNPI-2 f(I).X=(Pi(I+1).X-2#*Pi(I).X+Pi(I-1).X)*z2 f(I).Y=(Pi(I+1).Y-2#*Pi(I).Y+Pi(I-1).Y)*z2 NextI ' CallTRIDAG(a(),B(),C(),f(),s(),NPI-2) ForI=1ToNPI-2 s(NPI-I).X=s(NPI-1-I).X s(NPI-I).Y=s(NPI-1-I).Y NextI ' s(1).X=0# s(NPI).X=0# s(1).Y=0# s(NPI).Y=0# ' ' ' EndSub PrivateSubFind_CCof(Pi()AsP_Type,ByValNPI&,cof()AsP_Type) ' ' Find the coefficients of the cubic spline ' using constant interval parameterization: ' DimI&,H# ' ReDims(1ToNPI)AsP_Type,f(1ToNPI)AsP_Type,cof(1To4,1ToNPI)AsP_Type ReDima(1ToNPI)AsDouble,B(1ToNPI)AsDouble,C(1ToNPI)AsDouble ' H=1#/CDbl(NPI-1) ForI=1ToNPI-2 a(I)=1# B(I)=4# C(I)=1# NextI ' ForI=1ToNPI-2 f(I).X=6#*(Pi(I+1).X-2#*Pi(I).X+Pi(I-1).X)/H/H f(I).Y=6#*(Pi(I+1).Y-2#*Pi(I).Y+Pi(I-1).Y)/H/H NextI ' CallTRIDAG(a(),B(),C(),f(),s(),NPI-2) ForI=1ToNPI-2 s(NPI-I).X=s(NPI-1-I).X s(NPI-I).Y=s(NPI-1-I).Y NextI ' s(1).X=0# s(NPI).X=0# s(1).Y=0# s(NPI).Y=0# ForI=1ToNPI-1 cof(4,I).X=(s(I+1).X-s(I).X)/6#/H cof(4,I).Y=(s(I+1).Y-s(I).Y)/6#/H cof(3,I).X=s(I).X/2# cof(3,I).Y=s(I).Y/2# cof(2,I).X=(Pi(I).X-Pi(I-1).X)/H-(2#*s(I).X+s(I+1).X)*H/6# cof(2,I).Y=(Pi(I).Y-Pi(I-1).Y)/H-(2#*s(I).Y+s(I+1).Y)*H/6# cof(1,I).X=Pi(I-1).X cof(1,I).Y=Pi(I-1).Y NextI ' ' ' EndSub PrivateSubTRIDAG(a#(),B#(),C#(),f()AsP_Type,s()AsP_Type,ByValNPI_2&) ' ' Solves the tridiagonal linear system of equations: ' DimJ&,bet# ReDimgam#(1ToNPI_2) ' IfB(1)=0ThenExitSub ' bet=B(1) s(1).X=f(1).X/bet s(1).Y=f(1).Y/bet ForJ=2ToNPI_2 gam(J)=C(J-1)/bet bet=B(J)-a(J)*gam(J) If(bet=0)ThenExitSub s(J).X=(f(J).X-a(J)*s(J-1).X)/bet s(J).Y=(f(J).Y-a(J)*s(J-1).Y)/bet NextJ ' ForJ=NPI_2-1To1Step-1 s(J).X=s(J).X-gam(J+1)*s(J+1).X s(J).Y=s(J).Y-gam(J+1)*s(J+1).Y NextJ ' ' ' EndSub PrivateFunctionCosh(ByValzAsDouble)AsDouble ' ' Ritorna il coseno iperbolico di z#: ' Cosh=(Exp(z)+Exp(-z))/2# ' ' ' EndFunction PrivateFunctionSinh(ByValzAsDouble)AsDouble ' ' Ritorna il seno iperbolico di z#: ' Sinh=(Exp(z)-Exp(-z))/2# ' ' ' EndFunction
You can change the Deafult syntax colors of this control by its properties in design time, or in coding style. Space surrounding each word is significant. It allows searching on whole words. Note that these constant declares are long and could reach the line length limit of 1023 characters. If so, simply split to 2 constants and combine into a third constant with the appropriate name.
As I recover from a gruelling contract where I had to program in 4 different languages .. PIC Basic, C++, Javascript, & you guessed it, VB6 (my choice for post processing), I have come back here to see if there was any good news. Apparently not. Microsoft's stubbornness has not abated.
I saw a video recently that said "if you have an iphone, you can track your cycles at the click of a button. I believe there is an android app for that too; and if you have a Windows phone, I guess you can use it to carve the numbers into a rock". And that about sums up where MS has gone.
In my new environment, I am surrounded by people who use either a Mac or, reluctantly, Win 7. They apologise profusely if I must use W10. (To be fair, W10 is okay if you don't mind having to use Cortana to find your programs, to sign into everything, and can put up with daily changes because you can't turn off automatic updates (except on specific networks which is fine if you don't move). Microsoft is the epitome of success breeds contempt. It is becoming irrelevant. Dot Net itself is poopooed by developers and is fast becoming a second rate dead end.
There are career programmers who don't mind pounding the shift key putting squiggly brackets and parenthesis eveywhere until their code looks like Egyptian hieroglyphs, and then having to decipher gobbledygook errors when they forget the ";" at the end of a line or, god-forbid, use incorrect case. This is a careerist's gravy -- as much as doctors & lawyers invented a new language and writing style to obscure their methods and protect their industry, so it would seem the career programmers hide the simple behind jargon and layers of bloatware, text only IDEs, separate compilers, linkers and GUI builders.
Then there are those who just want to get the job done, to not have to wait a minute for compilation or a day installing a new framework, to use a powerful but simple "app" to produce a scripted equivalent of a spreadsheet. That's what VB6 is (not forgetting that VB6 is indeed the core of the scripting language in Excel). Someone invented VB, perfected VB6, then someone else came along and trashed it because it didn't fit the fashionable hieroglyphs and bloatware mantra. And along with it, our faith in them died. Currently, my W7 still makes a good desktop, but I am looking at Mint as Linux is now better supported by the open source community. For anything else, I use a Linux box or no OS. RIP MS.
Microsoft seemed to be suffering from a strain of collective pyromania, obsoleting stuff for fun, then hiding it behind patents and legalese to ensure no one can replicate what they have done (let's just remind ourselves that open source and Linux exists largely to escape the patent trolls). This is commercial bulimia. Dine well, engorge your coffers, then throw it all up again. Repeat until dead.
We have a clinical term for this disorder where I come from.. It's called pig ignorance.
HTTP Recon is an advanced web server fingerprinting system made in Visual Basic 6.0. We are accustomed to advanced VB6 projects, however, few are open source and most of them are sold for impressive amounts of money. Here we have to thank a security engineer named Marc Ruef. The httprecon project is doing some research in the field of web server fingerprinting, also known as http fingerprinting. The goal is the highly accurate identification of given httpd implementations. This is very important within professional vulnerability analysis.
Besides the discussion of different approaches and the documentation of gathered results also an implementation for automated analysis is provided. This software shall improve the easyness and efficiency of this kind of enumeration. Traditional approaches as like banner-grabbing, status code enumeration and header ordering analysis are used. However, many other analysis techniques were introduced to increase the possibilities of accurate web server fingerprinting. Some of them were already discussed in the book Die Kunst des Penetration Testing (Chapter 9.3, HTTP-Fingerprinting, pp. 530-550).
The application works very straight forward. After the user has defined the target service which shall be fingerprinted, a common tcp connection is opened to the destination port. If the connection could be established, the http requests are sent to the target service. This one will shall react with responses. These could be dissected to identify some specific fingerprint elements. Those elements are looked up in the local fingerprint database. If there is a match, the according implementation is flagged as "identified". All these flags were counted so httprecon is able to determine which implementation has the best match rate.
Architecture
The following picture illustrates the architecture of the whole framework. The scan engine uses nine different requests which are sent to the target web server. These shall provoke the response which can be used for the fingerprinting. There were different kind of requests used. Some of them are very common and legitimate (e.g. GET / HTTP/1.1) and others are usually not accepted due to their malicious nature (e.g. a very long URI in a GET request).
The dissection of the responses is handled by the parsing and fingerprint engine. As you can see many different fingerprint elements are looked up (e.g. statuscode, banner, Etag length, header-order, etc.). These elements are saved in the local fingerprint database which allows the sum of the matches. All data is correlated which will result in the final fingerprint scan report.
Features
These are the main features of the current implementation of httprecon which makes this solution better than other tools of this kind:
Many test-cases: There are nine test-cases possible
HTTPS/SSL support: Secure web servers can be tested too
Advanced result analysis: Different methods for the analysis of results is provided
Many fingerprint details: The analysis is based on many fingerprint elements
Plaintext Database: The fingerprint data is saved in a file-based plaintext database
Fingerprint Wizard: Fingerprints can be saved and updated within the GUI
IDS evasion mechanism: The configuration settings allow to use IDS evasion mechanisms
Reporting: XML, HTML and TXT reporting is provided for professional testers
Autoupdate: An autoupdate feature informs about new releases
Open-source (GPLv3): Everyone can improve the application for themselves
There are differen applications for http fingerprinting available. This Excel sheet is comparing the four most popular HTTP fingerprinting tools (httprecon, httprint, hmap, and WebserverFP).
Key Analysis Index
Most web server implementations come with a Key Analysis Index (KAI), a very special and dominant behaviour which allows a very quick identification. The following list shall demonstrate the KAI for some popular implementations:
Apache: Every generation of Apache web servers usually introduces these three values first in an http response header: Date, Server, and X-Powered-By (optional). The length of the ETag values varies between 17 and 34 bytes and they are usually surrounded by double-quotes. It is very typical for an Apache installation to announce PHP/x.x.x within the X-Powered-By line (it is also common for Abyss). It is also common that an Apache web server reacts with the statuscode 403 (Forbidden) if a very long URI was proposed within the request. Usually the supported http methods are announced as: GET, HEAD, POST, OPTIONS, and TRACE.
Microsoft IIS: The length of the ETag values varies between 18 and 23 bytes. This web server is the only one so far which is announcing ASP.NET within the X-Powered-By line.
Oracle Application Server: The length of the ETag values varies between 15 and 30 bytes and they are usually surrounded by double-quotes. Usually the supported http methods are announced as: OPTIONS, TRACE, GET, and HEAD. In some cases also an additional line similar to Allow is used and defined as Public.
Sun One Web Server: The implemenation by Sun Microsystems Inc. usually starts with the values Server, {Date|Content-type}.
Netscape Enterprise Server: This implementation usually uses these three values within a response header: Server, Date, and Content-type.
Compaq HTTP Server: Old implementations of the generation 5.x always propose HTTP/1.0 instead of HTTP/1.1 as protocol. A very special behaviour is the statustext "Ok" instead of full capitalized "OK" for a successful processing. They also use uncapitalized letters is a response line uses some dash (e.g. Content-type and Content-length). And the response header always consists of: Date, Server, Content-type, Content-length, and Set-Cookie. A Compaq HTTP Server sends the http statuscode 200 (Ok) even a very long URI was proposed within the request (also common for LANCOM DSL router).
Zyxel: The embedded web server of Zyxel devices proposes usually the same http response header structure: Content-Type, Date, Pragma, Expires, Transfer-Encoding, Server, and EXT. Very special in this case is the header line EXT.
4D WebSTAR: Versions prior 4.x always announce the MIME-Version as first element of the http response header. The version 4.x do not use this value anymore and rely on Server as first element. And in the later releases 5.x the Date announcement moved the Server announcement to the second line. A request for a non-existing ressource returns the statuscode "File Not Found" instead of the more common "Not Found".
Roxen: The length of the ETag values is always set to 34 bytes and they are usually surrounded by double-quotes.
OmniHTTPd: Another special behaviour for successful requests is the status text "Document Follows" (similar to TclHttpd) where usually an "OK" is used. The response headers usually contain the values Content-Length, Content-Type, Date and the header is ended by the value Server.
TclHttpd: Another special behaviour for successful requests is the status text "Data follows" (similar to OmniHTTPd) where usually an "OK" is used.
Gatling: A very special behaviour for successful requests is the status text "Coming Up" where usually an "OK" is used.
Squid: Common http get requests always produce the announcement of HTTP/1.0 instead of HTTP/1.1 as protocol.
Counter-measures
The possibility of fingerprinting is not a vulnerability in a traditional way which allows to compromise a host. It is more a flaw or exposure which may provide the foundation for further enumeration and specific attack scenarios.
Nevertheless, applying some counter-measures to harden a service is always a good idea. Preventing fingerprinting 100 % is not possible due to the nature of interaction between network software. But there are possibilities to decrease the accuracy of such an analysis. These are illustrated in the diagram and listed below:
Change or supression of banner
The most accepted and widely known approach to defend against fingerprinting is the manipulation or change of the application banner. Within web responses the line Server announces the name of the given implementation. Some web servers allow the change of this value within a configuration file.
Apache supports downstripping the announcement with the ServerToken directive. Downstripping requires the definition of Prod which would announce "Apache" only (see the ServerSignature directive too). To change this value really some manipulation of the file /src/include/httpd.h within the source-code (AP_SERVER_BASEVENDOR, AP_SERVER_BASEPRODUCT, AP_SERVER_MAJORVERSION_NUMBER, AP_SERVER_MINORVERSION_NUMBER, AP_SERVER_PATCHLEVEL_NUMBER) is required:
* The below defines the base string of the Server: header. Additional * tokens can be added via the ap_add_version_component() API call. * * The tokens are listed in order of their significance for identifying the * application. * * "Product tokens should be short and to the point -- use of them for * advertizing or other non-essential information is explicitly forbidden." * * Example: "Apache/1.1.0 MrWidget/0.1-alpha" */ #define AP_SERVER_BASEVENDOR "Apache Software Foundation" #define AP_SERVER_BASEPROJECT "Apache HTTP Server" #define AP_SERVER_BASEPRODUCT "Apache"
Microsoft IIS requires some hex hack in the library W3SVC.DLL to change the Server-output. There is a freeware named MS IIS/PWS Banner Edit Tool available which automates this manipulation. IISBanner is a well-known ISAPI filter which can be used to safely remove or disguise the IIS server header by editing the INI file. Microsoft suggests the use of URLscan which introduces the same advantages.
thttpd allows some minor changes within the file config.h which steers some of the settings during compilation (e.g. ERR_APPEND_SERVER_INFO for the announcement of the server name within server generated error pages or the default charset of iso-8859-1 in DEFAULT_CHARSET). Furthermore it is possible to change SHOW_SERVER_VERSION which suppresses the version number announcement in the Server line. To change or suppress the real server name a modification of EXPOSED_SERVER_SOFTWARE within libhttpd.c is required:
The open-source web server fnord does not support any configuration settings or constant mutation within the source code to modify the application behavior easily. The announcement of the web server as FNORD is realized within the separated replies created by buffer_puts() in httpd.c. This includes the application banner, status messages and header order. However, enhanced search and replace modifications might improve the obscurity without touching the architecture of the application. Further improvements as like introduction of new http methods (by default only GET, POST and HEAD are suppoted in version 1.10) require some deeper modifications.
Some modules (e.g. PHP and SSH) announce themselves within the Server line. In most cases this can be prevented with a configuration setting for the according module. For PHP in the file php.ini the value expose_php must be set to Off.
Change statuscode and statustext
Web servers include implementation dependent statuscodes and statustexts in their responses. Changeing them prevents most of todays web server fingerprinting. Only a few http daemons allow such change of basic behaviour within run-time configuration.
Apache requires some changes within the source code and re-compilation. In /src/include/httpd.h the statuscodes are defined as integer constants:
* The size of the static array in http_protocol.c for storing * all of the potential response status-lines (a sparse table). * A future version should dynamically generate the apr_table_t at startup. */ #define RESPONSE_CODES 57
And in /src/modules/http/http_protocol.c the statustexts are defined as string constants:
#else static const char * const status_lines[RESPONSE_CODES] = #endif { "100 Continue", "101 Switching Protocols", "102 Processing", #define LEVEL_200 3 "200 OK", "201 Created", "202 Accepted", "203 Non-Authoritative Information", "204 No Content", "205 Reset Content", "206 Partial Content", "207 Multi-Status", #define LEVEL_300 11 "300 Multiple Choices", "301 Moved Permanently", "302 Found", "303 See Other", "304 Not Modified", "305 Use Proxy", "306 unused", "307 Temporary Redirect", #define LEVEL_400 19 "400 Bad Request", "401 Authorization Required", "402 Payment Required", "403 Forbidden", "404 Not Found", "405 Method Not Allowed", "406 Not Acceptable", "407 Proxy Authentication Required", "408 Request Time-out", "409 Conflict", "410 Gone", "411 Length Required", "412 Precondition Failed", "413 Request Entity Too Large", "414 Request-URI Too Large", "415 Unsupported Media Type", "416 Requested Range Not Satisfiable", "417 Expectation Failed", "418 unused", "419 unused", "420 unused", "421 unused", "422 Unprocessable Entity", "423 Locked", "424 Failed Dependency", /* This is a hack, but it is required for ap_index_of_response * to work with 426. */ "425 No code", "426 Upgrade Required", #define LEVEL_500 46 "500 Internal Server Error", "501 Method Not Implemented", "502 Bad Gateway", "503 Service Temporarily Unavailable", "504 Gateway Time-out", "505 HTTP Version Not Supported", "506 Variant Also Negotiates", "507 Insufficient Storage", "508 unused", "509 unused", "510 Not Extended" };
An easier way to change the status reaction for specific request types (e.g. unsupported http methods as like DELETE) is the use of re-write rules. Instead of react with the expected error message 405 a less usefull forwarding to a 404 error site is possible. Most web servers support such definitions within .htaccess files. The following example is redirecting the unwanted requests to 403 Forbidden instead to 405 Method Not Allowed if the Apache web server has mod_rewrite enabled:
RewriteRule .* - [F]
Change header-values and order
Some web server fingerprinting tools regard header values and header order. Changeing this within the web server usually requires some deep impact to the source code too. This requires a very high level of understanding the given application. The rate of errors might be very high with such an intrusive change.
In Microsoft IIS new custom header values can be added which changes the overview fingerprint of the header order. Just by adding one new header line (usually web browsers ignore those which start with X, e.g. X-Garbage) the possibilities of successful fingerprints can be reduced. This is possible very easily in the tab HTTP headers in the web site properties. Those can be reached within the context menu of the according web site in the Internet Information Services (IIS) Manager. In some cases it is possible to overwrite some other header values (e.g. the location in 302 moved messages). However, this is not possible for the Server banner itself.
Add Custom HTTP Header in MS IIS
However, some scripting languages as like PHP allow the web developer to have some influence to the headers with the function header(). For example a new header with the call header("X-Powered-By: ASP.NET 2.0") can be used althought no ASP.NET is used at all. This compromises the fingerprint analysis, especially if it is very static and pattern-based, in any way. In ASP.NET the function Response.AppendHeader() is used for the same purposes.
And in JSP different methods of the response object defined by the javax.servlet.http.HttpServletResponse interface might be used: response.setHeader() to set a header value, response.addHeader() to add a new header value, response.setIntHeader() to set an header with an integer value and response.setDateHeader() to set a header with a date value (e.g. from System.currentTimeMillis()).
The ColdFusion Markup Language (CFML) uses the tag cfheader to define headers and their values. Status codes can be changed with a statement like and new header lines introduced with a statement like .
Redirect known attack scripts
Another way of defending against fingerprinting utilities is to redirect attack scripts as like httprecon. Within the following .htaccess example the well-known user-agents are detected and redirected to the attackers own computer:
This introduces several advantages. First, the attacker is consuming more of his resources which might slow down the scan approach. Second, most of the attack scripts do not recognize the redirect and think the final destination host - which is the attackers own computer - shall be fingerprinted. Thus, in some cases wrong results might be gathered.
However, this blacklist technique only works as long as the attack scripts are detected properly. If the attacker is going to change the approach and behavior of the scanning software, no further redirection might be possible.
Httprecon is a http fingerprinting tool for Windows. The results are based on the analysis of 9 requests (see detailed requests below):
GET existing
GET long request
GET non-existing
GET wrong protocol
HEAD existing
OPTIONS common
DELETE existing
TEST method
Attack Request
Detailed tests
Here is the list of the requests (with default options) sent by the tool:
GET existing
GET / HTTP/1.1 User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.2; en-US; rv:1.8.1.11) Gecko/20071127 Firefox/2.0.0.8 Host: 192.168.100.24 Connection: Keep-Alive Cache-Control: no-cache
GET long request
GET /nnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnn HTTP/1.1 User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.2; en-US; rv:1.8.1.11) Gecko/20071127 Firefox/2.0.0.8 Host: 192.168.100.24 Connection: Keep-Alive Cache-Control: no-cache
GET non-existing
GET /dX6cZ6.html HTTP/1.1 User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.2; en-US; rv:1.8.1.11) Gecko/20071127 Firefox/2.0.0.8 Host: 192.168.100.24 Connection: Keep-Alive Cache-Control: no-cache
GET wrong protocol
GET / HTTP/9.8 User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.2; en-US; rv:1.8.1.11) Gecko/20071127 Firefox/2.0.0.8 Host: 192.168.100.24 Connection: Keep-Alive Cache-Control: no-cache
HEAD existing
HEAD / HTTP/1.1 User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.2; en-US; rv:1.8.1.11) Gecko/20071127 Firefox/2.0.0.8 Host: 192.168.100.24 Content-Length: 0 Connection: Keep-Alive Cache-Control: no-cache
Here we have an application that measures execution times for the three sorting algorithms: Quick Sort, Selection Sort and Bubble Sort. A visual interface shows what these algorithms do in real time. The implementation is made in algorithm in Visual Basic 6.0 and the source code is shown below:
The quicksortalgorithm was developed in 1959 by Tony Hoare while in the Soviet Union, as a visiting student at Moscow State University. At that time, Hoare worked in a project on machine translation for the National Physical Laboratory. As a part of the translation process, he needed to sort the words of Russian sentences prior to looking them up in a Russian-English dictionary that was already sorted in alphabetic order on magnetic tape. After recognizing that his first idea, insertion sort, would be slow, he quickly came up with a new idea that was Quicksort. He wrote a program in Mercury Autocode for the partition but couldn't write the program to account for the list of unsorted segments. On return to England, he was asked to write code for Shellsort as part of his new job. Hoare mentioned to his boss that he knew of a faster algorithm and his boss bet sixpence that he didn't. His boss ultimately accepted that he had lost the bet. Later, Hoare learned about ALGOL and its ability to do recursion that enabled him to publish the code in Communications of the Association for Computing Machinery, the premier computer science journal of the time.
Quicksort gained widespread adoption, appearing, for example, in Unix as the default library sort subroutine. Hence, it lent its name to the C standard library subroutine qsort and in the reference implementation of Java.
Robert Sedgewick's Ph.D. thesis in 1975 is considered a milestone in the study of Quicksort where he resolved many open problems related to the analysis of various pivot selection schemes including Samplesort, adaptive partitioning by Van Emden as well as derivation of expected number of comparisons and swaps. Bentley and McIlroy incorporated various improvements for use in programming libraries, including a technique to deal with equal elements and a pivot scheme known as pseudomedian of nine, where a sample of nine elements is divided into groups of three and then the median of the three medians from three groups is chosen. Jon Bentley described another simpler and compact partitioning scheme in his book Programming Pearls that he attributed to Nico Lomuto. Later Bentley wrote that he used Hoare's version for years but never really understood it but Lomuto's version was simple enough to prove correct. Bentley described Quicksort as the "most beautiful code I had ever written" in the same essay. Lomuto's partition scheme was also popularized by the textbook Introduction to Algorithms although it is inferior to Hoare's scheme because it does three times more swaps on average and degrades to O(n2) runtime when all elements are equal.
In 2009, Vladimir Yaroslavskiy proposed the new dual pivot Quicksort implementation. In the Java core library mailing lists, he initiated a discussion claiming his new algorithm to be superior to the runtime library’s sorting method, which was at that time based on the widely used and carefully tuned variant of classic Quicksort by Bentley and McIlroy. Yaroslavskiy’s Quicksort has been chosen as the new default sorting algorithm in Oracle’s Java 7 runtime library after extensive empirical performance tests.
Selection sort is a sorting algorithm, specifically an in-place comparison sort. It has O(n2) time complexity, making it inefficient on large lists, and generally performs worse than the similar insertion sort. Selection sort is noted for its simplicity, and it has performance advantages over more complicated algorithms in certain situations, particularly where auxiliary memory is limited.
The algorithm divides the input list into two parts: the sublist of items already sorted, which is built up from left to right at the front (left) of the list, and the sublist of items remaining to be sorted that occupy the rest of the list. Initially, the sorted sublist is empty and the unsorted sublist is the entire input list. The algorithm proceeds by finding the smallest (or largest, depending on sorting order) element in the unsorted sublist, exchanging (swapping) it with the leftmost unsorted element (putting it in sorted order), and moving the sublist boundaries one element to the right.
Bubble sort, sometimes referred to as sinking sort, is a simple sorting algorithm that repeatedly steps through the list to be sorted, compares each pair of adjacent items and swaps them if they are in the wrong order. The pass through the list is repeated until no swaps are needed, which indicates that the list is sorted. The algorithm, which is a comparison sort, is named for the way smaller or larger elements "bubble" to the top of the list. Although the algorithm is simple, it is too slow and impractical for most problems even when compared to insertion sort. It can be practical if the input is usually in sorted order but may occasionally have some out-of-order elements nearly in position.
PublicSubQuickSort33(vArrayAsVariant,AccordingToAsInteger,Dimension2SizeAsInteger,LAsInteger,RAsInteger) ' name of array, sorting according to which dimension?, size of second dimension, lbound(), ubound() DimaAsInteger,iAsInteger,jAsInteger' counters DimX,Y,z' temporary values
PublicSubSelectionSort(vArray,LAsInteger,RAsInteger) ' name of array, lbound(), ubound() DimiAsInteger DimjAsInteger Dimbest_value' smallest value in array Dimbest_jAsInteger ' loop from left to right Fori=LToR-1 DoEvents ' initialize lowest value best_value=vArray(i) best_j=i' initialize lowest value array location Forj=i+1ToR ' find the lowest value in the array in this loop IfvArray(j)<best_valueThen best_value=vArray(j) best_j=j EndIf Nextj ' put the smallest value at the from (left) of the array ' and put the value on the left of the array in the smallest ' value's previous position vArray(best_j)=vArray(i) vArray(i)=best_value Nexti
EndSub
PublicSubQuickSortBars(vArrayAsVariant,LAsInteger,RAsInteger,OptionalSleepTimeAsLong=0) DimiAsInteger' counter DimjAsInteger' counter DimBarVal1' temporary bar value DimBarVal2' temporary bar value
PublicSubSelectionSortBars(vArray,LAsInteger,RAsInteger,OptionalSleepTimeAsLong=0) ' name of array, lbound(), ubound() DimiAsInteger' counter DimjAsInteger' counter Dimbest_value' smallest value in array Dimbest_jAsInteger
' loop from left to right Fori=LToR-1 DoEvents IfSleepTime>0Then SleepSleepTime EndIf ' initialize lowest value best_value=vArray(i) best_j=i' initialize lowest value array location Forj=i+1ToR ' find the lowest value in the array in this loop IfvArray(j)<best_valueThen best_value=vArray(j) best_j=j EndIf Nextj ' put the smallest value at the from (left) of the array ' and put the value on the left of the array in the smallest ' value's previous position vArray(best_j)=vArray(i) vArray(i)=best_value frmMain.Bar(best_j)=vArray(best_j) frmMain.Bar(i)=vArray(i) Nexti EndSub
The author of this ingenious project is a scientist, namely Dr. Todd Danko. I found Dr. Danko to be connected with General Electric, Lockheed Martin and DARPA. Nice to have such people in the VB6 community. Like every VB6 programmer, it's fluent in C ++, Java and ASM. From here I will let his text explain the project.
Introduction
There are many off the shelf range finding components available including ultrasonic, infrared, and even laser rangefinders. All of these devices work well, but in the field of aerial robotics, weight is a primary concern. It is desirable to get as much functionality out of each component that is added to an air-frame. Miniature robotic rotor craft for example can carry about 100g of payload. It is possible to perform machine vision tasks such as obstacle identification and avoidance though the use of a webcam (or mini wireless camera interfaced to a computer via USB adapter). Better yet, two webcams can provide stereo machine vision thus improving obstacle avoidance because depth can be determined. The drawback of this of course is the addition of the weight of a second camera. This page describes how a mini laser pointer can be configured along with a single camera to provide mono-machine vision with range information.
The diagram below shows how projecting a laser dot onto a target that is in the field of view of a camera, the distance to that target may be calculated. The math is very simple, so this technique works very well for machine vision applications that need to run quickly.
So, here is how it works. A laser-beam is projected onto an object in the field of view of a camera. This laser beam is ideally parallel to the optical axis of the camera. The dot from the laser is captured along with the rest of the scene by the camera. A simple algorithm is run over the image looking for the brightest pixels. Assuming that the laser is the brightest area of the scene (which seems to be true for my dollar store laser pointer indoors), the dots position in the image frame is known. Then we need to calculate the range to the object based on where along the y axis of the image this laser dot falls. The closer to the center of the image, the farther away the object is.
As we can see from the diagram earlier in this section, distance (D) may be calculated:
Of course, to solve this equation, you need to know h, which is a constant fixed as the distance between your laser pointer and camera, and theta. Theta is calculated:
Put the two above equations together, we get:
OK, so the number of pixels from the center of the focal plane that the laser dot appears can just be counted from the image. What about the other parameters in this equation? We need to perform a calibration to derive these.
To calibrate the system, we will collect a series of measurements where I know the range to the target, as well as the number of pixels the dot is from the center of the image each time. This data is below:
Calibration Data
pixels from center
actual D (cm)
103
29
81
45
65
58
55
71
49
90
45
109
41
127
39
159
37
189
35
218
Using the following equation, we can calculate the actual angle based on the value of h as well as actual distance for each data point.
Now that we have a Theta_actual for each value, we can come up with a relationship that lets us calculate theta from the number of pixels from image center. I used a linear relationship (thus a gain and offset are needed). This seems to work well even though it does not account for the fact that the focal plane is a plane rather than curved at a constant radius around the center of the lens.
From my calibration data, I calculated:
Offset (ro) = -0.056514344 radians
Gain (rpc) = 0.0024259348 radians/pixel
Using:
I solved for calculated distances, as well as error from actual distance from the calibration data:
Actual and Calculated Range Data
pixels from center
calc D (cm)
actual D (cm)
% error
103
29.84
29
2.88
81
41.46
45
-7.87
65
57.55
58
-0.78
55
75.81
71
6.77
49
93.57
90
3.96
45
110.85
109
1.70
41
135.94
127
7.04
39
153.27
159
-3.60
37
175.66
189
-7.06
35
205.70
218
-5.64
Components
There are not a lot of parts in my sample range finder. I used a piece of cardboard to hold a laser pointer to a webcam so that the laser pointer points in a direction that is parallel to that of the camera. The parts seen below are laid out on a one inch grid for reference.
My assembled range finder looks like this:
Software
I have written software two ways, one using visual c++ and the other using visual basic. You will probably find that the visual basic version of the software is much easier to follow than the vc++ code, but with everything, there is a tradeoff. The vc++ code can be put together for free (assuming that you have visual studio), while the vb code requires the purchase of a third party software package (also in addition to visual studio).
Visual Basic
The visual basic code that I have written is available as a package named vb_laser_ranger.zip at the bottom of this page. For this code to work, you will need the VideoOCX ActiveX component installed on your computer. The code that describes the functions found in the main form is seen below:
PrivateSub exit_Click() ' only if running... If (Timer1.Enabled) Then
PrivateSub Start_Click() 'Init VideoOCX Control, allocate memory and start grabbing
If (Not Timer1.Enabled) Then Start.Caption = "Stop"
' Disable internal error messages in VideoOCX VideoOCX.SetErrorMessages False
' Init control If (Not VideoOCX.Init) Then ' Init failed. Display error message and end sub MsgBox VideoOCX.GetLastErrorString, vbOKOnly, "VideoOCX Error" End Else ' Allocate memory for global image handle capture_image = VideoOCX.GetColorImageHandle ' result_image = VideoOCX_Processed.GetColorImageHandle
Timer1.Enabled = True'Start capture timer
' Start Capture mode If (Not VideoOCX.Start) Then ' Start failed. Display error message and end sub MsgBox VideoOCX.GetLastErrorString, vbOKOnly, "VideoOCX Error" End EndIf EndIf Else Start.Caption = "Start" Timer1.Enabled = False'Stop Timer VideoOCX.Stop VideoOCX.Close EndIf
EndSub
PrivateSub Timer1_Timer() ' Timer for capturing - handles videoOCXTools Dim matrix AsVariant Dim height, width AsInteger Dim r, c AsInteger Dim max_r, max_c AsInteger Dim max_red AsInteger Dim gain, offset AsVariant Dim h_cm AsVariant Dim range AsInteger Dim pixels_from_center AsInteger
' Calibrated parameter for pixel to distance conversion gain = 0.0024259348 offset = -0.056514344 h_cm = 5.842
max_red = 0
' Capture an image If (VideoOCX.Capture(capture_image)) Then
' The laser dot should not be seen above the middle row (with a little pad) For r = height / 2 - 20 To height - 1
' Our physical setup is roughly calibrated to make the laser ' dot in the middle columns...dont bother lookng too far away For c = width / 2 - 25 To width / 2 + 24
' Look for the largest red pixel value in the scene (red laser) If (matrix(c, r, 2) > max_red) Then max_red = matrix(c, r, 2) max_r = r max_c = c EndIf Next c Next r
' Calculate the distance for the laser dot from middle of frame pixels_from_center = max_r - 120
' Calculate range in cm based on calibrated parameters range = h_cm / Tan(pixels_from_center * gain + offset)
' Print laser dot position row and column to screen row_val.Caption = max_r col_val.Caption = max_c
' Print range to laser illuminated object to screen range_val.Caption = range
' Draw a red vertical line to intersect target For r = 0 To height - 1 matrix(max_c, r, 2) = 255 Next r
' Draw a red horizontal line to intersect target For c = 0 To width - 1 matrix(c, max_r, 2) = 255 Next c
void CTripodDlg::doMyImageProcessing(LPBITMAPINFOHEADER lpThisBitmapInfoHeader) { // doMyImageProcessing: This is where you'd write your own image processing code // Task: Read a pixel's grayscale value and process accordingly
unsignedint W, H; // Width and Height of current frame [pixels] unsignedint row, col; // Pixel's row and col positions unsignedlong i; // Dummy variable for row-column vector unsignedint max_row; // Row of the brightest pixel unsignedint max_col; // Column of the brightest pixel BYTE max_val = 0; // Value of the brightest pixel
// Values used for calculating range from captured image data // these values are only for a specific camera and laser setup constdouble gain = 0.0024259348; // Gain Constant used for converting // pixel offset to angle in radians constdouble offset = -0.056514344; // Offset Constant constdouble h_cm = 5.842; // Distance between center of camera and laser double range; // Calculated range unsignedint pixels_from_center; // Brightest pixel location from center // not bottom of frame
char str[80]; // To print message CDC *pDC; // Device context need to print message
W = lpThisBitmapInfoHeader->biWidth; // biWidth: number of columns H = lpThisBitmapInfoHeader->biHeight; // biHeight: number of rows
for (row = 0; row < H; row++) { for (col = 0; col < W; col++) {
// Recall each pixel is composed of 3 bytes i = (unsignedlong)(row*3*W + 3*col);
// If the current pixel value is greater than any other, // it is the new max pixel if (*(m_destinationBmp + i) >= max_val) { max_val = *(m_destinationBmp + i); max_row = row; max_col = col; } } } // After each frame, reset max pixel value to zero max_val = 0;
for (row = 0; row < H; row++) { for (col = 0; col < W; col++) {
i = (unsignedlong)(row*3*W + 3*col);
// Draw a white cross-hair over brightest pixel in the output display if ((row == max_row) || (col == max_col)) *(m_destinationBmp + i) = *(m_destinationBmp + i + 1) = *(m_destinationBmp + i + 2) = 255; } }
// Calculate distance of brightest pixel from center rather than bottom of frame pixels_from_center = 120 - max_row;
// Calculate range in cm based on bright pixel location, and setup specific constants range = h_cm / tan(pixels_from_center * gain + offset);
// To print message at (row, column) = (75, 580) pDC = GetDC();
// Display frame coordinates as well as calculated range sprintf(str, "Max Value at x= %u, y= %u, range= %f cm ",max_col, max_row, range); pDC->TextOut(75, 580, str); ReleaseDC(pDC); }
My complete code for this project is available as a package named LaserRange.zip at the bottom of this page. Note, to run this executable, you will need to have both qcsdk and the qc543 driver installed on your computer. Sorry, but you are on your own to find both of these. Below are two examples of the webcam based laser range finder in action. Note how it looks like there are two laser dots in the second example. This "stray light" is caused by internal reflections in the camera. The reflected dot loses intensity as it bounces within the camera so it does not interfere with the algorithm that detects the brightest pixel in the image.
Future Work
One specific improvement that can be made to this webcam based laser range finder is to project a horizontal line rather than a dot onto a target. This way, we could calculate the range for each column of the image rather than just one column. Such a setup would be able to be used to locate areas of maximum range as places that a vehicle could steer towards. Likewise, areas of minimum range would be identified as obstacles to be avoided.
In a previous article entitled "Range finder with a Laser and a Webcam in Visual Basic 6.0" we have shown a project for distance estimation by using a laser and a webcam. Here we have another interesting project by Dr. Todd Danko, that uses VB6. Download from ME
Introduction
The most advanced pieces of scientific equipment are often both very rare and very expensive. This is a poor combination if your research needs dictate the use of such a device.
For example, scanning electron microscopes, depending on quality, cost millions of dollars. This high price prohibits their purchase for occasional research use. This high price also decreases the probability that there is a facility near by that has both, a scanning electron microscope, and has time to let you use it.
One solution to this scenario is to create a product that allows scanning electron microscopes and possibly other equipment to be used remotely. Such a device would utilize the internet to pass commands to the microscope, as well as return data from the microscope back to the user.
Proposed Solution
Build a prototype of an internet controlled appliance that simulates the remote operation of a scanning electron microscope.
This appliance has four main components:
Server Software - to interface with the mechanical device, and the internet
Client Software - to interface with the user, and the internet
Camera -to simulate output data similar to that of a microscope
Stepper Motor X-Y table - to allow the user to change the position of the subject under the microscope
Procedure
Parts
Internet Appliance Electro-Mechanical Parts
Part
Vendor
Part #
Quantity
Price
Stepper Motor
Jameco
151861
2
$6.19
Diode (1N4003)
Jameco
76970
8
$0.04
Gears (Assorted)
Jameco
131801
1
$7.99
Power Driver
Arrow
UNC5804B
2
$4.22
8255 Card
Boondog.com
8255 Kit
1
$59.00
Logitech WebCam
Best Buy
961237-0403
1
$49.99
Breadboard
Radio Shack
276-174
1
$13.49
Battery Holder
Radio Shack
270-396
1
$1.79
Wrapping Wire
Radio Shack
278-501
1
$2.99
Electro-Mechanical
This internet appliance calls for several electro-mechanical components:
An X-Y table that is moved by two stepper motors.
The X-Y table's purpose is to move the subject of the camera up/down, left/right in the camera's field of view.
The X-Y table is constructed of a wood frame, with stepper motors gear mounted.
Rotational motions of the stepper motors are transferred into linear motion by a system of wires and pulleys.
An 8255 card to interface the server PC with the stepper motor control circuits.
The 8255 cards purpose is to provide an interface between the server PC and the stepper motor control circuits. More information on the 8255 card's construction and operation may be found at www.boondog.com.
Two stepper motor control circuits.
The 5804 chips take digital data in from the 8255 card, and convert it into the phasing of the stepper motor coils to rotate the stepper motors in the desired directions by specified amounts.
Below is a schematic for one stepper motor control circuit. Please note that this appliance requires two motors, so two of these circuits must be constructed. The first control circuit is connected to ports A.0 and A.1 of the 8255 card. The second control circuit is connected to ports B.0 and B.1 of the 8255 card.
A Camera to collect image data.
A camera captures pictures of the subject on the X-Y table.
The camera is connected to the server PC in this case through a USB cable.
The drivers included with the camera were used to capture image data through the USB port of the server PC.
Below is a picture of the overall electro-mechanical setup.
Note the ribbon cable coming in from the top of the scene. This attaches to the 8255 card.
Note the stepper motor control circuits to the left of the photo.
A battery pack is used to power the stepper motors.
Note the use of wires wrapped around posts on the X-Y table to convert the motor's motion into linear movement. The platform that the subject would be placed on has been removed to expose the X-Y table mechanism.
Software
The software to control this internet appliance is broken into two groups: Software on the server PC and software on the client PC.
Server
The purpose of the server is to interface the internet appliance with the internet.
The server has several software components:
Visual Basic software to communicate with the client through WinSock, and to communicate with the 8255 Card through the 8255.dll.
The visual basic software contains the following components and may be downloaded at the bottom of this page as stepper_control.zip:
stepper_control_server.frm
stepper_control_server.vbp
stepper_control_server.vbw
stepper_control_server.exe
FTP Server software to make image files written by the Visual Basic software available for retrieval by the client software. For this project, the Titan FTP Server was used.
ActiveX component to accept data from the camera, and save it to a file on the server PC's hard drive. For this project, WebCam OCX was used.
Note, ActiveX components as well as the 8255.dll must be installed on a PC in addition to the Visual Basic Executable in order for the server to properly function.
Client
The purpose of the client is to interface the user with the appliance through the internet.
The client has several software components:
Visual Basic software to communicate with the server through WinSock, and to communicate with the user through a GUI.
The visual basic software contains the following components and may be downloaded at the bottom of this page as stepper_control.zip:
stepper_control_client.frm
stepper_control_client.vbp
stepper_control_client.vbw
stepper_control_client.exe
ActiveX FTP Client component to allow the client to FTP image files from the server PC's FTP server. For this application, Distinct Software's ftpClient OCX was used.
Note, ActiveX components must be installed on a PC in addition to the Visual Basic Executable in order for the client to properly function.
Turbo C
To demonstrate the functionality of the circuit before investing time into writing the server and client software, I wrote some simple code in TurboC.
This code:
Prompts the user for the address of the 8255 card.
Prompts the user for the direction to rotate the motor.
Prompts the user for the number of degrees to rotate the motor.
Rotates the stepper motor in the desired direction by the desired amount.
You may download source and executable code at the bottom of this page below in turboc.zip:
Theory of Operation
Here is how the system works:
Control data is passed between the server and client using WinSock TCP/IP.
Image data is passed from the server to the client using FTP. The client has ftp code built into it, but the server PC must be running third party FTP server software.
When a command is sent from the client to the server, the server determines what direction to control the X-Y table.
The server then sends commands to the 8255 card by way of the 8255.dll file.
Once the server has completed commanding the X-Y table to move, the server saves an image of the new scene.
This scene is written to a file on the server PC by means of the WebCam OCX ActiveX component.
Upon completion of writing the image file, the server sends a message to the client to indicate that the X-Y table has reached the desired position, and that a new image file is available.
When the client receives this message, it opens an FTP dialog with the server PC and retrieves the latest image file.
The client then displays the latest image file and allows the user through the client to send more control data.
Operating Procedure
These steps must be taken in the following order to successfully run the internet appliance:
Start Up Procedure:
Connect appliance to the server PC.
Confirm network connectivity between server and client PCs.
Determine IP address of server PC.
Start the FTP server on the server PC.
Start the Stepper Control Server software on the server PC.
Select the camera driver when the Stepper Control Server requests it.
Start the Stepper Control Client software on the client PC.
Enter the server PC's IP address when the Stepper Control Client software requests it.
Utilize appliance.
Shut Down Procedure:
Close Stepper Control Client software.
Close Stepper Control Server software.
Close FTP server software on server PC.
Conclusion
Utilization of internet appliances such as an internet adapted scanning electron microscope have many advantages. When a piece of equipment is networked through the internet, two main things happen:
Expensive and rare pieces of equipment may be brought closer to users all over the world. This allows those who can not justify purchasing equipment to use it after an agreement is made with a host organization.
Organizations considering purchase of expensive equipment may be able to diffuse the cost of the equipment through renting time on the machine over the internet. The customer base for this use is of course worldwide.
The first lesson I learned about "dumb decisions" from Microsoft was when they trashed hardware access when upgrading from Windows 98 and ME. No one noticed that the decision instantly put many thousands of small businesses in trouble, mine included, because the hardware layer access in Windows 98 was the cornerstone of many industrial control systems. All of a sudden I found myself in dozens of meetings explaining to clients why non-NT was no longer made!
An entire Industrial rack mount computer industry was wiped out overnight. The millions invested in motherboard I/O (including printed circuit boards we developed), now wasted. The information systems I sold each meant many MS OS sales. MS didn't even seem to notice or care that their industrial usage arm was holed below the waterline. (Now, in industrial control solutions, Linux has taken over .. it could have been MS).
XP is a lot more stable, and indeed most NT based upgrades have been a considerable improvement, but it wouldn't have been rocket science for Microsoft to have added backward compatibility.
In more general terms, Microsoft need to learn "consistency consistency consistency". The ignorance and arrogance of Microsoft is bewildering if not epic. Ever since Gates left, they seem incapable of applying common sense. Someone in Harvard must have done a popular thesis on obsolescence because MS has adopted it as their mantra! They have abandoned VB6, XNA, SilverLight, Skype API, the original MS Office menus, VS Java, Foxpro, Access, .... Each time they do this, people jump out windows with their careers and businesses in tatters. But Microsoft are just psychopathic at times in their ability to ignore the plight of their own customers. At one point in W8, they even tried to get rid of the desktop! It beggars belief that they expected clients to sit at desks with administrative software running on something that looked more like a giant mobile phone than a desktop PC. Success breeds contempt!
A great story: Once there were two microprocessor giants, Intel and Motorola. Motorola had the best and fastest 16-bit chips and were set to dominate. Then they both decided to come out with a 32 bit chip. Motorola's was miles better than the Intel 32-bit device. Miles better! BUT Intel did something Motorola decided wasn't important... Intel maintained backward compatibility with their original 16-bit processes. Motorola BETRAYED their own client base. They just stuck up their middle finger and said "go back to school and re-learn". NOW -- how many people have heard of Motorola processors these days? And that's the same dumb mistake Microsoft make over and over again. "CONSISTENCY CONSISTENCY CONSISTENCY".
The lesson is that when people buy tech, they are making a decision to invest colossal amounts of time in experience in that tech. My customers used to demand Microsoft because they thought the bigger the company, the safer their investment. But now they are just confused. Even banks use opensource.
Once a company has shown a willingness to betray customer investment with gaay abandon, that company is dead. Teach that one in Harvard!
Description: Creating music, I've seen a lot of different virtual instruments and effects. One of the most interesting effects is the vocoder, which allows you to modulate his voice and make it look like a voice for example a robot or something like that. Vocoder was originally used to compress the voice data, and then it began to be used in the music industry. Because I had free time, I decided to write something like this for the sake of the experiment and describe in detail the stages of development for VB6.
The signal from the microphone (speech) is fed to a bank of band-pass filters, each of which passes only a small part of the frequency band of the speech signal. The greater the number of filters - the better speech intelligibility. At the same time, the carrier signal (e.g. ramp) is also passed through the same filter bank. Filter output speech signal is fed to envelope detectors which control modulators and outputs a filter carrier signal passes to the other input of the modulator. As a result, each band speech signal adjusts the level of the corresponding band carrier (modulates it). Further, output signals from all modulators are mixed and sent to the output. Further, all signal modulators are mixed and sent to the output. In order to improve speech intelligibility also apply additional blocks, such as the detector "sizzling" sound. So, to begin development necessary to determine the source signals, where they will take. It is possible for example to capture data from a file or directly processed in real-time from a microphone or line input. To test very easy to use file, so we will do and so and so. As the carrier will use an external file looped in a circle, to adjust the tone simply add the ability to change the playback speed, which will change the tone. To capture the sound of the file will use Audio Compression Manager (ACM), with it very convenient to make conversion between formats (because the file can be in any format, you would have to write some functions to different formats). It may be that to convert to the desired format will not correct ACM drivers, then play this file will not be available (although you can try to do it in 2 stages). As input files will use the wav - files, because to work with them in the system has special features to facilitate retrieving data from them.
When loading of form we perform initialization of all components. Capture, playing back the audio size FFT, the amount of overlap, overlapping buffers, creating buffers for integer and complex data. Next, I made a box shape with rounded corners, as use a window without frame (draw in the nonclient area had no desire). Now the whole problem is reduced to handling events - AudioPlayback_NewData and AudioCapture_NewData. First event occurs when the playback device needs another portion of the audio data, the second when the buffer capture, in which we simply copy the data into a temporary buffer from where it will take them at processing AudioPlayback_NewData. The main method - Process, in it we just do the conversion. First we check whether we capture from a file or device. To do this, we check the variable mInpFile, which specifies the name of the input file to capture. If capture is made from a file, then we are using object inpConv, which is an instance of clsTrickWavConverter, convert the data into the format you want us to. If the data is finished (the number of bytes read does not match the passed), it means that we are on the edge of the file and continue to have to start over again. Also check the carrier signal and if it is not set then just copy the input data on output and, in this case, we will hear the raw sound. Otherwise, we translate the data into a complex form (count a real part of the signal and the imaginary zero out) and puts the resulting array in an overlapping buffer. Next, start processing the carrier signal. Because carrier signal we can have a very small length (you can use one wave period), in order to optimize I will do the repetition of the signal if required. Let me explain. For example, if we have a carrier signal 10 ms and 100 ms buffer (for example), then you could just call the conversion each time using ACM overwriting the pointer to the array destination, but it is not optimal. For optimization can be converted only once, and then simply duplicate the data to the end of the array, which we did. Only then do not forget to change the position in the source file, otherwise the next phase of the reading will not be the same and will flicks. We will write to another buffer (rawBuffer). This buffer length is based on the pitch shift. For example, if we want to shift the tone for the amount of semitones (halftones), the buffer size must be rawBuffer 2semitones / 12 times more. Then we simply compress / stretch buffer to a value mFFTSize, which will give us the acceleration / deceleration, and as a result increase / decrease tone. After all the manipulations we write data in an overlapping buffer and start processing. To do this, we pass by the number of overlapping data and handle them. Class objects clsTrickOverlappedBuffer return us the correct data. Processing is clear from the code, as We consider in detail the performance of each class. After processing all of overlap we get the output and convert them to integer suitable for playback. As the setting uses a form frmSettings. As the list of devices using a standard listbox, just going through my drawing class. The list of devices will be added in the following order:
A default device predetermined format
Device 1
Device 2
...
Device n
Capturing from a file
For testing click on the last point message is used LB_GETITEMRECT, which receives the coordinates and size of the item in the list. If this is not done then click the sheet of paper, if there is an empty space at the bottom will be equivalent to clicking on the last point. In the handler settings button in the main form frmTrickVocoder we check capture device and either open the file for conversion or initialize capture. To adjust the volume and mixing using a logarithmic scale, as the sensitivity of the human ear is not linear.
Here we have a superb app made by Krivous Anatoly Anatolevich. Here is his description on VBForums: The sound is analyzed through a standard recording device, i.e. you can select the microphone and view its spectrum, or you can select stereo mixer and view a spectrum of a playback sound.
Captures via my Microphone
This visualizer allows to adjust the number of displayed octaves, transparency of background and amplification.
You can also load a palette from an external PNG file with 32ARGB format. It also supports the following effects: "blur" and "burning". You can view a spectrum of a signal represented in the two modes: arcs (rings) and sectors (pies). If you use the ring view an octave is mapped to radial coordinate and a semitone to angle. The separated harmonics are placed along the same line; color represents an intensity. The sectors view maps the amount of signal to the radial coordinate, the frequency in octaves to the color, the frequency in semitones to the angular coordinate.
This idea was suggested to me by Vladislav Petrovky (aka Hacker). His idea was a little different.
Initially it creates the buffers for sound and buffer bitmaps. Further it starts the sound capture process and waits when a buffer will be filled. When a buffer has been filled it begins processing. Firstly it performs the Fast Fourier Transform in order to transform a signal to the frequency domain form. Before performing it applies the Hamming window in order to reduce distortions because a signal has discontinuity at the edges of a buffer. When a signal has been translated to the frequency domain the buffer contains complex value that represent the vectors. The module (length) of a vector implies the energy of signal in that frequency and the argument (angle) implies phase of a harmonic in that frequency.
Screenshot of the menu
We need the energy of frequency although the phase information allows to determine the frequency more accurately considering the phase difference. I don't use phase information in this project. The drawing method is different for each appearance mode. In order to boost the work it uses the precalculated coordinates named MapData. This array contains the angles of arcs and sectors for the current appearance mode. When coordinates has been calculated it calculates the amount of frequency for each FFT bin figuring out the length of a vector. This value is uses as the index in the color palette after converting the value to a range from 0 to 255. Further GDI+ draws the necessary primitives depending on the appearance mode. Note that all drawing occur onto the buffer bitmap not on window. I specially have not mentioned about the Release procedure that animates the background. This procedure applies an effect to the buffer bitmap before signal processing. It uses the Fade property that determines the speed of the disappearance of previous drawing bitmap. It just decrease the alpha value of the entire bitmap. When you use an effect it also works with the bits of the buffer bitmap and decreases the alpha value. For instance, if the blur effect has been selected it averages the near pixels (analog of low-pass filtering) then it decreases the alpha value for all pixels depending on Fade property. Eventually it draws buffer bitmap onto the main window. Thus it draws the energy of the spectrum of signal in the polar coordinates. It can be used as the start point for the notes or chord recognition.
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' 'Cephes Math Library Release 2.8: June, 2000 'Copyright by Stephen L. Moshier ' 'Contributors: ' * Sergey Bochkanov (ALGLIB project). Translation from C to ' pseudocode. ' 'See subroutines comments for additional copyrights. ' '>>> SOURCE LICENSE >>> 'This program is free software; you can redistribute it and/or modify 'it under the terms of the GNU General Public License as published by 'the Free Software Foundation (www.fsf.org); either version 2 of the 'License, or (at your option) any later version. ' 'This program is distributed in the hope that it will be useful, 'but WITHOUT ANY WARRANTY; without even the implied warranty of 'MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 'GNU General Public License for more details. ' 'A copy of the GNU General Public License is available at 'http://www.fsf.org/licensing/licenses ' '>>> END OF LICENSE >>> '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' 'Routines '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' 'Airy function ' 'Solution of the differential equation ' 'y"(x) = xy. ' 'The function returns the two independent solutions Ai, Bi 'and their first derivatives Ai'(x), Bi'(x). ' 'Evaluation is by power series summation for small x, 'by rational minimax approximations for large x. ' ' ' 'ACCURACY: 'Error criterion is absolute when function <= 1, relative 'when function > 1, except * denotes relative error criterion. 'For large negative x, the absolute error increases as x^1.5. 'For large positive x, the relative error increases as x^1.5. ' 'Arithmetic domain function # trials peak rms 'IEEE -10, 0 Ai 10000 1.6e-15 2.7e-16 'IEEE 0, 10 Ai 10000 2.3e-14* 1.8e-15* 'IEEE -10, 0 Ai' 10000 4.6e-15 7.6e-16 'IEEE 0, 10 Ai' 10000 1.8e-14* 1.5e-15* 'IEEE -10, 10 Bi 30000 4.2e-15 5.3e-16 'IEEE -10, 10 Bi' 30000 4.9e-15 7.3e-16 ' 'Cephes Math Library Release 2.8: June, 2000 'Copyright 1984, 1987, 1989, 2000 by Stephen L. Moshier ' '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' PublicSub Airy(ByVal x AsDouble, _ ByRef Ai AsDouble, _ ByRef Aip AsDouble, _ ByRef Bi AsDouble, _ ByRef Bip AsDouble) Dim z AsDouble Dim zz AsDouble Dim t AsDouble Dim f AsDouble Dim g AsDouble Dim uf AsDouble Dim ug AsDouble Dim k AsDouble Dim zeta AsDouble Dim theta AsDouble Dim domflg AsLong Dim c1 AsDouble Dim c2 AsDouble Dim sqrt3 AsDouble Dim sqpii AsDouble Dim AFN AsDouble Dim AFD AsDouble Dim AGN AsDouble Dim AGD AsDouble Dim APFN AsDouble Dim APFD AsDouble Dim APGN AsDouble Dim APGD AsDouble Dim AN AsDouble Dim AD AsDouble Dim APN AsDouble Dim APD AsDouble Dim BN16 AsDouble Dim BD16 AsDouble Dim BPPN AsDouble Dim BPPD AsDouble
sqpii = 0.564189583547756 c1 = 0.355028053887817 c2 = 0.258819403792807 sqrt3 = 1.73205080756888 domflg = 0# If x > 25.77 Then Ai = 0# Aip = 0# Bi = MaxRealNumber Bip = MaxRealNumber ExitSub End If If x < -2.09 Then domflg = 15# t = Sqr(-x) zeta = -(2# * x * t / 3#) t = Sqr(t) k = sqpii / t z = 1# / zeta zz = z * z AFN = -0.131696323418332 AFN = AFN * zz - 0.626456544431912 AFN = AFN * zz - 0.693158036036933 AFN = AFN * zz - 0.279779981545119 AFN = AFN * zz - 0.04919001326095 AFN = AFN * zz - 4.06265923594885E-03 AFN = AFN * zz - 1.59276496239262E-04 AFN = AFN * zz - 2.77649108155233E-06 AFN = AFN * zz - 1.67787698489115E-08 AFD = 1# AFD = AFD * zz + 13.3560420706553 AFD = AFD * zz + 32.6825032795225 AFD = AFD * zz + 26.73670409415 AFD = AFD * zz + 9.1870740290726 AFD = AFD * zz + 1.47529146771666 AFD = AFD * zz + 0.115687173795188 AFD = AFD * zz + 4.40291641615211E-03 AFD = AFD * zz + 7.54720348287414E-05 AFD = AFD * zz + 4.5185009297058E-07 uf = 1# + zz * AFN / AFD AGN = 1.97339932091686E-02 AGN = AGN * zz + 0.391103029615688 AGN = AGN * zz + 1.06579897599596 AGN = AGN * zz + 0.93916922981665 AGN = AGN * zz + 0.351465656105548 AGN = AGN * zz + 6.33888919628925E-02 AGN = AGN * zz + 5.85804113048388E-03 AGN = AGN * zz + 2.82851600836737E-04 AGN = AGN * zz + 6.98793669997261E-06 AGN = AGN * zz + 8.11789239554389E-08 AGN = AGN * zz + 3.41551784765924E-10 AGD = 1# AGD = AGD * zz + 9.30892908077442 AGD = AGD * zz + 19.8352928718312 AGD = AGD * zz + 15.5646628932865 AGD = AGD * zz + 5.47686069422975 AGD = AGD * zz + 0.954293611618962 AGD = AGD * zz + 8.64580826352392E-02 AGD = AGD * zz + 4.12656523824223E-03 AGD = AGD * zz + 1.01259085116509E-04 AGD = AGD * zz + 1.17166733214414E-06 AGD = AGD * zz + 4.9183457006293E-09 ug = z * AGN / AGD theta = zeta + 0.25 * PI() f = Sin(theta) g = Cos(theta) Ai = k * (f * uf - g * ug) Bi = k * (g * uf + f * ug) APFN = 0.185365624022536 APFN = APFN * zz + 0.886712188052584 APFN = APFN * zz + 0.987391981747399 APFN = APFN * zz + 0.401241082318004 APFN = APFN * zz + 7.10304926289631E-02 APFN = APFN * zz + 5.90618657995662E-03 APFN = APFN * zz + 2.33051409401777E-04 APFN = APFN * zz + 4.08718778289035E-06 APFN = APFN * zz + 2.48379932900442E-08 APFD = 1# APFD = APFD * zz + 14.7345854687503 APFD = APFD * zz + 37.542393343549 APFD = APFD * zz + 31.4657751203046 APFD = APFD * zz + 10.9969125207299 APFD = APFD * zz + 1.78885054766999 APFD = APFD * zz + 0.141733275753663 APFD = APFD * zz + 5.44066067017226E-03 APFD = APFD * zz + 9.39421290654511E-05 APFD = APFD * zz + 5.65978713036027E-07 uf = 1# + zz * APFN / APFD APGN = -3.55615429033082E-02 APGN = APGN * zz - 0.637311518129436 APGN = APGN * zz - 1.70856738884312 APGN = APGN * zz - 1.50221872117317 APGN = APGN * zz - 0.563606665822103 APGN = APGN * zz - 0.102101031120217 APGN = APGN * zz - 9.48396695961445E-03 APGN = APGN * zz - 4.60325307486781E-04 APGN = APGN * zz - 1.14300836484517E-05 APGN = APGN * zz - 1.33415518685547E-07 APGN = APGN * zz - 5.63803833958894E-10 APGD = 1# APGD = APGD * zz + 9.8586580169613 APGD = APGD * zz + 21.6401867356586 APGD = APGD * zz + 17.3130776389749 APGD = APGD * zz + 6.17872175280829 APGD = APGD * zz + 1.08848694396321 APGD = APGD * zz + 9.95005543440888E-02 APGD = APGD * zz + 4.78468199683887E-03 APGD = APGD * zz + 1.18159633322839E-04 APGD = APGD * zz + 1.37480673554219E-06 APGD = APGD * zz + 5.79912514929148E-09 ug = z * APGN / APGD k = sqpii * t Aip = -(k * (g * uf + f * ug)) Bip = k * (f * uf - g * ug) ExitSub End If If x >= 2.09 Then domflg = 5# t = Sqr(x) zeta = 2# * x * t / 3# g = Exp(zeta) t = Sqr(t) k = 2# * t * g z = 1# / zeta AN = 0.346538101525629 AN = AN * z + 12.0075952739646 AN = AN * z + 76.2796053615235 AN = AN * z + 168.089224934631 AN = AN * z + 159.756391350164 AN = AN * z + 70.5360906840444 AN = AN * z + 14.026469116339 AN = AN * z + 1# AD = 0.56759453263877 AD = AD * z + 14.7562562584847 AD = AD * z + 84.5138970141475 AD = AD * z + 177.3180881454 AD = AD * z + 164.23469287153 AD = AD * z + 71.4778400825576 AD = AD * z + 14.0959135607834 AD = AD * z + 1# f = AN / AD Ai = sqpii * f / k k = -(0.5 * sqpii * t / g) APN = 0.613759184814036 APN = APN * z + 14.7454670787755 APN = APN * z + 82.0584123476061 APN = APN * z + 171.184781360976 APN = APN * z + 159.317847137142 APN = APN * z + 69.9778599330103 APN = APN * z + 13.9470856980482 APN = APN * z + 1# APD = 0.334203677749737 APD = APD * z + 11.1810297306158 APD = APD * z + 71.172735214786 APD = APD * z + 158.778084372838 APD = APD * z + 153.206427475809 APD = APD * z + 68.675230459278 APD = APD * z + 13.8498634758259 APD = APD * z + 1# f = APN / APD Aip = f * k If x > 8.3203353 Then BN16 = -0.253240795869364 BN16 = BN16 * z + 0.575285167332467 BN16 = BN16 * z - 0.329907036873225 BN16 = BN16 * z + 0.06444040689482 BN16 = BN16 * z - 3.82519546641337E-03 BD16 = 1# BD16 = BD16 * z - 7.15685095054035 BD16 = BD16 * z + 10.6039580715665 BD16 = BD16 * z - 5.23246636471251 BD16 = BD16 * z + 0.957395864378384 BD16 = BD16 * z - 0.055082814716355 f = z * BN16 / BD16 k = sqpii * g Bi = k * (1# + f) / t BPPN = 0.465461162774652 BPPN = BPPN * z - 1.08992173800494 BPPN = BPPN * z + 0.638800117371828 BPPN = BPPN * z - 0.126844349553103 BPPN = BPPN * z + 7.6248784434211E-03 BPPD = 1# BPPD = BPPD * z - 8.70622787633159 BPPD = BPPD * z + 13.8993162704553 BPPD = BPPD * z - 7.14116144616431 BPPD = BPPD * z + 1.34008595960681 BPPD = BPPD * z - 7.84273211323342E-02 f = z * BPPN / BPPD Bip = k * t * (1# + f) ExitSub End If EndIf f = 1# g = x t = 1# uf = 1# ug = x k = 1# z = x * x * x DoWhile t > MachineEpsilon uf = uf * z k = k + 1# uf = uf / k ug = ug * z k = k + 1# ug = ug / k uf = uf / k f = f + uf k = k + 1# ug = ug / k g = g + ug t = Abs(uf / f) Loop uf = c1 * f ug = c2 * g If domflg Mod 2# = 0# Then Ai = uf - ug EndIf If domflg \ 2# Mod 2# = 0# Then Bi = sqrt3 * (uf + ug) EndIf k = 4# uf = x * x / 2# ug = z / 3# f = uf g = 1# + ug uf = uf / 3# t = 1# DoWhile t > MachineEpsilon uf = uf * z ug = ug / k k = k + 1# ug = ug * z uf = uf / k f = f + uf k = k + 1# ug = ug / k uf = uf / k g = g + ug k = k + 1# t = Abs(ug / g) Loop uf = c1 * f ug = c2 * g If domflg \ 4# Mod 2# = 0# Then Aip = uf - ug EndIf If domflg \ 8# Mod 2# = 0# Then Bip = sqrt3 * (uf + ug) EndIf EndSub
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' 'Copyright (c) 2009, Sergey Bochkanov (ALGLIB project). ' '>>> SOURCE LICENSE >>> 'This program is free software; you can redistribute it and/or modify 'it under the terms of the GNU General Public License as published by 'the Free Software Foundation (www.fsf.org); either version 2 of the 'License, or (at your option) any later version. ' 'This program is distributed in the hope that it will be useful, 'but WITHOUT ANY WARRANTY; without even the implied warranty of 'MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 'GNU General Public License for more details. ' 'A copy of the GNU General Public License is available at 'http://www.fsf.org/licensing/licenses ' '>>> END OF LICENSE >>> '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' 'Routines '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' 'Fast kernel ' ' -- ALGLIB routine -- ' 19.01.2010 ' Bochkanov Sergey ' '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' PublicFunction CMatrixRank1F(ByVal M AsLong, _ ByVal N AsLong, _ ByRef A() As Complex, _ ByVal IA AsLong, _ ByVal JA AsLong, _ ByRef U() As Complex, _ ByVal IU AsLong, _ ByRef V() As Complex, _ ByVal IV AsLong) AsBoolean Dim Result AsBoolean
Result = False
CMatrixRank1F = Result EndFunction
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' 'Fast kernel ' ' -- ALGLIB routine -- ' 19.01.2010 ' Bochkanov Sergey ' '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' PublicFunction RMatrixRank1F(ByVal M AsLong, _ ByVal N AsLong, _ ByRef A() AsDouble, _ ByVal IA AsLong, _ ByVal JA AsLong, _ ByRef U() AsDouble, _ ByVal IU AsLong, _ ByRef V() AsDouble, _ ByVal IV AsLong) AsBoolean Dim Result AsBoolean
Result = False
RMatrixRank1F = Result EndFunction
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' 'Fast kernel ' ' -- ALGLIB routine -- ' 19.01.2010 ' Bochkanov Sergey ' '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' PublicFunction CMatrixMVF(ByVal M AsLong, _ ByVal N AsLong, _ ByRef A() As Complex, _ ByVal IA AsLong, _ ByVal JA AsLong, _ ByVal OpA AsLong, _ ByRef X() As Complex, _ ByVal IX AsLong, _ ByRef Y() As Complex, _ ByVal IY AsLong) AsBoolean Dim Result AsBoolean
Result = False
CMatrixMVF = Result EndFunction
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' 'Fast kernel ' ' -- ALGLIB routine -- ' 19.01.2010 ' Bochkanov Sergey ' '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' PublicFunction RMatrixMVF(ByVal M AsLong, _ ByVal N AsLong, _ ByRef A() AsDouble, _ ByVal IA AsLong, _ ByVal JA AsLong, _ ByVal OpA AsLong, _ ByRef X() AsDouble, _ ByVal IX AsLong, _ ByRef Y() AsDouble, _ ByVal IY AsLong) AsBoolean Dim Result AsBoolean
'# John Wiley & Sons, Inc. # '# # '# Book: Markov Chains: From Theory To Implementation And Experimentation # '# Author: Dr. Paul Gagniuc # '# Data: 01/05/2017 # '# # '# Title: # '# Discrete Probability Detector # '# # '# Short Description: # '# The purpose of this algorithm is to convert any string into a probability matrix. # '#_______________________ # '# Detailed description: \ # '# This algorithm is an advanced variation of the "ExtractProb" function from the book. The # '# main difference between "ExtractProb" function and the DPD algorithm is the automatic # '# identification of states. # '#_________________________________________________________________________________________________# '# Initially, the states are identified in the first phase. Each new letter found in "S" is # '# appended to the string forming in variable "a". Thus, variable "a" gradually increases until # '# all types of letters from "S" are identified. # '#_________________________________________________________________________________________________# '# In the second phase the elements of matrix "m" are filled with zero values for later # '# purposes. Also, in the second phase the first column of matrix "e" is filled with letters # '# found in variable "a", and the second column of matrix "e" is filled with zero values for # '# later use. # '#_________________________________________________________________________________________________# '# In the third phase, the transitions between letters of "S" are counted and stored in # '# matrix "m". # '# The strategy in this particular case is to fill matrix "m" with transition counts before the # '# last letter in "S" is reached. In this case, the first column of matrix "e" already contains # '# the letters from variable "a". The two components of vector "l" contain the "i" and "i+1" # '# letters from "S". The count of individual transitions between letters is made by a comparison # '# between vector "l" and the elements from the first column of matrix "e". The number of rows # '# in matrix "m" and matrix "e" is the same, namely "d". Therefore, an extra loop can be avoided # '# by mapping matrix "m" through a coordinate system. For instance, if the letter from position # '# "i" in "S" stored in "l(0)" and the letter from "j" row in matrix "e" (e(j,0)) are the same # '# then variable "r = j". Likewise, if the letter "i+1" stored in l(1) and the letter from e(j,0) # '# are the same then variable "c = j". Variable "r" represents the rows of matrix "m", whereas # '# variable "c" represents the columns of matrix "m" (m(r, c)). # '# Thus, at each step through "S", an element of matrix "m" is always incremented according to # '# the coordinates received from "r" and "c". This "coordinate" approach greatly increases the # '# processing speed of the algorithm. The number of loops = (k-1)*d, where "d" represents the # '# number of states (or letter types), and "k" is the number of letters in "S". When the letter # '# stored in "l(0)" and the letter from "j" row in matrix "e" are the same, the second column of # '# matrix "e" is also incremented. The second column of matrix "e" stores the number of # '# appearances for each type of letter in "S". # '#_________________________________________________________________________________________________# '# In the fourth phase, the counts from matrix "m" elements are divided by the counts from the # '# second column of matrix "e". The result of this division is stored in the same position in # '# matrix "m", and represents a transition probability. # '#_________________________________________________________________________________________________# '# # '# Special considerations: # '# # '# If a state at the end of "S" (ie HAHAAAHQ) does not occur in the rest of "S" then matrix "m" # '# will contain a row with all elements on zero. Since it is at the end of "S", the letter does # '# not make a transition to anything. If a state from the beginning of "S" (ie. QHAHAAAH) does # '# not occur in the rest of "S" then matrix "m" will contain a column with all elements on zero. # '# Since the first letter it is only seen at the beginning of "S", no other letter makes a # '# transition to it. # '# # '# The meaning of variables: # '# _____________________________________________________________________________________ # '# S = |The string that is being analyzed. _| # '# _____________________________________________________________________________________ # '# q = |It is a flag variable with initial value of 1. The value of q becomes zero only if a | # '# |letter x in the "S" string coresponds with a letter y in the "a" string. _| # '# _____________________________________________________________________________________ # '# a = |The variable that holds the letters representing the states. The variable gradually | # '# |increases in length as the "S" string is analyzed. At each loop, a new letter is | # '# |added to variable "a" only if the value of q becomes zero. Thus, at the end of the | # '# |first loop the number of letters in the variable is equal to the total number | # '# |of states. _| # '# _____________________________________________________________________________________ # '# d = |Represents the total number of states and is the length of "a" variable. _| # '# _____________________________________________________________________________________ # '# m = |The main probability matrix which the function produces. _| # '# _____________________________________________________________________________________ # '# k = |Represents the length of the "S" string. _| # '# _____________________________________________________________________________________ # '# e = |It is a matrix with two columns, namely column 0 and 1. Column 0 stores all the | # '# |letters found in "a". Column 1 stores the number of appearances for each type | # '# |of letter in "S". _| # '# _____________________________________________________________________________________ # '# l = |Is a vector with two components. Vector "l" contains the "i" and "i+1" letters | # '# |from "S". _| # '# # '###################################################################################################
Function Discrete_Probability_Detector(ByVal S AsString)
Dim e() AsString Dim m() AsString Dim l(0 To 1) AsString
k = Len(S) w = 1
For i = 1 To k q = 1 For j = 0 To Len(a) x = Mid(S, i, 1) y = Mid(a, j + 1, 1) If x = y Then q = 0 Next j If q = 1 Then a = a & x Next i
d = Len(a)
ReDim e(w To d, 0 To 1) AsString ReDim m(w To d, w To d) AsString
For i = w To d For j = w To d m(i, j) = 0 If j = w Then e(i, 0) = Mid(a, i, 1) e(i, 1) = 0 EndIf Next j Next i
For i = 1 To k - 1 l(0) = Mid(S, i, 1) l(1) = Mid(S, i + 1, 1) For j = w To d If l(0) = e(j, 0) Then e(j, 1) = Val(e(j, 1)) + 1 r = j EndIf If l(1) = e(j, 0) Then c = j Next j m(r, c) = Val(m(r, c)) + 1 Next i
tmp = "S="& S & vbCrLf & vbCrLf tmp = tmp & "The algorithm detected a total of "& (d - w + 1) & " states."& vbCrLf & vbCrLf tmp = tmp & MatrixPaint(w, d, m, a, "(C)", "Count:") & vbCrLf
For i = w To d For j = w To d If Val(e(i, 1)) > 0 Then m(i, j) = Val(m(i, j)) / Val(e(i, 1)) EndIf Next j Next i
tmp = tmp & MatrixPaint(w, d, m, a, "(P)", "Transition matrix M:")
OutPut.Text = tmp
EndFunction
Function MatrixPaint(w, d, ByVal m AsVariant, a, n, ByVal msg AsString) AsString
Dim e() AsString ReDim e(1 To d) AsString
d = Len(a) q = "| " h = "|_____|" l = vbCrLf
For i = (w - 1) To d If i = (w - 1) Then t = t & l & "." t = t & "_____." If i = d Then t = t & l & "| "& n & " | " Next i
For i = w To d e(i) = Mid(a, i, 1) t = t & e(i) & " | " h = h & "_____|" Next i
t = t & l & h & l
For i = w To d For j = w To d v = Round(m(i, j), 2) u = Mid(q, 1, Len(q) - Len(v)) If j = d Then o = "|"Else o = "" For b = w To d If j = w And i = b Then t = t & "| "& e(i) & "" EndIf Next b t = t & u & v & o Next j t = t & l & h & l Next i
MatrixPaint = msg & " M["& Val(d - w + 1) & ","& Val(d - w + 1) & "]"& l & t & l
Source: Gagniuc, Paul A. (2017). Markov Chains: From Theory to Implementation and Experimentation. USA, NJ: John Wiley & Sons. pp. 1–235. ISBN 978-1-119-38755-8.
For those interested in experimenting with Markov Chains, we found a new type of app implemented in four or five different programming languages, including VB6. The applications below represent a prediction and a simulator for different experiments related to Markov Chains, both developed by Dr. Paul A. Gagniuc for a math book (Gagniuc, Paul A. (2017). Markov Chains: From Theory to Implementation and Experimentation. USA, NJ: John Wiley & Sons. pp. 1–235. ISBN 978-1-119-38755-8.). These apps are available as binary files and source code.
Windows Information Provider Version 1.2.2 is an advanced app made by A_X_O (unfortunately I did not find his real name). It is a software that can teach you about everything inside the Windows 10 OS. This project is open source and can be modified, you can take parts of it as you need and you can ask A_X_O a little bit more about it (if he has the time to do so). You may wish to search for A_X_O on PSC/Internet, as he has published many advanced software as open source, made in the great VB6 programming language!
GCD (Greatest Common Divisor) of two numbers is the largest number that divides both of them. For example GCD of 20 and 28 is 4 and GCD of 98 and 56 is 14. In mathematics, the greatest common divisor (gcd) of two or more integers, which are not all zero, is the largest positive integer that divides each of the integers. For example, the gcd of 8 and 12 is 4.
Last year, researchers identified new crimeware, Loki-Bot, which steals data and login credentials. Loki-Bot is generally distributed through malicious spam, and is difficult to identify without getting into the malware. Loki-Bot crimeware targets Windows, in contrast to the recent Android banking ransomware, Loki Bot. While the crimeware is not as prevalent in the wild, it has some unique and differentiating characteristics.
Loki-Bot’s crypter is especially interesting and unique because it utilizes Visual Basic 6.0 to load multiple stages of shellcode to deliver the Loki-Bot payload. We saw an interesting sample highlighted by @DissectMalware on Twitter and decided to take a closer look for ourselves. We’ll walk through Loki-Bot’s crypter functionality, the first and second stage shellcodes, the payload, and then provide some thoughts on stopping these kinds of attacks and what we can expect to see next.
Loki-Bot Crypter Functionality
The main Visual Basic compiled binary uses a raw pointer access technique to jump into the first stage shellcode which then calls the second-stage shellcode that is executed off of the stack. The first stage shellcode disables data execution prevention (DEP) to make the stack executable, and uses jmp esp to jump into the stage 2 shellcode to allow it to begin execution. Stage 2 then sets up persistence, decrypts the payload, and executes the payload contents by using process hollowing. In this case the payload is Loki-Bot, crimeware designed to steal private information from a system. Loki-Bot’s functionality has already been covered in detail elsewhere, so we will instead focus on the mode of compromise and its anti-reversing engineering tactics.
DELIVERY CONTEXT
This particular malware sample began its life as an executable with an RTF exploit. Generally an RTF exploit is a specially crafted file that exploits vulnerabilities in the Rich Text Format parser of an application like Microsoft Word or Adobe Acrobat in order to gain code execution on the victim. The malware can then use this initial code execution to begin its exploit chain. Usually, crafted files are spread as attachments in phishing emails, which was the case in this sample. Once the malware gets code execution on the host it then downloads jazz.exe from the link below.
The binary was compiled with VB6.0 professional/enterprise and so contains normal x86asm with a dependency on msvbvm60.dll. Stage 1 makes specific use of the visual basic runtime DLL to make dll calls to other libraries.
First Stage Shellcode
The first stage shellcode exists within the VB6 portion of the malware, which we’ll refer to as the crypter. The first stage shellcode exists in the “rentegninger” sub module. The original sub module is then partially overwritten with obfuscated shellcode. The “Remanipulation8” public function is called from Load_Form(). This function manipulates the list of values of the Virtual Function Table returned by the “Me” reference to the form.
Set var_2 = Me
HOW TO GET THE SHELLCODE ENTRYPOINT
Stage 1 overwrites one of the variables in the compiled Visual Basic 6 code to point to an offset in the middle of the “rentegninger” submodule. The pointer is a hard coded integer that is calculated with division and a square root as shown below.
var_17 = 0x1526C77
new_value = var_17 \ CLng(Sqr((25)))
new_value = 0x43AF4B
UTILIZING STRPTR TO ACCESS ADDRESSES
In the code example below the pointer to the Me value points to the beginning of the Virtual Function Table of the class that Me points to. In this case the Class is a Form. The offset 0x2B0 is actually the function “Show”. The pointer to the show function is overwritten by the entry point of the shellcode which is 0x43AF4B. Then you can easily call “Form.Show” and call into your shellcode. An example of this is located in the Appendix.
var_num1 = StrPtr(var_2) + 2B0h
ReplacePtr(var_num1, new_value)
This value is later called in the “Remanipulation8” function as call dword ptr [eax+2B0h]. It treats this call as a method of the Me object. DispCallFunc .
STRING-TO-STACK METHOD
The crypter uses a common trick to get the strings that are inline with the assembly onto the stack. When a call is made, the next address gets pushed onto the stack as the return destination address. A disassembler will try to disassemble the string even though it never gets executed.
USING SHELL_NOTIFYICON
The first stage shellcode uses the Shell_NotifyIcon in a non-standard way. It passes an address off the stack that does not resemble a proper PNOTIFYICONDATA struct. The Windows API still processes the events as if they were normal. As you can see below, the Shell_TrayWnd icon is junk data for this process. It is called twice where NIM_ADD and NIM_DELETE are used.
UTILIZING THE PEB LOADER AND DLLFUNCTIONCALL
The first stage shellcode uses a common technique among other Windows shellcode to get a reference to DllFunctionCall by utilizing the Process Environment Block (PEB). The PEB is a data structure provided to every running process, and can be used to gain information about that process such as environment variables, image base addresses and DLL imports. This shellcode contains a PEB loader routine that gets a reference to msvbvm60.dll and then finds the offset of DllFunctionCall at 0x8D560CEC. Once it has the correct offset to DllFunctionCall, it can then use it to load Windows APIs so that it can make calls to them. More information on the PEB can be found here.
In a nutshell, the PEB is a linked list of offset values and the string names of the desired functions. You can linearly traverse the linked list, check for the desired function and save its offset if found.
DISABLING DATA EXECUTION PREVENTION (DEP)
The function ZwSetInformationProcess can be called with parameters -1 and 0x22 to turn off DEP for the process. DEP was originally intended to prevent programs from executing code on the stack, however DEP can also cause normal programs to crash without any notifications. Giving the program the ability to turn off DEP for itself allows the malware to avoid unknown crashes while also allowing malware authors to execute shellcode explicitly on the Stack. Microsoft Support provides additional details about DEP.
DECODING THE SHELLCODE
The stage 1 shellcode then utilizes JMP ESP to jump into that stack at the offset where the buffer for stage 2 shellcode was allocated. The stage 2 shellcode that was initially loaded onto the stack undergoes an initial pass of XOR decoding with an immediate value of 0x510473D1.
Second Stage Shellcode
SANDBOX EVASION
The stage 1 shellcode executes CPUID to detect if it’s being run in a virtual environment. If it is running in a virtual environment, it exits. If the stage 1 shellcode is not running in a virtual environment, then it continues execution normally. As detailed elsewhere, the malware sets the EAX register to 1, calls CPUID and then checks the 31st bit of the ECX register by applying a bitmask. If the 31st bit is 0 it knows it’s being run in a virtual environment.
Subsequently, the stage 1 shellcode calls the sleep function. Sleeping for prolonged durations of time is one evasion technique used by malware to subvert detection in sandboxed environments which are usually constrained by resource allocation to not run any given sample for longer than some established period of time, such as 30 seconds. Thus for the first 30 seconds of the program’s lifetime, it is benign and might fool some sandboxing environments.
ANTI-DEBUGGING USING NTYIELDEXECUTION
The ntdll function NtYieldExecution or its kernel32 equivalent SwitchToThread function allows the current thread to allocate the rest of the execution time, and allows the next scheduled thread to execute. If no threads are scheduled to execute or when the system is busy (and will not allow such a switch to occur), the ntdll NtYieldExecution() function returns a STATUS_NO_YIELD_PERFORMED (0x40000024) status code, which causes the kernel32 SwitchToThread function to return zero. When an application is being debugged, the act of single-stepping through the code causes debug events to occur and often results in no yield being allowed to occur. However, this is a hopelessly unreliable method of detecting the presence of a debugger because this method will also detect the presence of a thread that is running with high priority. An example of this code can be found here.
for (int i = 0; i < 0x20; i++)
{
Sleep(0xf);
if (NtYieldExecution() != STATUS_NO_YIELD_PERFORMED)
iDebugged++;
}
CHECK FOR ADAPTERS AND WINDOWS
The stage 2 shellcode calls VirtualAllocEX to populate a new memory region with GetAdaptersInfo. It checks the offset +10Ch of the struct for the Description. It then calls EnumWindows to check if the window has an empty string, most likely an attempt to detect execution within some sandbox.
PERSISTENCE AND PROCESS HOLLOWING
The stage 2 shellcode takes two routes. During the first route, the shellcode sets up persistent mechanisms with schtasks.exe. It then decrypts the payload with Xor and RC4 during the second route, creating a suspended process of itself and then hollows it out with the payload’s contents. Each route is explained below.
Route 1 (Persistence)
The stage 2 shellcode’s first route will acquire the hardcoded strings APPDATA=, TEMP=, and copied.exe in order to place a copy of itself in the %APPDATA% and %TEMP% locations as %AppData%\\Roaming\\copied.exe. Once the path is acquired, it will create a scheduled task to copy and run copied.exe using ShellExecuteA.
The stage 2 shellcode’s second route focuses on decrypting the executable payload into memory and then hollowing out a child process to execute the payload. “Wee2" is the marker on the stack in the shellcode and in the file that denotes the beginning of the copy operation. The format is Wee2<length of payload><key>.
Decrypting the Payload with Key
The key is stored in the shellcode after the marker and size in the shellcode. Its length is 0x100h, and is shown below.
The stage 2 shellcode uses the 0x100 sized byte key to xor the Loki-Bot payload of 0x34801 size then uses the same key to RC4 decrypt the product of the xor operation.
The shellcode then calls CreateProcessW to create a process of itself in suspended mode. It uses NTCreateFile, NTWriteVirtualMemory, NTUnMapViewofSection for process hollowing on the newly created process which contains the Loki-Bot payload. NtGetContextThread and NtSetContextThread and NtSetContextThread resume the process. The parent process terminates allowing the child to run as an orphan process. Because the Loki-Bot payload has already been reviewed by many researchers, we did not post the details about this malware.
Conclusion
There are a few key aspects to this crypter and its behaviour that make it fishy, including its crafty implementation of the VB6 runtime in shellcode, and use of anti-reverse engineering techniques and process hollowing. First, VB6 and the VB6 run time are rather old. While there are numerous binary distributions of software in the wild that were built with VB6 enterprise, it is still suspicious. Other suspicious activities include disabling its own DEP and checking if it’s being virtualized. Lastly, the crypter makes calls to the Windows API with malformed structs (i.e. the lack of an image for Shell_NotifyIcon). The combination of all of these suspicious activities could signal to a sensor like Endgame MalwareScoreTM that the program is up to no good, allowing us to stop it before the final execution occurs.
As for the future, we are likely to see more samples using legacy run times and features. Judging from this sample, a performant Visual Basic 6 crypter has recently been distributed in the wild. It seems natural that in the future its capabilities will improve and the volume of distribution will increase with continued black market adoption.
Appendix: Stage 1 Code Replication
After careful analysis, we were able to reproduce the method of Stage 1 shellcode execution in VB6. The code below will only display an empty message box.
VB6 CODE
Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" _
This app shows mapping of a rectangular image onto a quadrilateral, circle, cylinder, cone and a sphere. This implementation is made by Robert Rayment (I think is a mathematician if I am not mistaken - he influenced a few generations of programmers with his implementations).