Skip to content

Commit

Permalink
initial version
Browse files Browse the repository at this point in the history
  • Loading branch information
ayoubfaouzi committed Jul 18, 2021
1 parent 1d344a6 commit 8ebb2fa
Show file tree
Hide file tree
Showing 12 changed files with 1,699 additions and 2 deletions.
21 changes: 21 additions & 0 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "Launch",
"type": "go",
"request": "launch",
"mode": "auto",
"program": "${fileDirname}",
"env": {},
"args": ["-sdk", "C:\\Program Files (x86)\\Windows Kits\\10\\Include\\10.0.19041.0\\",
// "--printretval",
// "--printanno",
"--minify",
],
}
]
}
41 changes: 39 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,39 @@
# winsdk2json
Windows SDK parsed to JSON
# Windows SDK API definition in JSON

sdk2json is a go package that parses the Windows SDK (prototypes, structures, unions) to JSON format.

Here is an example:

```json
{
"advapi32.dll": {
"ControlService": {
"callconv": "WINAPI",
"name": "ControlService",
"retVal": "BOOL",
"params": [
{
"anno": "_In_",
"type": "SC_HANDLE",
"name": "hService"
},
{
"anno": "_In_",
"type": "DWORD",
"name": "dwControl"
},
{
"anno": "_Out_",
"type": "LPSERVICE_STATUS",
"name": "lpServiceStatus"
}
]
},
```

The malware sandbox hooking module makes use of this to implement a generic hook handler that does not require to implement a handler for each API we need to hook.


## Challenges

- Not a consistent way of defines functions prototypes or structs.
112 changes: 112 additions & 0 deletions api.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
// Copyright 2021 Saferwall. All rights reserved.
// Use of this source code is governed by Apache v2 license
// license that can be found in the LICENSE file.

package main

import (
"log"

"regexp"
"strings"
)

const (
// RegAPIs is a regex that extract API prototypes.
RegAPIs = `(_Success_|HANDLE|INTERNETAPI|WINHTTPAPI|BOOLAPI|BOOL|STDAPI|WINUSERAPI|WINBASEAPI|WINADVAPI|NTSTATUS|_Must_inspect_result_|BOOLEAN|int)[\w\s\)\(,\[\]\!*+=&<>/|]+;`

// RegProto extracts API information.
RegProto = `(?P<Attr>WINBASEAPI|WINADVAPI|WSAAPI)?( )?(?P<RetValType>[A-Za-z]+) (?P<CallConv>WINAPI|APIENTRY|WSAAPI) (?P<ApiName>[a-zA-Z0-9]+)( )?\((?P<Params>.*)\);`

// RegAPIParams parses params.
RegAPIParams = `(?P<Anno>_In_|IN|OUT|_In_opt_|_Inout_opt_|_Out_|_Inout_|_Out_opt_|_Outptr_opt_|_Reserved_|_(O|o)ut[\w(),+ *]+|_In[\w()]+|_When[\w() =,!*]+) (?P<Type>[\w *]+) (?P<Name>[*a-zA-Z0-9]+)`

// RegParam extacts API parameters.
RegParam = `, `
)

// APIParam represents a paramter of a Win32 API.
type APIParam struct {
Annotation string `json:"anno"`
Type string `json:"type"`
Name string `json:"name"`
}

// API represents information about a Win32 API.
type API struct {
Attribute string `json:"-"` // Microsoft-specific attribute.
CallingConvention string `json:"callconv"` // Calling Convention.
Name string `json:"name"` // Name of the API.
ReturnValueType string `json:"retVal"` // Return value type.
Params []APIParam `json:"params"` // API Arguments.
CountParams uint8 `json:"-"` // Count of Params.
}

func parseAPIParameter(params string) APIParam {
m := regSubMatchToMapString(RegAPIParams, params)
apiParam := APIParam{
Annotation: m["Anno"],
Name: m["Name"],
Type: m["Type"],
}

// move the `*` to the type.
if strings.HasPrefix(apiParam.Name, "*") {
apiParam.Name = apiParam.Name[1:]
apiParam.Type += "*"
}

return apiParam
}

func parseAPI(apiPrototype string) API {
m := regSubMatchToMapString(RegProto, apiPrototype)
api := API{
Attribute: m["Attr"],
CallingConvention: m["CallConv"],
Name: m["ApiName"],
ReturnValueType: m["RetValType"],
}

// Treat the VOID case.
if m["Params"] == " VOID " {
api.CountParams = 0
return api
}

if api.Name == "" || api.CallingConvention == "" {
log.Printf("Failed to parse: %s", apiPrototype)
return api
}

re := regexp.MustCompile(RegParam)
split := re.Split(m["Params"], -1)
for i, v := range split {
// Quick hack:
ss := strings.Split(standardizeSpaces(v), " ")
if len(ss) == 2 {
// Force In for API without annotations.
v = "_In_ " + v
} else {
if i+1 < len(split) {
vv := standardizeSpaces(split[i+1])
if !strings.HasPrefix(vv, "In") &&
!strings.HasPrefix(vv, "Out") &&
!strings.HasPrefix(vv, "_In") &&
!strings.HasPrefix(vv, "IN") &&
!strings.HasPrefix(vv, "OUT") &&
!strings.HasPrefix(vv, "_Reserved") &&
!strings.HasPrefix(vv, "_When") &&
!strings.HasPrefix(vv, "__out") &&
!strings.HasPrefix(vv, "_Out") {
v += ", " + split[i+1]
split[i+1] = v
continue
}
}
}
api.Params = append(api.Params, parseAPIParameter("_"+v))
api.CountParams++
}
return api
}
Loading

0 comments on commit 8ebb2fa

Please sign in to comment.