首页
社区
课程
招聘
[原创]未导出api的查找与使用
2023-11-28 15:42 2636

[原创]未导出api的查找与使用

2023-11-28 15:42
2636

一、什么是未导出api?

一般来说在windows操作系统中并不是所有的api原型都会给到开发者调用的,如果想要使用这些未导出的api,就只能通过逆向或泄露的源代码去找到函数原型,并在程序中声明再进行调用。

二、函数调用查询

首先需要明白,在调用api时,是通过调用了哪些函数一层一层的往下调用,最后才能找到相同功能的函数进行使用。

使用OD跟踪api调用
1
2
3
4
5
6
7
8
//首先先写一个小例子
#include<winsock2.h>
void main() {
    STARTUPINFOEXA si;
    PROCESS_INFORMATION pi;
    ZeroMemory(&si, sizeof(STARTUPINFOEXA));
    CreateProcessA(NULL, (LPSTR)"notepad", NULL, NULL, TRUE, EXTENDED_STARTUPINFO_PRESENT, NULL, NULL, (LPSTARTUPINFOA)&si.StartupInfo, &pi);
}

使用od进行跟踪,找到与CreateProcess相关函数调用地址,跟进去
图片描述
第一个调用函数
图片描述
第二个调用
图片描述
所以,用户态下CreateProcess大致的调用流程为:CreateProcessA->CreateProcessInternalA->CreateProcessInternalW->NtCreateProcessEx

三、获取函数原型

已知函数调用流后,那么就可通过直接使用某个函数来绕过hook。
使用ida对函数对应的dll进行逆向,通过查找函数获取函数原型:
图片描述

1
2
3
4
5
6
7
8
9
10
NTSTATUS __stdcall NtCreateProcess(
    PHANDLE ProcessHandle,
    ACCESS_MASK DesiredAccess,
    POBJECT_ATTRIBUTES ObjectAttributes,
    HANDLE InheritFromProcessHandle,
    BOOLEAN InheritHandles,
    HANDLE SectionHandle,
    HANDLE DebugPort,
    HANDLE ExceptionPort
);

相关参数的意思:
ProcessHandle(输出参数): 一个指向HANDLE类型的指针,用于接收新创建进程的句柄。通过这个句柄,可以操作和控制新创建的进程。
DesiredAccess(输入参数): 指定了对新进程对象的访问权限。这是一个ACCESS_MASK类型的参数,定义了对新进程的操作权限,比如读取、写入、执行等。
ObjectAttributes(输入参数): 一个指向OBJECT_ATTRIBUTES结构的指针,该结构包含了一些与对象(在这里是进程对象)相关的属性。包括对象名称、安全描述符等信息。
InheritFromProcessHandle(输入参数): 一个句柄,用于指定新进程继承安全上下文的父进程句柄。新进程将继承父进程的访问权限和安全上下文。
InheritHandles(输入参数): 一个布尔值,指定新进程是否继承父进程的句柄。如果为TRUE,新进程将继承父进程的句柄;如果为FALSE,则不继承。
SectionHandle(输入参数): 一个指向文件映射对象的句柄。如果新进程是通过映射文件创建的,那么这个参数将指定用于创建新进程的文件映射对象的句柄。
DebugPort(输入参数): 一个句柄,用于指定新进程的调试端口。如果不需要调试,可以将此参数设置为NULL。
ExceptionPort(输入参数): 一个句柄,用于指定新进程的异常端口。异常端口用于处理新进程中的异常情况。如果不需要异常处理,可以将此参数设置为NULL。
函数的返回值是一个NTSTATUS类型,表示函数执行的状态。NTSTATUS是一个32位的错误代码,用于指示函数是否成功执行以及发生了什么错误。如果函数成功执行,返回值通常是STATUS_SUCCESS

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
//一段例子
#include <Windows.h>
#include <winternl.h>
#include<stdio.h>
 
typedef NTSTATUS(WINAPI* pfnNtCreateProcess)(
    PHANDLE ProcessHandle,
    ACCESS_MASK DesiredAccess,
    POBJECT_ATTRIBUTES ObjectAttributes,
    HANDLE InheritFromProcessHandle,
    BOOLEAN InheritHandles,
    HANDLE SectionHandle,
    HANDLE DebugPort,
    HANDLE ExceptionPort
);
 
