Author Topic: Win64 Crypto API SHA1 MD5 (Example code)  (Read 10414 times)

Offline encryptor256

  • Full Member
  • **
  • Posts: 250
  • Country: lv
  • Win64 .
    • On Youtube: encryptor256
Win64 Crypto API SHA1 MD5 (Example code)
« on: October 25, 2013, 12:07:50 PM »
Hello!

Im planning to build some program where hash functions SHA1, MD5 are required.
So, i made some exploration, about it, and i found, that
there is no need - to make new bicycle (make algorithm again, by your self).
Win64 Crypto API provides a way to get hash string of data.

.Formal_description:

; ---------------------------------------------------------------------------
; This is: Win64 Crypto API SHA1 MD5 (Example code)
; Author: J.K. Encryptor256
; Date: October 25, 2013
; ---------------------------------------------------------------------------
; This example code will answer to
; Question: How to use Win64 Crypto API to get hash of data?
; ---------------------------------------------------------------------------
; Using:
;
; 1. The Netwide Assembler, NASM (http://nasm.us/)
;        The Netwide Assembler, NASM: It is an 80x86 and x86-64 assembler
;        designed for portability and modularity.
;
; 2. Minimalist GNU for Windows, MinGW64 (http://mingw-w64.sourceforge.net/)
;        Mingw-w64: It delivers runtime, headers and libs for developing
;        both, 64 bit (x64) and  32 bit (x86) windows applications, using GCC and
;        other free software compilers.
;
;        GCC is a part of MINGW64 toolset.
;
; 3. GoLink, (http://www.godevtool.com/)
;
; ---------------------------------------------------------------------------
; How to compile:
;
; Like this, in the way i did:
;
; 1. NASM: "nasm.exe main.asm -f win64 -o main.obj"
; Output obj size: 2.86 KB (2,930 bytes)
;
; Choose your linker:
;
; 2.1. GCC: "gcc.exe -m64 main.obj -o main.exe"
; Output exe size: 46.8 KB (47,958 bytes)
;
; 2.2. GoLink: "golink.exe /entry main main.obj MSVCRT.dll kernel32.dll user32.dll advapi32.dll"
; Output exe size: 3.00 KB (3,072 bytes)
;
; ---------------------------------------------------------------------------
; More information, about SHA1 and MD5, you can find:
;
; 1. SHA-1:
;   " http://en.wikipedia.org/wiki/SHA-1"
;
; 2. MD5:
;   "http://en.wikipedia.org/wiki/MD5"
;
; 3. US Secure Hash Algorithm 1 (SHA1):
;   "http://www.ietf.org/rfc/rfc3174.txt"
; This link NR. 3 is very good for SHA1, there is also C source code.
; ---------------------------------------------------------------------------


Example code is organized into three files, one of them is main compile file, other two are just an include files.
  • main.asm (Main compile file)
  • macros.asm
  • sha1.asm

Include file, named sha1.asm, contains:
; PROCEDURES
; 1. getHash - get's hash string of data
; 2. printHex - print's hash string, displays a message box

Include file, named macros.asm, contains:
; MACROS
; 1. Invoke
; 2. Invoke_error
; 3. Invoke_returns
; 4. stringTable

.How_to_use:

Content of main.asm file:

Code: [Select]
; ---------------------------------------------------------------------------
; Tell compiler to generate 64 bit code
; ---------------------------------------------------------------------------
bits 64
%include "macros.asm"
%include "sha1.asm"

; ---------------------------------------------------------------------------
; Data segment:
; ---------------------------------------------------------------------------
section .data use64

stringTable txt_buffer,"BufferBufferBufferBufferBufferBufferBufferBufferBufferBufferBuffer"

stringTable txt_error_code,"Error code: %d"

stringTable txt_text,"The quick brown fox jumps over the lazy dog", \
txt_sha1,"SHA1: should be '2fd4e1c6 7a2d28fc ed849ee1 bb76e739 1b93eb12'", \
txt_md5,"MD5: should be '9e107d9d372bb6826bd81d3542a419d6'"

ptrToHashData: dq 0

; ---------------------------------------------------------------------------
; Bss segment:
; ---------------------------------------------------------------------------
section .bss use64

; ---------------------------------------------------------------------------
; Code segment:
; ---------------------------------------------------------------------------
section .text use64

global main
extern sprintf
extern MessageBoxA
extern ExitProcess
extern strlen
extern malloc
extern GetLastError
extern free

main:
; -----------------------------------------------------------------------------
; Allocate stack memory
; -----------------------------------------------------------------------------
sub rsp,8*17

; -----------------------------------------------------------------------------
; 1. ## Let's test SHA1 of data: "The quick brown fox jumps over the lazy dog"
; -----------------------------------------------------------------------------

; Get len of the string
Invoke strlen,txt_text

; Invoke getHash
Invoke getHash,CALG_SHA1,txt_text,rax,ptrToHashData

; Check expected result, should be 20 of len
Invoke_error je,20,main.getSHA1_Success
Invoke GetLastError
Invoke sprintf,txt_buffer,txt_error_code,rax
Invoke MessageBoxA,0,txt_buffer,0,0
Invoke ExitProcess,0

main.getSHA1_Success:

; printHex
Invoke printHex,[ptrToHashData],20,txt_sha1

; -----------------------------------------------------------------------------
; Free allocated hash data
; -----------------------------------------------------------------------------
Invoke free,[ptrToHashData]

; -----------------------------------------------------------------------------
; 2. ## Let's test MD5 of data: "The quick brown fox jumps over the lazy dog"
; -----------------------------------------------------------------------------

; Get len of the string
Invoke strlen,txt_text

; Invoke getHash
Invoke getHash,CALG_MD5,txt_text,rax,ptrToHashData

; Check expected result, should be 16 of len
Invoke_error je,16,main.getMD5_Success
Invoke GetLastError
Invoke sprintf,txt_buffer,txt_error_code,rax
Invoke MessageBoxA,0,txt_buffer,0,0
Invoke ExitProcess,0

main.getMD5_Success:

; printHex
Invoke printHex,[ptrToHashData],16,txt_md5

; -----------------------------------------------------------------------------
; Free allocated hash data
; -----------------------------------------------------------------------------
Invoke free,[ptrToHashData]

; -----------------------------------------------------------------------------
; Quit
; -----------------------------------------------------------------------------
add rsp,8*17
Invoke ExitProcess,0
ret

Content's of both include files you can find at attachment.

The first test of Win64 Crypto API i made in C,
because i wanted to be sure, that this Crypto API actually works, and it did.

If you don't like my source code you can build your own version, derive from C language, here is some info:

1. I modified and builded a working version from this link, named:
"Example C Program: Creating an HMAC"
http://msdn.microsoft.com/en-us/library/windows/desktop/aa382379(v=vs.85).aspx

2. Or, you just can have my final C version (a bit dirty):

Code: [Select]
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <wincrypt.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define uCALG_MD2 (32769)
#define uCALG_MD4 (32770)
#define uCALG_MD5 (32771)
#define uCALG_SHA1 (32772)

int getHash(unsigned int tagType, void * tagData, unsigned int tagDataLen, void ** tagDataOut)
{
HCRYPTPROV  hProv       = 0;
HCRYPTHASH  hHash       = 0;
DWORD       dwDataLen   = 0;
DWORD dwResult = 0;

if(0!=CryptAcquireContext(&hProv,NULL,NULL,PROV_RSA_FULL,CRYPT_VERIFYCONTEXT))
{
if(0!=CryptCreateHash(hProv,tagType,0,0,&hHash))
{
if (0!=CryptHashData(hHash,(CONST BYTE*)tagData,tagDataLen,0))
{   
if(0!=CryptGetHashParam(hHash,HP_HASHVAL,NULL,&dwDataLen,0))
{
(*tagDataOut)=malloc(dwDataLen);
if (0!=CryptGetHashParam(hHash,HP_HASHVAL,(BYTE*)(*tagDataOut),&dwDataLen,0))
{
dwResult=dwDataLen;
}
else
{
free((*tagDataOut));
};
};
};
};
};


    if(hHash)
        CryptDestroyHash(hHash); 
 
    if(hProv)
        CryptReleaseContext(hProv, 0);

return dwResult;
};



int PASCAL WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszCmdLine, int nCmdShow)
{
/*
; -----------------------------------------------------
; SHA1("The quick brown fox jumps over the lazy dog")
; = 2fd4e1c6 7a2d28fc ed849ee1 bb76e739 1b93eb12
; -----------------------------------------------------
; MD5("The quick brown fox jumps over the lazy dog")
; = 9e107d9d372bb6826bd81d3542a419d6
; -----------------------------------------------------
*/
char buffz1[231] = "";
sprintf(buffz1,"%d",'f');
MessageBox(0,buffz1,0,0);

char str[] = {"The quick brown fox jumps over the lazy dog"};
unsigned char * out;
int len = getHash(CALG_SHA1,str,strlen(str),(void*)&out);

if(len==0)
MessageBox(0,0,0,0);

char buffz[231] = "";
for(DWORD i = 0 ; i < len ; i++)
{
sprintf(buffz,"%s%2.2x",buffz,out[i]);
}
MessageBox(0,buffz,0,0);

return 0;
}

.Attachment: "win64_crypto_api_sha1_md5.zip"
There is no C included, only main topic files: main.asm, macros.asm, sha1.asm, main.obj, main.exe.

 
And that's it,
Encryptor256!
Encryptor256's Investigation \ Research Department.

Offline Gunner

  • Jr. Member
  • *
  • Posts: 74
  • Country: us
    • Gunners Software
Re: Win64 Crypto API SHA1 MD5 (Example code)
« Reply #1 on: October 26, 2013, 02:45:31 AM »
GoLink has a nifty feature where you do not have to put all the dlls you are linking against on the command line.  Instead, you put them all in a file, 1 dll per line and save it as whatever name you want.  I usually name it, imports.inc.  Then use the @ symbol on the command line:

golink.exe /entry main main.obj @imports.inc
An GoLink will read in that file and use the dlls contained in it.  This helps keep the command line neat and tidy.

imports.inc
MSVCRT.dll
kernel32.dll
user32.dll
advapi32.dll


This comes in handy when you are linking against MANY libraries like GTK+

Offline encryptor256

  • Full Member
  • **
  • Posts: 250
  • Country: lv
  • Win64 .
    • On Youtube: encryptor256
Re: Win64 Crypto API SHA1 MD5 (Example code)
« Reply #2 on: October 26, 2013, 06:02:18 AM »
GoLink has a nifty feature where you do not have to put all the dlls you are linking against on the command line.  Instead, you put them all in a file, 1 dll per line and save it as whatever name you want.  I usually name it, imports.inc.  Then use the @ symbol on the command line:

golink.exe /entry main main.obj @imports.inc
An GoLink will read in that file and use the dlls contained in it.  This helps keep the command line neat and tidy.

imports.inc
MSVCRT.dll
kernel32.dll
user32.dll
advapi32.dll


This comes in handy when you are linking against MANY libraries like GTK+

Cool, didn't knew that! :)
Encryptor256's Investigation \ Research Department.

Offline Bryant Keller

  • Forum Moderator
  • Full Member
  • *****
  • Posts: 360
  • Country: us
    • About Bryant Keller
Re: Win64 Crypto API SHA1 MD5 (Example code)
« Reply #3 on: October 28, 2013, 03:10:38 AM »
GoLink has a lot of really nifty features like that. GoASM supports a directive called #DYNAMICLINKFILE that allows you to specify the libraries that GoLink uses in your source code. I've discussed this in an older post. It explains how to implement the same feature using NASM macros. Also, don't just create a really large "imports" file to use with everything. If you are going to do like Gunner does, make sure you create one for each project. Last time I used it, GoLINK would generate an entry in the IAT for each DLL you specify, whether you use it or not, when you pass them like that. It could cause your executable to become unnecessarily bloated.

About Bryant Keller
bkeller@about.me

Offline encryptor256

  • Full Member
  • **
  • Posts: 250
  • Country: lv
  • Win64 .
    • On Youtube: encryptor256
Re: Win64 Crypto API SHA1 MD5 (Example code)
« Reply #4 on: October 28, 2013, 09:12:28 AM »
GoLink has a lot of really nifty features like that. GoASM supports a directive called #DYNAMICLINKFILE that allows you to specify the libraries that GoLink uses in your source code. I've discussed this in an older post. It explains how to implement the same feature using NASM macros. Also, don't just create a really large "imports" file to use with everything. If you are going to do like Gunner does, make sure you create one for each project. Last time I used it, GoLINK would generate an entry in the IAT for each DLL you specify, whether you use it or not, when you pass them like that. It could cause your executable to become unnecessarily bloated.

Cool, thanks for the, additional info, will try it out! :)
Encryptor256's Investigation \ Research Department.

Offline encryptor256

  • Full Member
  • **
  • Posts: 250
  • Country: lv
  • Win64 .
    • On Youtube: encryptor256
Re: Win64 Crypto API SHA1 MD5 (Example code)
« Reply #5 on: November 02, 2013, 11:30:27 AM »
Hi, reader!

  • Recently i got sickness with carry flag's,
    i found that, it's a cheap way to store function final result -> error or success -> jc or jnc,
    and rax, can be used for other things, like returning some extra value.

So, now, im building functions like that and this:

I updated getHash into cmdHash.

Carry's flag version:

; ###################################################### ;
; cmdHash - Get hash sum of buffer
;
;   Usage:
;      ...
;      txt_text: db "The quick brown fox jumps over the lazy dog",0
;      ...
;      mov rcx,qword txt_text
;      call strlen
;      ...
;      mov rcx,qword CALG_SHA1
;      mov rdx,qword txt_text
;      mov r8,rax
;      call cmdHash
;
; Returns, pointer to buffer, returned in rax.
; Returns, buffer len, returned in rcx.
; Set's carry flag on error.
; Clear's carry flag on success.
;
; ###################################################### ;

Code: [Select]
cmdHash:
mov qword [rsp+8*1],rcx ; Hash type: like CALG_SHA1,...
mov qword [rsp+8*2],rdx ; Buffer address
mov qword [rsp+8*3],r8 ; Buffer len
mov qword [rsp+8*4],qword 0 ; Unused
; Check arguments
cmp rcx,qword 0
je .badArguments
cmp rdx,qword 0
je .badArguments
cmp r8,qword 0
jne .goodArguments
.badArguments:
stc
ret
.goodArguments:
; Allocate stack space
; rsp+8*0 => arg 0;
; rsp+8*1 => arg 1;
; rsp+8*2 => arg 2;
; rsp+8*3 => arg 3;
; rsp+8*4 => arg 4;
; rsp+8*5 => tmp 0;hProv
; rsp+8*6 => tmp 1;hHash
; rsp+8*7 => tmp 2;dwDataLen
; rsp+8*8 => tmp 3;bufferAddress
sub rsp,qword 8*9
; CryptAcquireContextA
lea rcx,[rsp+8*5]
xor rdx,rdx
xor r8,r8
mov r9,qword PROV_RSA_FULL
mov rax,qword CRYPT_VERIFYCONTEXT
mov qword [rsp+8*4],rax
call CryptAcquireContextA
cmp rax,qword 0
jne .step0
add rsp,qword 8*9
stc
ret
.step0:
; CryptCreateHash
mov rcx,qword [rsp+8*5]
mov rdx,qword [rsp+8*9+8*1]
xor r8,r8
xor r9,r9
lea rax,[rsp+8*6]
mov qword [rsp+8*4],rax
call CryptCreateHash
cmp rax,qword 0
jne .step1
mov rcx,qword [rsp+8*5]
xor rdx,rdx
call CryptReleaseContext
add rsp,qword 8*11
stc
ret
.step1:
; CryptHashData
mov rcx,qword [rsp+8*6]
mov rdx,qword [rsp+8*9+8*2]
mov r8,qword [rsp+8*9+8*3]
xor r9,r9
call CryptHashData
cmp rax,qword 0
jne .step2
mov rcx,qword [rsp+8*6]
call CryptDestroyHash
mov rcx,qword [rsp+8*5]
xor rdx,rdx
call CryptReleaseContext
add rsp,qword 8*9
stc
ret
.step2:
; CryptGetHashParam
mov rcx,qword [rsp+8*6]
mov rdx,qword HP_HASHVAL
xor r8,r8
lea r9,[rsp+8*7]
mov qword [rsp+8*4],qword 0
call CryptGetHashParam
cmp rax,qword 0
jne .step3
mov rcx,qword [rsp+8*6]
call CryptDestroyHash
mov rcx,qword [rsp+8*5]
xor rdx,rdx
call CryptReleaseContext
add rsp,qword 8*9
stc
ret
.step3:
; malloc
mov rcx,qword [rsp+8*9+8*3]
call malloc
cmp rax,qword 0
jne .step4
mov rcx,qword [rsp+8*6]
call CryptDestroyHash
mov rcx,qword [rsp+8*5]
xor rdx,rdx
call CryptReleaseContext
add rsp,qword 8*9
stc
ret
.step4:
mov qword [rsp+8*8],rax
; CryptGetHashParam
mov rcx,qword [rsp+8*6]
mov rdx,qword HP_HASHVAL
mov r8,rax
lea r9,[rsp+8*7]
mov qword [rsp+8*4],qword 0
call CryptGetHashParam
cmp rax,qword 0
jne .step5
mov rcx,qword [rsp+8*8]
call free
mov rcx,qword [rsp+8*6]
call CryptDestroyHash
mov rcx,qword [rsp+8*5]
xor rdx,rdx
call CryptReleaseContext
add rsp,qword 8*9
stc
ret
.step5:
mov rcx,qword [rsp+8*6]
call CryptDestroyHash
mov rcx,qword [rsp+8*5]
xor rdx,rdx
call CryptReleaseContext
mov rax,qword [rsp+8*8]
mov rcx,qword [rsp+8*7]
add rsp,qword 8*9
clc
ret

Returned => Not A Darn Thing, oh, sorry, actually returned something:
Code: [Select]
2FD4E1C67A2D28FCED849EE1BB76E7391B93EB12

And that's it!
So, now i can return to this post and copy paste the code, when ever i need. :)

