我们在开发自己的Android项目的时候会经常做网络调试,我们可以自己制作一个抓包小工具来帮助我们测试网络数据,更能加深对网络数据传输过程的理解,以下是使用Android Studio编写的一个简单的Android网络抓包工具的实现过程。
首先,在Android Studio中创建一个新的Android项目。您可以按照常规的步骤进行创建,并在创建项目时选择适当的项目名称、包名称和应用程序的其他细节。确保您选择最新的Android SDK版本,并选择空Activity模板。
步骤1:添加依赖项
在项目的build.gradle文件中,我们需要添加以下依赖项:
dependencies {
implementation 'org.pcap4j:pcap4j-core:1.9.1'
implementation 'org.pcap4j:pcap4j-packetfactory-static:1.9.1'
}
这些依赖项将我们需要使用的Pcap4J库添加到我们的项目中。
步骤2:创建UI
为您的应用程序创建一个简单的布局文件,以显示捕获的网络数据包。您可以在res/layout/目录下创建一个新的布局文件。
在MainActivity中,我们将创建一个RecyclerView来显示捕获到的数据包。我们还将添加一个Clear按钮,用于清除数据包列表。以下是我们activity_main.xml示例布局文件的代码:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:scrollbars="vertical" />
<Button
android:id="@+id/btnClear"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:text="Clear" />
</LinearLayout>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:scrollbars="vertical" />
<Button
android:id="@+id/btnClear"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:text="Clear" />
</LinearLayout>
步骤3:创建数据包模型
为了在RecyclerView中显示数据包,我们需要创建一个数据包模型。我们将创建一个PacketModel类,其中包含数据包的各个属性,例如时间戳、源IP地址、目标IP地址、源端口、目标端口和数据大小。
public class PacketModel {
private String timestamp;
private String srcIp;
private String dstIp;
private int srcPort;
private int dstPort;
private int length;
public PacketModel(String timestamp, String srcIp, String dstIp, int srcPort, int dstPort, int length) {
this.timestamp = timestamp;
this.srcIp = srcIp;
this.dstIp = dstIp;
this.srcPort = srcPort;
this.dstPort = dstPort;
this.length = length;
}
public String getTimestamp() {
return timestamp;
}
public String getSrcIp() {
return srcIp;
}
public String getDstIp() {
return dstIp;
}
public int getSrcPort() {
return srcPort;
}
public int getDstPort() {
return dstPort;
}
public int getLength() {
return length;
}
}
步骤4:创建RecyclerView适配器
接下来,我们需要创建一个RecyclerView适配器来管理数据包列表。我们将创建一个PacketAdapter类,该类扩展RecyclerView.Adapter类,并重写必要的方法。
public class PacketAdapter extends RecyclerView.Adapter {
private List mPackets;
public PacketAdapter(List packets) {
mPackets = packets;
}
@NonNull
@Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.packet_item, parent, false);
return new ViewHolder(view);
}
@Override
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
PacketModel packet = mPackets.get(position);
holder.timestampTextView.setText(packet.getTimestamp());
holder.srcIpTextView.setText(packet.getSrcIp());
holder.dstIpTextView.setText(packet.getDstIp());
holder.srcPortTextView.setText(String.valueOf(packet.getSrcPort()));
holder.dstPortTextView.setText(String.valueOf(packet.getDstPort()));
holder.lengthTextView.setText(String.valueOf(packet.getLength()));
}
@Override
public int getItemCount() {
return mPackets.size();
}
public static class ViewHolder extends RecyclerView.ViewHolder {
public TextView timestampTextView;
public TextView srcIpTextView;
public TextView dstIpTextView;
public TextView srcPortTextView;
public TextView dstPortTextView;
public TextView lengthTextView;
public ViewHolder(@NonNull View itemView) {
super(itemView);
timestampTextView = itemView.findViewById(R.id.timestampTextView);
srcIpTextView = itemView.findViewById(R.id.srcIpTextView);
dstIpTextView = itemView.findViewById(R.id.dstIpTextView);
srcPortTextView = itemView.findViewById(R.id.srcPortTextView);
dstPortTextView = itemView.findViewById(R.id.dstPortTextView);
lengthTextView = itemView.findViewById(R.id.lengthTextView);
}
}
}
步骤5:创建抓包方法
现在,我们需要编写代码来捕获网络数据包。我们将在MainActivity中创建一个方法来执行此操作。以下是我们的MainActivity.java文件的代码:
public class MainActivity extends AppCompatActivity {
private static final String TAG = MainActivity.class.getSimpleName();
private static final int SNAPLEN = 65536;
private static final int READ_TIMEOUT = 10;
private static final int BUFFER_SIZE = 1024 * 1024;
private RecyclerView mRecyclerView;
private Button mBtnClear;
private List mPackets = new ArrayList<>();
private PacketAdapter mAdapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mRecyclerView = findViewById(R.id.recyclerView);
mRecyclerView.setLayoutManager(new LinearLayoutManager(this));
mAdapter = new PacketAdapter(mPackets);
mRecyclerView.setAdapter(mAdapter);
mBtnClear = findViewById(R.id.btnClear);
mBtnClear.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mPackets.clear();
mAdapter.notifyDataSetChanged();
}
});
startCapture();
}
private void startCapture() {
try {
// 获取网络接口
List allDevs = Pcaps.findAllDevs();
if (allDevs.isEmpty()) {
Log.e(TAG, "No network interface found.");
return;
}
// 选择网络接口
PcapNetworkInterface nif = allDevs.get(0);
// 打开网络接口
final PacketCapture packetCapture = nif.openLive(SNAPLEN, PromiscuousMode.PROMISCUOUS, READ_TIMEOUT);
// 开始捕获数据包
packetCapture.loop(-1, new PacketListener() {
@Override
public void gotPacket(Packet packet) {
// 解析数据包
byte[] data = packet.getPayload().getRawData();
if (data != null && data.length > 0) {
String timestamp = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS", Locale.getDefault()).format(new Date(packet.getCaptureHeader().timestampInMillis()));
String srcIp = packet.getHeader(IpV4Packet.class).getHeader().getSrcAddr().getHostAddress();
String dstIp = packet.getHeader(IpV4Packet.class).getHeader().getDstAddr().getHostAddress();
int srcPort = packet.getHeader(TcpPacket.class).getHeader().getSrcPort().valueAsInt();
int dstPort = packet.getHeader(TcpPacket.class).getHeader().getDstPort().valueAsInt();
int length = data.length;
PacketModel packetModel = new PacketModel(timestamp, srcIp, dstIp, srcPort, dstPort, length);
mPackets.add(packetModel);
// 更新UI
runOnUiThread(new Runnable() {
@Override
public void run() {
mAdapter.notifyItemInserted(mPackets.size() - 1);
mRecyclerView.smoothScrollToPosition(mPackets.size() - 1);
}
});
}
}
});
} catch (PcapNativeException e) {
Log.e(TAG, "Error while opening device: " + e.getMessage());
} catch (InterruptedException e) {
Log.e(TAG, "Error while capturing packets: " + e.getMessage());
}
}
}
我们首先获取网络接口,然后选择第一个接口。接着,我们打开接口并使用PacketListener捕获数据包。对于每个捕获的数据包,我们解析它并将其添加到mPackets列表中。最后,我们在UI线程中通知适配器,以便更新UI列表并滚动到最后一个条目。
在onCreate()方法中,我们设置RecyclerView和适配器,并为清除按钮添加一个点击监听器。然后,我们调用startCapture()方法开始捕获数据包。
最后,我们需要在清单文件中声明Internet权限,以便我们可以访问网络:
<uses-permission android:name="android.permission.INTERNET" />
现在,我们可以编译并运行应用程序了。在模拟器或实际设备上运行应用程序后,您应该可以看到列表开始填充数据包。如果您打开设备的浏览器并浏览一些网页,您应该能够看到抓取的HTTP请求和响应。
这是一个简单的抓包应用程序示例,您可以在此基础上添加更多功能和改进,例如过滤器,更高级的解析,导出功能等等。
完整代码如下所示:
public class MainActivity extends AppCompatActivity {
private RecyclerView recyclerView;
private PacketAdapter adapter;
private List packetList = new ArrayList<>();
private Button clearButton;
private PacketCapture packetCapture;
private static final int SNAP_LEN = 65536;
private static final int READ_TIMEOUT = 1000;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
recyclerView = findViewById(R.id.recyclerView);
adapter = new PacketAdapter(packetList);
recyclerView.setLayoutManager(new LinearLayoutManager(this));
recyclerView.setAdapter(adapter);
clearButton = findViewById(R.id.clearButton);
clearButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
packetList.clear();
adapter.notifyDataSetChanged();
}
});
startCapture();
}
private void startCapture() {
try {
NetworkInterface nif = PcapNetworkInterface.getSelectedInterface();
packetCapture = nif.openLive(SNAP_LEN, PromiscuousMode.PROMISCUOUS, READ_TIMEOUT);
new Thread(new Runnable() {
@Override
public void run() {
while (!Thread.currentThread().isInterrupted()) {
Packet packet = packetCapture.getNextPacket();
if (packet != null) {
packetList.add(packet);
runOnUiThread(new Runnable() {
@Override
public void run() {
adapter.notifyItemInserted(packetList.size() - 1);
recyclerView.scrollToPosition(packetList.size() - 1);
}
});
}
}
}
}).start();
} catch (PcapNativeException e) {
e.printStackTrace();
} catch (NotOpenException e) {
e.printStackTrace();
}
}
@Override
protected void onDestroy() {
super.onDestroy();
if (packetCapture != null) {
packetCapture.breakLoop();
packetCapture.close();
}
}
}
这是Packet类:
public class Packet {
private String time;
private String sourceIp;
private String destinationIp;
private String protocol;
private String length;
public Packet(String time, String sourceIp, String destinationIp, String protocol, String length) {
this.time = time;
this.sourceIp = sourceIp;
this.destinationIp = destinationIp;
this.protocol = protocol;
this.length = length;
}
public String getTime() {
return time;
}
public String getSourceIp() {
return sourceIp;
}
public String getDestinationIp() {
return destinationIp;
}
public String getProtocol() {
return protocol;
}
public String getLength() {
return length;
}
}
这是PacketAdapter类:
public class PacketAdapter extends RecyclerView.Adapter {
private List packetList;
public PacketAdapter(List packetList) {
this.packetList = packetList;
}
@NonNull
@Override
public PacketViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.packet_item, parent, false);
return new PacketViewHolder(view);
}
@Override
public void onBindViewHolder(@NonNull PacketViewHolder holder, int position) {
Packet packet = packetList.get(position);
holder.timeTextView.setText(packet.getTime());
holder.sourceIpTextView.setText(packet.getSourceIp());
holder.destinationIpTextView.setText(packet.getDestinationIp());
holder.protocolTextView.setText(packet.getProtocol());
holder.lengthTextView.setText(packet.getLength());
}
@Override
public int getItemCount() {
return packetList.size();
}
static class PacketViewHolder extends RecyclerView.ViewHolder {
private TextView timeTextView;
private TextView sourceIpTextView;
private TextView destinationIpTextView;
private TextView protocolTextView;
private TextView lengthTextView;
public PacketViewHolder(@NonNull View itemView) {
super(itemView);
timeTextView = itemView.findViewById(R.id.timeTextView);
sourceIpTextView = itemView.findViewById(R.id.sourceIpTextView);
destinationIpTextView = itemView.findViewById(R.id.destinationIpTextView);
protocolTextView = itemView.findViewById(R.id.protocolTextView);
lengthTextView = itemView.findViewById(R.id.lengthTextView);
}
}
}
最后,还需要在AndroidManifest.xml文件中添加网络权限:
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
这样,我们就完成了一个简单的网络抓包应用程序。当我们运行程序时,它将开始捕获在手机网络接口上传输的所有数据包,并将它们显示在一个RecyclerView列表中。我们还添加了一个清除按钮,以便在需要时清空列表。
请注意,这只是一个简单的示例应用程序,仅用于教学目的。实际上,您可能需要对网络数据包进行更详细的分析,并根据您的具体需求进行定制。此外,在使用网络抓包应用程序时,请务必遵守所有相关法律和规定,以免触犯隐私权和网络安全等问题。
希望这篇文章对您有所帮助!
Social Plugin