int main() {
    HMODULE hNtdll = LoadLibrary(L"ntdll.dll");
    if (hNtdll == NULL) {
        return 1;
    }
    pfnNtCreateProcess NtCreateProcess = (pfnNtCreateProcess)GetProcAddress(hNtdll, "NtCreateProcess");
    if (NtCreateProcess == NULL) {
        FreeLibrary(hNtdll);
        return 1;
    }
    HANDLE processHandle;
    ACCESS_MASK desiredAccess = PROCESS_ALL_ACCESS;
    OBJECT_ATTRIBUTES objectAttributes = { sizeof(OBJECT_ATTRIBUTES), NULL, NULL, 0, NULL, NULL };
    HANDLE inheritFromProcessHandle = GetCurrentProcess();
    BOOLEAN inheritHandles = FALSE;
    HANDLE sectionHandle = NULL;
    HANDLE debugPort = NULL;
    HANDLE exceptionPort = NULL;
    NTSTATUS status = NtCreateProcess(
        &processHandle,
        desiredAccess,
        &objectAttributes,
        inheritFromProcessHandle,
        inheritHandles,
        sectionHandle,
        debugPort,
        exceptionPort
    );
 
    // 检查函数调用结果
    if (NT_SUCCESS(status)) {
        // 进程创建成功
        printf("进程创建成功,句柄: %p\n", processHandle);
 
        // 在这里可以使用 processHandle 进行其他操作,例如等待进程退出、关闭句柄等
 
        // 关闭进程句柄
        CloseHandle(processHandle);
    }
    else {
        // 进程创建失败
        printf("进程创建失败,错误码: %08X\n", status);
    }
 
    // 卸载 ntdll.dll 库
    FreeLibrary(hNtdll);
 
    return 0;
}

最后再对这段代码进行逆向分析
图片描述
图片描述
可以看到,这段代码并没有调用用户态的代码,直接创建的内核对象。


[培训]《安卓高级研修班(网课)》月薪三万计划,掌握调试、分析还原ollvm、vmp的方法,定制art虚拟机自动化脱壳的方法

收藏
点赞0
打赏
分享
最新回复 (8)
雪    币: 30092
活跃值: (2047)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
bestbird 2023-11-28 22:17
2
0
这个跟syscall有什么区别?
雪    币: 651
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
很强强 2023-11-29 08:31
3
0
这只是一个找未导出api的思路,syscall的使用也只不过是使用了用户态下最底层的api,当然往上也可以使用createprocessinternal
雪    币: 651
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
很强强 2023-11-29 08:33
4
0
bestbird 这个跟syscall有什么区别?
这只是一个找未导出api的思路,syscall的使用也只不过是使用了用户态下最底层的api,当然往上也可以使用createprocessinternal
雪    币: 19461
活跃值: (29125)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
秋狝 2023-11-29 10:07
5
1
感谢分享
雪    币: 2635
活跃值: (3257)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
Mr.hack 2023-11-29 18:17
6
0
特征码搜索YYDS
雪    币: 6124
活跃值: (4091)
能力值: ( LV6,RANK:80 )
在线值:
发帖
回帖
粉丝
黑洛 1 2023-12-1 00:44
7
0
https://github.com/MiroKaku/micore
雪    币: 1
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
巫师学徒 2023-12-1 11:05
8
0

可以看到,这段代码并没有调用用户态的代码,直接创建的内核对象。

__________________________________________________________________________________

小白不太理解,为什么最后这张图可以看出是直接创建内核对象了呢?


另外,为什么IDA里会有NtCreateProcess的函数原型呢?是因为IDA加载了windows的pdb符号表吗?那么这份符号表是哪里来的啊?既然是未导出函数,为啥会提供符号表呢,小白表示也不理解。


另外,未导出函数 == Windows未公开api 吗? 

最后于 2023-12-1 11:23 被巫师学徒编辑 ,原因: 没表达清楚
雪    币: 651
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
很强强 2023-12-1 13:05
9
0
巫师学徒 可以看到,这段代码并没有调用用户态的代码,直接创建的内核对象。_____________________________________________________________________ ...
第一个为什么能直接看出创建内核对象,这里我偷了个懒,od里追的太深了就没继续追,当然你可以使用OD一直往下跟,就会有发现。
第二个IDA为什么会有函数原型,这个你可以去反编译相关的DLL,然后去搜索函数你就找到了。
第三个未导出函数是否等于WINDOWS未公开API,是的,只要微软没有提供给开发者使用的API,都可以叫未导出API,当然我这篇文章只是其中一种找未导出API的方法,只能找相同功能的API,其他无法通过调用流找到的,还可以通过逆向WINDOWS操作系统上的功能获取。
游客
登录 | 注册 方可回帖
返回