Encryptor256!!!
Encryptor256's Investigation \ Research Department.

Offline encryptor256

  • Full Member
  • **
  • Posts: 250
  • Country: lv
  • Win64 .
    • On Youtube: encryptor256
Re: Win64 Crypto API SHA1 MD5 (Example code)
« Reply #6 on: November 17, 2013, 10:25:21 AM »
Created a new version.

This time, I used more brains in creating this.

Im planning to build a torrent client, so that's why updates and changes are needed.

There are prefix "sys", which means, that, these functions must go in a library named sys.obj.

Library "sys.obj" follows two rules:
  • Each procedure is independent.
  • or it can depend only on library "sys.obj" procedures (kernel, os, ... procedures doesn't count).


 :)

; -----------------------------------------------------------;
;
; Procedure: sysAllocateMemory
;
; Usage:
;           mov rcx,qword (2048)
;           call sysAllocateMemory
;           jc .quitError
;
;           mov rcx,rax
;           call sysFreeMemory
;           jc .quitError
;
;
; Returns a pointer to buffer into RAX.
; Set's carry flag on error.
; Clear's carry flag on success.
;
; -----------------------------------------------------------;
;
; Procedure: sysFreeMemory
;
; Usage:
;           mov rcx,qword (2048)
;           call sysAllocateMemory
;           jc .quitError
;
;           mov rcx,rax
;           call sysFreeMemory
;           jc .quitError
;
;
; Returns a pointer to buffer into RAX.
; Set's carry flag on error.
; Clear's carry flag on success.
;
; -----------------------------------------------------------;
;
; Procedure: sysCheckMemory
;
; This procedure will check if,
; procedure count of sysAllocateMemory is equals to,
; procedure count of sysFreeMemory.
; If they are equal, it clears carry flag.
;
; Usage:
;           call sysCheckMemory
;           jc .quitMemoryError
;
; Returns procedure count into RAX.
; Set's carry flag on error.
; Clear's carry flag on success.
;
; -----------------------------------------------------------;
;
; Procedure: sysHash
;
; Usage:
;      ...
;      txt_text: db "The quick brown fox jumps over the lazy dog",0
;      ...
;      mov rcx,qword txt_text
;      call strlen
;      ...
;      mov rcx,qword CALG_SHA1
;      mov rdx,qword txt_text
;      mov r8,rax
;      call sysHash
;           jc .quit
;
;
; Returns, pointer to buffer, returned in rax.
; Returns, buffer len, returned in rcx.
; Set's carry flag on error.
; Clear's carry flag on success.
;
; -----------------------------------------------------------;


; =>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=> ;
; =>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=> ;
; =>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=> ;
; Imports n Exports:
; =>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=> ;
; =>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=> ;
; =>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=> ;


      extern malloc, memset, free

      global sysAllocateMemory, sysFreeMemory, sysCheckMemory

      global sysHash
      extern CryptAcquireContextA, CryptCreateHash, CryptHashData, \
            CryptGetHashParam, CryptGetHashParam, CryptDestroyHash, \
            CryptDestroyHash, CryptReleaseContext

; =>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=> ;
; =>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=> ;
; =>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=> ;
; Constants:
; =>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=> ;
; =>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=> ;
; =>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=> ;



      ; sysHash:
      ; ----------------------------------------------------- ;
      CRYPT_VERIFYCONTEXT equ 0xF0000000
      PROV_RSA_FULL equ 1
      HP_HASHVAL equ 0x0002
      CALG_MD2 equ 32769
      CALG_MD4 equ 32770
      CALG_MD5 equ 32771
      CALG_SHA1 equ 32772

; =>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=> ;
; =>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=> ;
; =>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=> ;
; Section: DATA
; =>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=> ;
; =>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=> ;
; =>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=> ;


Code: [Select]

section .data use64

      ; sysAllocateMemory, sysFreeMemory, sysCheckMemory:
      ; ----------------------------------------------------- ;
      sysAllocateNFreeMemory_COUNT: dq 0



; =>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=> ;
; =>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=> ;
; =>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=> ;
; Section: CODE
; =>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=> ;
; =>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=> ;
; =>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=> ;


Code: [Select]
; -----------------------------------------------------------;
;
; Procedure: sysAllocateMemory
;
; -----------------------------------------------------------;

sysAllocateMemory:

      ; Check incoming argument
      cmp rcx,qword (0)
      jg .goodArg
      stc
      ret

.goodArg:

      ; Create stack
      push rbp
      mov rbp,rsp
      push rbx
      lea rsp,[rsp-(8*5)]

      ; Save "byte count" into RBX
      mov rbx,rcx

      ; Allocate memory
      ; rcx remains set
      call malloc

      ; Check for error
      cmp rax,qword (0)
      jne .gotMemory
      stc
      jmp .quit

.gotMemory:

      ; Restore "byte count" into r8
      mov r8,rbx

      ; Save mem pointer into RBX
      mov rbx,rax

      ; Init memory
      mov rcx,rbx
      xor rdx,rdx
      ; r8 remains set
      call memset

      ; Set return value, restore mem pointer from RBX
      mov rax,rbx

      ; STATISTICS
      inc qword [sysAllocateNFreeMemory_COUNT]

      ; Set success
      clc

      ; Clean stack n quit
.quit:
      lea rsp,[rsp+(8*5)]
      pop rbx
      pop rbp
      ret



; -----------------------------------------------------------;
;
; Procedure: sysFreeMemory
;
; -----------------------------------------------------------;

sysFreeMemory:

      ; Check incoming argument
      cmp rcx,qword (0)
      je .earlyQuit

      ; STATISTICS
      cmp qword [sysAllocateNFreeMemory_COUNT],qword (0)
      jg .proceed

.earlyQuit:
      stc
      ret

.proceed:

      ; Create stack
      push rbp
      mov rbp,rsp
      lea rsp,[rsp-(8*4)]

      ; Free memory
      ; rcx remains set
      call free

      ; STATISTICS
      dec qword [sysAllocateNFreeMemory_COUNT]

      ; Set success
      clc

      ; Clean stack n quit
.quit:
      lea rsp,[rsp+(8*4)]
      pop rbp
      ret



; -----------------------------------------------------------;
;
; Procedure: sysCheckMemory
;
; -----------------------------------------------------------;
sysCheckMemory:
      mov rax,qword [sysAllocateNFreeMemory_COUNT]
      cmp rax,qword (0)
      je .proceed
      stc
.proceed:
      clc
      ret



; -----------------------------------------------------------;
;
; Procedure: sysHash
;
; -----------------------------------------------------------;

;int getHash(unsigned int tagType, void * tagData, unsigned int tagDataLen, void ** tagDataOut)
;{
; HCRYPTPROV  hProv       = 0;
; HCRYPTHASH  hHash       = 0;
; DWORD       dwDataLen   = 0;
; DWORD dwResult = 0;
;
; if(0!=CryptAcquireContext(&hProv,NULL,NULL,PROV_RSA_FULL,CRYPT_VERIFYCONTEXT))
; {
; if(0!=CryptCreateHash(hProv,tagType,0,0,&hHash))
; {
; if (0!=CryptHashData(hHash,(CONST BYTE*)tagData,tagDataLen,0))
; {
; if(0!=CryptGetHashParam(hHash,HP_HASHVAL,NULL,&dwDataLen,0))
; {
; (*tagDataOut)=malloc(dwDataLen);
; if (0!=CryptGetHashParam(hHash,HP_HASHVAL,(BYTE*)(*tagDataOut),&dwDataLen,0))
; {
; dwResult=dwDataLen;
; }
; else
; {
; free((*tagDataOut));
; };
; };
; };
; };
; };
;
;
;    if(hHash)
;        CryptDestroyHash(hHash);
;
;    if(hProv)
;        CryptReleaseContext(hProv, 0);
;
; return dwResult;
;};

sysHash:

      ; Check args
      cmp rcx,qword (0)
      je .earlyQuit
      cmp rdx,qword (0)
      je .earlyQuit
      cmp r8,qword (0)
      jne .proceed

.earlyQuit:
      stc
      ret

.proceed:

      ; Save registers
mov qword [rsp+8*1],rcx ; Hash type: like CALG_SHA1,...
mov qword [rsp+8*2],rdx ; Buffer address
mov qword [rsp+8*3],r8 ; Buffer len

; Stack space map:
; rsp+8*0 => arg 0;
; rsp+8*1 => arg 1;
; rsp+8*2 => arg 2;
; rsp+8*3 => arg 3;
; rsp+8*4 => arg 4;
; rsp+8*5 => tmp 0;hProv
; rsp+8*6 => tmp 1;hHash
; rsp+8*7 => tmp 2;dwDataLen
; rbx => tmp 3;bufferAddress
; r12 => tmp 4;zero on success, nzero on error

      ; Create stack
      push rbp
      mov rbp,rsp
      push rbx
      push r12
lea rsp,[rsp-(8*8)]

      ; Init error indicator register
      xor r12,r12

      ; Init buffer address register
      xor rbx,rbx

      ; if(0!=CryptAcquireContext(&hProv,NULL,NULL,PROV_RSA_FULL,CRYPT_VERIFYCONTEXT))
      lea rcx,[rsp+8*5]
xor rdx,rdx
xor r8,r8
mov r9,qword (PROV_RSA_FULL)
mov rax,qword (CRYPT_VERIFYCONTEXT)
mov qword [rsp+8*4],rax
call CryptAcquireContextA
cmp rax,qword (0)
je .quitError

      ; if(0!=CryptCreateHash(hProv,tagType,0,0,&hHash))
      mov rcx,qword [rsp+8*5]
mov rdx,qword [rsp+8*8+8*3+8*1]
xor r8,r8
xor r9,r9
lea rax,[rsp+8*6]
mov qword [rsp+8*4],rax
call CryptCreateHash
cmp rax,qword (0)
je .quitError

      ; if (0!=CryptHashData(hHash,(CONST BYTE*)tagData,tagDataLen,0))
      mov rcx,qword [rsp+8*6]
mov rdx,qword [rsp+8*8+8*3+8*2]
mov r8,qword [rsp+8*8+8*3+8*3]
xor r9,r9
call CryptHashData
cmp rax,qword (0)
je .quitError

      ; if(0!=CryptGetHashParam(hHash,HP_HASHVAL,NULL,&dwDataLen,0))
      mov rcx,qword [rsp+8*6]
mov rdx,qword HP_HASHVAL
xor r8,r8
lea r9,[rsp+8*7]
mov qword [rsp+8*4],qword 0
call CryptGetHashParam
cmp rax,qword (0)
je .quitError

      ; (*tagDataOut)=malloc(dwDataLen);
      mov rcx,qword [rsp+8*7]
call sysAllocateMemory
cmp rax,qword (0)
je .quitError

      ; Assign memory address to RBX
      mov rbx,rax

      ; if (0!=CryptGetHashParam(hHash,HP_HASHVAL,(BYTE*)(*tagDataOut),&dwDataLen,0))
mov rcx,qword [rsp+8*6]
mov rdx,qword HP_HASHVAL
mov r8,rbx
lea r9,[rsp+8*7]
mov qword [rsp+8*4],qword 0
call CryptGetHashParam
cmp rax,qword (0)
  jne .quitSuccess

      ; ~Error:
.quitError:

      ; Set Error indicator
      sete r12b

      ; ~Success:
.quitSuccess:

      ; Release
.release0:

      ; CryptDestroyHash
mov rcx,qword [rsp+8*6]
      cmp rcx,qword (0)
      je .release1
call CryptDestroyHash
.release1:

      ; CryptReleaseContext
mov rcx,qword [rsp+8*5]
      cmp rcx,qword (0)
      je .releaseDone
xor rdx,rdx
call CryptReleaseContext
.releaseDone:

      ; Check for error:
      cmp r12b,byte (0)
      jne .onError
.onSuccess:

      ; Set return values
      mov rax,rbx
      mov rcx,qword [rsp+8*7]

      ; Clear carry flag
      clc

      jmp .finalize
.onError:

      ; Release allocated buffer
      cmp rbx,qword (0)
      je .noBuffer
      mov rcx,rbx
      call sysFreeMemory
.noBuffer:

      ; Set carry flag
      stc

.finalize:
      ; Clear stack n quit
lea rsp,[rsp+(8*8)]
      pop r12
      pop rbx
      pop rbp
      ret

Bye,
Encryptor256!!!
« Last Edit: November 17, 2013, 10:29:12 AM by encryptor256 »
Encryptor256's Investigation \ Research Department.

Offline nasm32

  • Jr. Member
  • *
  • Posts: 25
Re: Win64 Crypto API SHA1 MD5 (Example code)
« Reply #7 on: September 15, 2014, 11:41:20 AM »
Golink command files are useful but it doesn't seem to work with environment variables.

This will work:

kernel32.dll
user32.dll
x:\library\myobject.obj

This will not work:

%INCLUDE%\library\myobject.obj

The right combination i've found to make nasm, golink and gnu make work together without corrupting the file path's is to:

1: All externs should be put in a separate file.inc and included directly into the .asm file, one shouldn't put externs directly into the asm file manually.
2: The gnu make file should have an "added command" so you can add externs to that separate include file by simple means of: make addobj objname and you can put environmental variables inside the make program, gnu make supports them. And automatically text replace the object name with its environmental path and also add the extern directive plus that you can add it to the golink command file at the same time.
3: All programs you make should use a general include file to include other files. In the "super" include file you put the correct paths to all your folders, and in the secondary include file is what you include in your .asm file. You only need to change the paths in the general include file if you change the folder structure. So if you change the general file, you also change all of your programs include files automatically.

You might also want to sort the golink @command file before linking, it will merge all objects in the precise order you put them in the command file. Put the most frequently used code at the top, near one another and the less frequently used code at the bottom. The top portion will appear near the beginning of the code, which is page aligned and get the benefit of that alignment and the objects that rely in one another should be near each other. By having most frequently used code in a linear fashion at the top reduces the chance they get flushed out of cache when needed. Keep "looping" or "most frequently used" object files at the top of the command file near one another.

The same is true for data. If you have a variable that you will access only one time, move it out of that section into a "less frequently used" storage place. If you access one variable, you order the whole cacheline from ram. Keep frequently used variables in one section, and one-time only variables in another.

Or in donald duck language: You have 512 very small rooms in your house. Each room can store 64 items. If you put 63 garbage items in room 1 and put 1 useful item beside those 63 garbage items, it's a waste of time and storage when you need to get to the one useful item, you have to take out all the 63 garbage items from the room before you can get to the one useful item.

But if you put 64 garbage items in one room, you probably never need to open up the door to that room, and time and storage is saved. By keeping the room full of only useful items, when you need to get to one useful item, you get the benefit of getting all the other useful items in one go, and time is saved.

Here is an example of a room filled with both garbage and useful items:

.data

wnd WNDCLASSEX   (WNDCLASSEX structure consumes 48 bytes) and you only need this structure once.
msg MSG                (MSG structure is also needed in windows, but you need access to this all the time (frequently)

The MSG structure consumes 24 bytes. 48 + 24 = 72 bytes. You consume two cachelines by having them near one another. (64 byte=one line, 72 MOD 64 = 8 (it crossed the boundary to the next cacheline and both are used)

Just keep that in mind when declaring data. If you access only a single byte, a single byte, the entire cacheline of 64 bytes is fetched from the extremely slow RAM and into the cache hierarchy. It makes sense to count the size of the data section to see that useful items fits within its own linearly organized cachelines.

To a programmer it makes sense to have relative data elements near one another (They belong to one another) but in a technical perspective it is not right. You should move WNDCLASSEX out of there and leave only MSG in it.

This is btw one of the major problems of HLL languages and OOP where they organize garbage and useful items in large objects and multiply this bad habit on a permanent scale all over the program. Cache utilization is probably 4-500% that of an equivalent c++ program.

Any HLL programmer thinks he has unlimited resources, of course he is wrong. He has exactly 512 lines of 64 bytes (in a high tech modern cpu) each, how are you going to spend those extremely rare bytes.

Just to give a perspective on this, this code here consumes one cacheline, 1 out of 512 available cachelines.

Code: [Select]
push 0040203b
push 0040203b
push 0040203b
push 0040203b
push 0040203b
push 0040203b
push 0040203b
push 0040203b
push 0040203b
push 0040203b
push 0040203b
push 0040203b
push ecx
push ecx
push ecx
push ecx

And this will consume 1 out of 512 cache lines of data

Code: [Select]
  resb 64

Obviously  :D
« Last Edit: September 15, 2014, 01:46:41 PM by nasm32 »